Files
Learn_System/frontend/textbooks/geometry_8_ch3.html
T
Maxim Dolgolyov 660e7e2747 feat(gamification): Phase 1 — full kill-switch + textbook XP wrapping
Until now the 'gamification' feature flag did nothing: it had no row in
app_settings, the admin couldn't toggle it, awardXP/awardCoins ignored
it, and the CSS only hid three dashboard widgets — XP bars in textbooks
stayed visible regardless.

Phase 1 closes every hole.

Backend (source of truth):
  • migration 029 seeds feature_gamification_enabled=1
  • new isGamificationEnabled() helper in gamification/_shared.js with a
    30s cache + invalidateGamificationCache() for instant admin toggles
  • awardXP / awardCoins / updateStreak / unlockAchievement /
    checkAchievements all bail out when the flag is off
  • /api/gamification/* and /api/shop/* (user routes) return 404 when
    disabled; admin routes remain open so the switch itself is reachable
  • adminController.updateFeatures gains 'gamification' in the allow-list
    and invalidates the cache on flip

Frontend:
  • LS.isGamificationEnabled() (synchronous, populated by loadFeatures)
    so xp.js + applyCosmetics can bail without a round-trip
  • xp.js load/add/flush become no-ops when the flag is off
  • applyCosmetics skips the round-trip when off
  • CSS .no-gamification rule expanded to cover .hero-xp-badge, .po-xp,
    .xp-card, .xp-bar, #frames-section, and a universal [data-gamified]
    hook for future blocks

Textbooks (Variant 2 of the plan):
  • backend/scripts/wrap_textbook_xp.py — idempotent script that adds
    data-gamified to 167 XP tags across 63 textbook files (chapters +
    hubs, all subjects/grades). Single CSS rule now hides everything.

Verified end-to-end: with the flag off, awardXP/awardCoins write nothing;
flipping back restores normal behavior.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 19:43:24 +03:00

4739 lines
393 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
<meta http-equiv="Pragma" content="no-cache">
<meta http-equiv="Expires" content="0">
<title>Геометрия 8 · Глава 3 · Подобные треугольники</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"
onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false})"></script>
<script src="/js/api.js" defer></script>
<script src="/js/xp.js" defer></script>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Manrope:wght@600;700;800;900&family=Unbounded:wght@700;800;900&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<style>
:root{
--bg:#fafafa; --card:#fff; --card-soft:#faf5ff; --text:#0f172a; --ink:#0f172a; --muted:#64748b;
--border:#e2e8f0; --sh:0 1px 3px rgba(0,0,0,.06); --sh2:0 4px 14px rgba(0,0,0,.08);
--pri:#7c3aed; --pri2:#6d28d9; --pri-soft:#ede9fe;
--acc:#8b5cf6; --acc2:#7c3aed; --acc-soft:#ddd6fe;
--ok:#10b981; --ok-bg:#d1fae5; --warn:#f59e0b; --warn-bg:#fef3c7;
--bad:#ef4444; --fail:#dc2626; --fail-bg:#fee2e2;
}
.dark{--bg:#08030e; --card:#10071a; --card-soft:#140820; --text:#f3e8ff; --ink:#f3e8ff; --muted:#9f7ec0; --border:#2a1040}
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.55;font-size:15px}
button,input,select,textarea{font-family:inherit;font-size:inherit}
button{cursor:pointer;border:0;background:transparent;color:inherit}
a{color:inherit;text-decoration:none}
.ic{width:16px;height:16px;display:inline-block;flex-shrink:0;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;vertical-align:middle}
.hdr{position:relative;background:linear-gradient(110deg,#3b0764 0%,#7c3aed 55%,#a78bfa 100%);color:#fff;padding:46px 22px 30px;overflow:hidden;border-bottom:2px solid rgba(167,139,250,.2);min-height:130px}
.hdr::before{content:'ГЛАВА 3';position:absolute;right:-12px;top:50%;transform:translateY(-50%);font-family:'Unbounded',sans-serif;font-size:clamp(5rem,15vw,11rem);font-weight:900;letter-spacing:-.04em;color:transparent;-webkit-text-stroke:1.5px rgba(220,200,255,.12);line-height:1;pointer-events:none;user-select:none;z-index:0}
.hdr-row{position:relative;z-index:1;display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.hdr h1{font-family:'Unbounded',sans-serif;font-size:1.5rem;font-weight:900;letter-spacing:-.01em;line-height:1.3;padding-top:4px}
.hdr-sub{font-size:.85rem;opacity:.88;margin-top:6px;font-weight:500;line-height:1.4}
.hdr-side{margin-left:auto;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.hdr-btn{padding:7px 12px;border-radius:9px;background:rgba(255,255,255,.14);color:#fff;font-weight:600;font-size:.82rem;display:inline-flex;align-items:center;gap:6px;transition:background .15s;text-decoration:none}
.hdr-btn:hover{background:rgba(255,255,255,.24)}
.main{max-width:1240px;margin:0 auto;padding:22px;width:100%;display:grid;grid-template-columns:1fr 280px;gap:24px}
@media(max-width:980px){.main{grid-template-columns:1fr;padding:14px}}
.col-main{min-width:0}
.hero{background:linear-gradient(135deg,var(--pri-soft) 0%,var(--acc-soft) 50%,var(--pri-soft) 100%);background-size:200% 200%;animation:heroShift 12s ease-in-out infinite;border:1px solid var(--border);border-radius:18px;padding:24px 22px;margin-bottom:24px;position:relative;overflow:hidden}
@keyframes heroShift{0%,100%{background-position:0% 50%}50%{background-position:100% 50%}}
.hero h2{font-family:'Unbounded',sans-serif;font-size:1.55rem;font-weight:800;color:var(--pri2);margin-bottom:10px;letter-spacing:-.01em}
.hero p{font-size:.95rem;color:var(--text);opacity:.88;margin-bottom:14px;max-width:640px}
.hero-row{display:flex;gap:14px;flex-wrap:wrap;align-items:center}
.btn-primary{padding:11px 22px;background:linear-gradient(135deg,var(--pri),var(--pri2));color:#fff;border-radius:11px;font-weight:700;font-size:.92rem;display:inline-flex;align-items:center;gap:8px;box-shadow:var(--sh2);transition:transform .15s,box-shadow .15s}
.btn-primary:hover{transform:translateY(-1px);box-shadow:0 8px 28px rgba(124,58,237,.32)}
.hero-progress{flex:1;min-width:200px;max-width:280px}
.hp-label{font-size:.74rem;font-weight:700;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;display:block;margin-bottom:5px}
.hp-bar{height:8px;background:rgba(124,58,237,.18);border-radius:5px;overflow:hidden}
.hp-fill{height:100%;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:5px;width:0%;transition:width .6s cubic-bezier(.16,1,.3,1)}
.hp-text{font-size:.78rem;color:var(--muted);font-weight:700;margin-top:4px;display:block}
.hero-xp-badge{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:linear-gradient(135deg,var(--warn,#f59e0b),var(--pri));color:#fff;border-radius:99px;font-size:.82rem;font-weight:800;letter-spacing:.02em;box-shadow:0 4px 12px rgba(124,58,237,.22);font-family:'Unbounded',sans-serif}
.psel{margin-bottom:24px}
.psel-title{font-size:.72rem;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.08em;margin-bottom:10px}
.psel-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(160px,1fr));gap:10px}
.psel-card{background:var(--card);border:1.5px solid var(--border);border-radius:13px;padding:14px;cursor:pointer;transition:transform .2s,box-shadow .2s,border-color .2s;text-align:left;position:relative}
.psel-card:hover{transform:translateY(-3px);box-shadow:var(--sh2);border-color:var(--pri)}
.psel-card.active{border-color:var(--pri);background:linear-gradient(135deg,var(--pri-soft),var(--card));box-shadow:var(--sh2)}
.psel-card.active::after{content:'';position:absolute;top:0;left:0;right:0;height:3px;background:linear-gradient(90deg,var(--pri),var(--acc));border-radius:13px 13px 0 0}
.psel-num{font-family:'Unbounded',sans-serif;font-size:.72rem;font-weight:800;color:var(--pri);text-transform:uppercase;letter-spacing:.08em;margin-bottom:5px}
.psel-name{font-size:.88rem;font-weight:700;color:var(--text);line-height:1.3;margin-bottom:8px}
.psel-prog{height:4px;background:rgba(124,58,237,.10);border-radius:3px;overflow:hidden}
.psel-prog-fill{height:100%;background:var(--pri);width:0%;transition:width .4s}
.psel-card.final{background:linear-gradient(135deg,#f5f3ff,#ede9fe)}
.psel-card.final .psel-num{color:var(--warn)}
/* SECTION COLORS — purple/violet/indigo spectrum */
.sec[id="sec-p1"] { --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-p2"] { --sec-acc:#8b5cf6; --sec-acc-d:#7c3aed; --sec-acc-soft:#ddd6fe; }
.sec[id="sec-p3"] { --sec-acc:#a78bfa; --sec-acc-d:#8b5cf6; --sec-acc-soft:#f3f0ff; }
.sec[id="sec-p4"] { --sec-acc:#6366f1; --sec-acc-d:#4f46e5; --sec-acc-soft:#e0e7ff; }
.sec[id="sec-p5"] { --sec-acc:#4f46e5; --sec-acc-d:#4338ca; --sec-acc-soft:#e0e7ff; }
.sec[id="sec-p6"] { --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-p7"] { --sec-acc:#8b5cf6; --sec-acc-d:#7c3aed; --sec-acc-soft:#ddd6fe; }
.sec[id="sec-p8"] { --sec-acc:#c026d3; --sec-acc-d:#a21caf; --sec-acc-soft:#fae8ff; }
.sec[id="sec-p9"] { --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-final3"] { --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec{display:none;position:relative;animation:fadeIn .35s ease}
.sec.active{display:block}
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.sec::before{content:attr(data-watermark);position:absolute;right:-20px;top:10%;font-family:'Unbounded',sans-serif;font-size:clamp(6rem,18vw,14rem);font-weight:900;color:transparent;-webkit-text-stroke:1.5px var(--sec-acc-soft,var(--pri-soft));line-height:1;pointer-events:none;user-select:none;z-index:0;opacity:.35}
.sec-header{margin-bottom:22px;padding-bottom:14px;border-bottom:2px solid var(--sec-acc-soft,var(--pri-soft));position:relative;z-index:1}
.sec-num{display:inline-block;padding:4px 10px;background:linear-gradient(135deg,var(--sec-acc,var(--pri)),var(--sec-acc-d,var(--pri2)));color:#fff;border-radius:7px;font-family:'Unbounded',sans-serif;font-size:.78rem;font-weight:800;letter-spacing:.04em;margin-bottom:8px}
.sec-h{font-family:'Unbounded',sans-serif;font-size:1.7rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));letter-spacing:-.01em;line-height:1.25}
.card{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:18px 20px;margin-bottom:16px;box-shadow:0 1px 3px rgba(0,0,0,.04),0 8px 24px rgba(124,58,237,.06);position:relative;z-index:1;transition:transform .25s cubic-bezier(.16,1,.3,1),box-shadow .25s}
.card:hover{transform:translateY(-2px);box-shadow:0 4px 10px rgba(0,0,0,.06),0 16px 36px rgba(124,58,237,.12)}
.card-header{display:flex;align-items:center;gap:10px;margin-bottom:12px;padding-bottom:10px;border-bottom:1px dashed var(--border)}
.card-icon{width:32px;height:32px;border-radius:9px;display:flex;align-items:center;justify-content:center;flex-shrink:0;color:#fff}
.card-icon.repeat{background:#0ea5e9}.card-icon.theory{background:#8b5cf6}.card-icon.algo{background:#f59e0b}.card-icon.rule{background:#ec4899}.card-icon.example{background:#10b981}.card-icon.oral{background:#06b6d4}.card-icon.class{background:#3b82f6}.card-icon.home{background:#f97316}
.card-icon .ic{width:18px;height:18px}
.card-title{font-family:'Unbounded',sans-serif;font-size:.82rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);flex:1}
.card-num{font-size:.74rem;font-weight:700;color:var(--muted);background:var(--sec-acc-soft,var(--pri-soft));padding:3px 7px;border-radius:5px}
.card-body{font-size:.94rem;line-height:1.65}
.card-body p{margin-bottom:8px}
.wg{background:linear-gradient(135deg,var(--card),var(--sec-acc-soft,var(--pri-soft)));border:1.5px solid var(--sec-acc,var(--pri));border-radius:14px;padding:18px 20px;margin-bottom:18px;box-shadow:var(--sh2);position:relative;z-index:1}
.wg-header{display:flex;align-items:center;gap:8px;margin-bottom:14px}
.wg-badge{padding:4px 9px;background:var(--sec-acc,var(--pri));color:#fff;border-radius:6px;font-family:'Unbounded',sans-serif;font-size:.68rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em}
.wg-title{font-family:'Unbounded',sans-serif;font-size:1.05rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));flex:1}
.wg-help{font-size:.88rem;color:var(--text);margin-bottom:12px;line-height:1.55;background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--sec-acc-soft,var(--pri-soft)));border-left:4px solid var(--warn,#f59e0b);padding:9px 14px;border-radius:9px}
.btn{padding:8px 16px;border-radius:8px;background:var(--card);color:var(--text);border:1.5px solid var(--border);font-weight:600;font-size:.88rem;transition:background .15s,border-color .15s,transform .1s}
.btn:hover{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
.btn:active{transform:scale(.96)}
.btn.primary{background:var(--sec-acc,var(--pri));color:#fff;border-color:var(--sec-acc,var(--pri))}
.btn.primary:hover{background:var(--sec-acc-d,var(--pri2));border-color:var(--sec-acc-d,var(--pri2))}
.btn.small{padding:5px 11px;font-size:.78rem}
.tinp{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);transition:border-color .15s}
.tinp:focus{outline:0;border-color:var(--sec-acc,var(--pri));box-shadow:0 0 0 3px var(--sec-acc-soft,var(--pri-soft))}
.feedback{padding:10px 14px;border-radius:9px;font-weight:600;font-size:.88rem;margin-top:8px;display:none}
.feedback.ok{display:block;background:var(--ok-bg);color:#065f46;border-left:4px solid var(--ok)}
.feedback.fail{display:block;background:var(--fail-bg);color:#7f1d1d;border-left:4px solid var(--fail)}
.col-side{position:sticky;top:14px;align-self:start;height:fit-content;max-height:calc(100vh - 28px);overflow-y:auto}
.sidecard{background:var(--card);border:1px solid var(--border);border-radius:14px;padding:16px;margin-bottom:14px;box-shadow:var(--sh)}
.sidecard h4{font-family:'Unbounded',sans-serif;font-size:.74rem;font-weight:800;color:var(--pri2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:10px;padding-bottom:8px;border-bottom:1px solid var(--border)}
.sidecard-row{margin-bottom:8px;font-size:.86rem;line-height:1.6}
.sidecard-row b{color:var(--pri);font-weight:700}
.sidecard-row:last-child{margin-bottom:0}
@media(max-width:980px){.col-side{position:static;max-height:none}}
.xp-card{background:linear-gradient(135deg,var(--acc-soft),var(--pri-soft));border:1.5px solid var(--acc);border-radius:12px;padding:14px;margin-bottom:14px}
.xp-card-title{font-size:.68rem;font-weight:800;color:var(--acc2);text-transform:uppercase;letter-spacing:.07em;margin-bottom:8px;display:flex;align-items:center;justify-content:space-between}
.xp-level{font-size:1.1rem;font-weight:900;color:var(--acc2);font-family:'Unbounded',sans-serif}
.xp-bar{height:9px;background:rgba(139,92,246,.15);border-radius:6px;overflow:hidden;margin:7px 0}
.xp-fill{height:100%;background:linear-gradient(90deg,var(--acc),var(--pri));border-radius:6px;transition:width .5s cubic-bezier(.4,0,.2,1)}
.xp-nums{font-size:.74rem;color:var(--muted);display:flex;justify-content:space-between}
.spoiler{border:1px solid var(--border);border-radius:10px;background:var(--card);margin:10px 0;overflow:hidden}
.spoiler summary{padding:8px 14px;background:var(--sec-acc-soft,var(--pri-soft));font-weight:700;cursor:pointer;font-size:.88rem;color:var(--sec-acc-d,var(--pri2));list-style:none;display:flex;align-items:center;gap:8px}
.spoiler summary::-webkit-details-marker{display:none}
.spoiler summary::before{content:'+';font-size:1.2rem;font-weight:900;color:var(--sec-acc,var(--pri));width:18px}
.spoiler[open] summary::before{content:'\2212'}
.spoiler-body{padding:10px 14px;font-size:.92rem;line-height:1.6}
.sec-nav{display:flex;gap:10px;margin-top:24px;padding-top:20px;border-top:1px solid var(--border);justify-content:space-between;flex-wrap:wrap}
.foot{text-align:center;padding:30px 16px;color:var(--muted);font-size:.78rem;border-top:1px solid var(--border);margin-top:30px}
.tbl{width:100%;border-collapse:collapse;margin:12px 0;font-size:.88rem}
.tbl th,.tbl td{padding:7px 10px;border:1px solid var(--border);text-align:center}
.tbl th{background:var(--sec-acc-soft,var(--pri-soft));color:var(--sec-acc-d,var(--pri2));font-weight:700}
.ach-popup{position:fixed;top:80px;right:18px;background:linear-gradient(135deg,#7c3aed,#8b5cf6);color:#fff;padding:12px 18px;border-radius:11px;font-weight:700;font-size:.9rem;box-shadow:0 8px 28px rgba(124,58,237,.45);z-index:1002;display:none;align-items:center;gap:8px;animation:achIn .45s cubic-bezier(.34,1.56,.64,1) forwards;max-width:340px}
.ach-popup.show{display:flex}
@keyframes achIn{from{opacity:0;transform:translateX(40px)}to{opacity:1;transform:none}}
.dnd-pool{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px;padding:10px;border:1.5px dashed var(--border);border-radius:10px;min-height:54px;transition:border-color .18s,background .18s}
.dnd-pool.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid}
.dnd-pool.col{flex-direction:column;align-items:stretch}
.dnd-chip{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:var(--card);border:1.5px solid var(--border);border-radius:10px;cursor:grab;user-select:none;font-size:.92rem;line-height:1.4;transition:transform .12s,box-shadow .12s,border-color .12s;touch-action:none;max-width:100%}
.dnd-chip:hover{transform:translateY(-1px);border-color:var(--sec-acc,var(--pri));box-shadow:var(--sh)}
.dnd-chip.armed{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));box-shadow:0 0 0 3px rgba(124,58,237,.22);transform:translateY(-1px)}
.dnd-chip.dragging{opacity:.28}
.dnd-chip.placed{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
.dnd-chip .dnd-x{padding:0 5px;color:var(--muted);font-weight:700;font-size:1.05rem;border-radius:4px;cursor:pointer}
.dnd-chip .dnd-x:hover{color:var(--bad,var(--fail));background:var(--fail-bg)}
.drop-box{background:var(--card);border:1.5px dashed var(--border);border-radius:10px;padding:10px;min-height:90px;transition:border-color .15s,background .15s}
.drop-box:hover{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft))}
.drop-box h5{font-family:'Unbounded',sans-serif;font-size:.78rem;color:var(--sec-acc-d,var(--pri2));margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em}
.drop-box.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid;transform:scale(1.015)}
.drop-items{display:flex;flex-wrap:wrap;gap:6px;min-height:32px}
.dnd-hint{font-size:.78rem;color:var(--muted);margin-bottom:8px;display:flex;align-items:center;gap:6px}
.dnd-hint svg{width:14px;height:14px;flex-shrink:0}
.actions{display:flex;gap:8px;flex-wrap:wrap;margin-top:10px}
.sliders{display:grid;grid-template-columns:repeat(auto-fit,minmax(180px,1fr));gap:10px;margin-bottom:10px}
.sliders label{display:block;font-size:.92rem;color:var(--muted);background:var(--card);padding:8px 12px;border-radius:8px;border:1px solid var(--border);line-height:1.5}
.sliders label b{font-family:'JetBrains Mono',monospace;font-size:1.05rem;color:var(--sec-acc-d,var(--pri2));margin-left:4px}
.sliders label input[type="range"]{display:block;width:100%;margin-top:6px;accent-color:var(--sec-acc,var(--pri))}
.score-display{display:flex;gap:14px;flex-wrap:wrap;align-items:center;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;margin-bottom:12px}
.score-display b{color:var(--sec-acc-d,var(--pri2));font-size:1.15rem}
.col-side-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.42);z-index:9990;display:none;animation:fadeIn .18s ease}
.col-side-backdrop.show{display:block}
@media(max-width:980px){
.col-side{position:fixed;top:0;right:0;height:100vh;width:300px;max-width:88vw;background:var(--bg);box-shadow:-12px 0 24px rgba(0,0,0,.18);padding:18px 16px;overflow-y:auto;transform:translateX(100%);transition:transform .25s ease;z-index:9991;max-height:none}
.col-side.open{transform:none}
}
.gloss-term{border-bottom:1.5px dotted var(--sec-acc,var(--pri));cursor:help;color:var(--sec-acc-d,var(--pri2));font-weight:600;padding:0 1px}
.gloss-term:hover{background:var(--sec-acc-soft,var(--pri-soft));border-radius:3px}
.gloss-tip{position:fixed;max-width:320px;padding:11px 14px;background:var(--card);border:1.5px solid var(--sec-acc,var(--pri));border-radius:11px;font-size:.84rem;line-height:1.55;box-shadow:0 12px 32px rgba(0,0,0,.18);z-index:9994;display:none;pointer-events:none;color:var(--text)}
.gloss-tip.show{display:block;animation:tipIn .15s ease}
.gloss-tip b{color:var(--sec-acc-d,var(--pri2));font-size:.92rem}
@keyframes tipIn{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
.search-modal{position:fixed;inset:0;background:rgba(15,23,42,.55);backdrop-filter:blur(4px);z-index:9993;display:none;align-items:flex-start;justify-content:center;padding-top:14vh}
.search-modal.show{display:flex;animation:fadeIn .15s ease}
.search-box{background:var(--bg);border:1px solid var(--border);border-radius:14px;width:560px;max-width:92vw;max-height:70vh;display:flex;flex-direction:column;overflow:hidden;box-shadow:0 24px 64px rgba(0,0,0,.4)}
.search-input{padding:14px 16px;font-size:1rem;border:0;border-bottom:1px solid var(--border);background:transparent;color:var(--text);outline:none}
.search-results{flex:1;overflow-y:auto;padding:6px 0}
.search-row{display:block;padding:8px 16px;cursor:pointer;border-bottom:1px solid var(--border);text-align:left;background:transparent;border-left:0;border-right:0;border-top:0;width:100%;color:var(--text)}
.search-row:hover,.search-row.active{background:var(--sec-acc-soft,var(--pri-soft))}
.search-row .sr-kind{font-size:.7rem;font-weight:800;color:var(--muted);text-transform:uppercase;letter-spacing:.06em;margin-bottom:2px}
.search-row .sr-title{font-weight:700;font-size:.92rem;color:var(--text)}
.search-row .sr-desc{font-size:.8rem;color:var(--muted);margin-top:2px}
.search-empty{padding:20px;text-align:center;color:var(--muted);font-size:.88rem}
.search-foot{padding:8px 14px;border-top:1px solid var(--border);font-size:.74rem;color:var(--muted);display:flex;gap:14px;background:var(--card-soft,transparent)}
.search-foot kbd{padding:2px 6px;background:var(--card);border:1px solid var(--border);border-radius:4px;font-family:'JetBrains Mono',monospace;font-size:.72rem}
</style>
</head>
<body>
<header class="hdr">
<div class="hdr-row">
<div>
<h1>Геометрия 8 · Глава 3</h1>
<div class="hdr-sub">Подобные треугольники</div>
</div>
<div class="hdr-side">
<a href="/textbook/geometry-8" class="hdr-btn" title="К Геометрии 8 — все главы">
<svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg>
К геометрии 8
</a>
<button id="search-btn" class="hdr-btn" title="Поиск (Ctrl+K)">
<svg class="ic" viewBox="0 0 24 24"><circle cx="11" cy="11" r="7"/><path d="m21 21-4-4"/></svg>
Поиск
</button>
<button id="sidebar-btn" class="hdr-btn" title="Шпаргалка">
<svg class="ic" viewBox="0 0 24 24"><line x1="4" y1="6" x2="20" y2="6"/><line x1="4" y1="12" x2="20" y2="12"/><line x1="4" y1="18" x2="14" y2="18"/></svg>
Шпаргалка
</button>
<button id="theme-btn" class="hdr-btn" title="Сменить тему">
<svg class="ic" viewBox="0 0 24 24"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg>
<span id="theme-lab">Тёмная</span>
</button>
</div>
</div>
</header>
<main class="main">
<div class="col-main">
<section class="hero">
<h2>Подобие: пропорции и три признака</h2>
<p>Подобные фигуры имеют одинаковую форму, но разный размер. Ключевые инструменты: <b>теорема Фалеса</b> (обобщённая), три признака подобия треугольников, свойство <b>биссектрисы</b>. Итог — отношение площадей подобных треугольников равно квадрату коэффициента подобия.</p>
<div class="hero-row">
<button class="btn-primary" onclick="goTo('p1')">
<svg class="ic" viewBox="0 0 24 24"><polygon points="6 4 20 12 6 20 6 4" fill="currentColor" stroke="none"/></svg>
Начать § 1
</button>
<div class="hero-progress">
<span class="hp-label">Прогресс по главе</span>
<div class="hp-bar"><div id="hero-hp-fill" class="hp-fill"></div></div>
<span id="hero-hp-text" class="hp-text">0%</span>
</div>
<div id="hero-xp-badge" class="hero-xp-badge" title="Опыт" data-gamified></div>
</div>
</section>
<section class="psel">
<div class="psel-title">Параграфы главы</div>
<div id="psel-grid" class="psel-grid"></div>
</section>
<section id="sec-p1" class="sec" data-watermark="&#8741;"><div class="sec-header"><span class="sec-num">§ 1</span><h2 class="sec-h">Теорема Фалеса (обобщённая)</h2></div><div id="p1-body"></div></section>
<section id="sec-p2" class="sec" data-watermark="m:n"><div class="sec-header"><span class="sec-num">§ 2</span><h2 class="sec-h">Деление отрезка в отношении m : n</h2></div><div id="p2-body"></div></section>
<section id="sec-p3" class="sec" data-watermark="&#8764;"><div class="sec-header"><span class="sec-num">§ 3</span><h2 class="sec-h">Определение подобных треугольников</h2></div><div id="p3-body"></div></section>
<section id="sec-p4" class="sec" data-watermark="&#8741;k"><div class="sec-header"><span class="sec-num">§ 4</span><h2 class="sec-h">Свойство прямой, параллельной стороне треугольника</h2></div><div id="p4-body"></div></section>
<section id="sec-p5" class="sec" data-watermark="&#8736;&#8736;"><div class="sec-header"><span class="sec-num">§ 5</span><h2 class="sec-h">Первый признак подобия. Следствие</h2></div><div id="p5-body"></div></section>
<section id="sec-p6" class="sec" data-watermark="&#8710;"><div class="sec-header"><span class="sec-num">§ 6</span><h2 class="sec-h">Второй признак подобия</h2></div><div id="p6-body"></div></section>
<section id="sec-p7" class="sec" data-watermark="&#8710;&#8764;"><div class="sec-header"><span class="sec-num">§ 7</span><h2 class="sec-h">Третий признак подобия</h2></div><div id="p7-body"></div></section>
<section id="sec-p8" class="sec" data-watermark="&#8901;"><div class="sec-header"><span class="sec-num">§ 8</span><h2 class="sec-h">Свойство биссектрисы треугольника</h2></div><div id="p8-body"></div></section>
<section id="sec-p9" class="sec" data-watermark="k&#178;"><div class="sec-header"><span class="sec-num">§ 9</span><h2 class="sec-h">Отношение площадей подобных треугольников</h2></div><div id="p9-body"></div></section>
<section id="sec-final3" class="sec" data-watermark="&#9733;"><div class="sec-header"><span class="sec-num" style="background:linear-gradient(135deg,#7c3aed,#8b5cf6)">Финал главы</span><h2 class="sec-h">Итоги. Боссы главы 3</h2></div><div id="final3-body"></div></section>
</div>
<aside class="col-side" id="col-side"><div id="sidebar-content"></div></aside>
<div class="col-side-backdrop" id="col-side-backdrop"></div>
</main>
<footer class="foot">Интерактивный учебник «Геометрия 8» · Глава 3 · Подобные треугольники · LearnSpace</footer>
<div id="ach-popup" class="ach-popup"><svg class="ic" viewBox="0 0 24 24" style="width:22px;height:22px"><polygon points="12,2 22,20 2,20"/></svg><span id="ach-text">Достижение!</span></div>
<div id="gloss-tip" class="gloss-tip"></div>
<div id="search-modal" class="search-modal" role="dialog" aria-label="Поиск по главе">
<div class="search-box">
<input type="text" id="search-input" class="search-input" placeholder="Поиск: признак подобия, теорема…" autocomplete="off">
<div id="search-results" class="search-results"></div>
<div class="search-foot"><span><kbd>&#8593;&#8595;</kbd> навигация</span><span><kbd>Enter</kbd> открыть</span><span><kbd>Esc</kbd> закрыть</span></div>
</div>
</div>
<script>
'use strict';
const STATE={ current:'p1', progress:{p1:0,p2:0,p3:0,p4:0,p5:0,p6:0,p7:0,p8:0,p9:0,final3:0}, achievements:new Map(), xp:0, level:1 };
function calcLevel(xp){ return Math.floor(Math.sqrt((xp||0)/100))+1; }
function _xpForLevel(lv){ return (lv-1)*(lv-1)*100; }
const ACH_LABELS={ start:'Начало главы 3!', ch3_done:'Подобие изучено!' };
function loadProgress(){ try{ const s=localStorage.getItem('geometry8_ch3_progress');if(s)Object.assign(STATE.progress,JSON.parse(s)); const a=localStorage.getItem('geometry8_ch3_achievements');if(a){const p=JSON.parse(a);if(Array.isArray(p))p.forEach(id=>STATE.achievements.set(id,ACH_LABELS[id]||id));else if(p&&typeof p==='object')for(const[id,t]of Object.entries(p))STATE.achievements.set(id,(t&&t!==id)?t:(ACH_LABELS[id]||id));} STATE.xp=+(localStorage.getItem('geometry8_xp')||0);STATE.level=calcLevel(STATE.xp); }catch(e){} }
function saveProgress(){ try{localStorage.setItem('geometry8_ch3_progress',JSON.stringify(STATE.progress));localStorage.setItem('geometry8_ch3_achievements',JSON.stringify(Object.fromEntries(STATE.achievements)));localStorage.setItem('geometry8_xp',String(STATE.xp));}catch(e){} }
function bumpProgress(key,delta){ STATE.progress[key]=Math.max(0,Math.min(100,(STATE.progress[key]||0)+delta));saveProgress();refreshProgressUI();if(STATE.progress[key]>=50)markParaRead(key); }
const _TB_SLUG='geometry-8-ch3';const _markedRead=new Set();let _pendingProgressBody=null,_progressTimer=null;
function _flushProgress(){const body=_pendingProgressBody;_pendingProgressBody=null;if(!body)return;const tok=(window.LS&&LS.getToken)?LS.getToken():'';if(!tok)return;fetch('/api/textbooks/'+_TB_SLUG+'/progress',{method:'POST',headers:{'Content-Type':'application/json','Authorization':'Bearer '+tok},body:JSON.stringify(body),keepalive:true}).catch(()=>{});}
function _queueProgress(patch){_pendingProgressBody=Object.assign(_pendingProgressBody||{},patch);if(_progressTimer)clearTimeout(_progressTimer);_progressTimer=setTimeout(_flushProgress,600);}
function markLastPara(id){_queueProgress({last_para:id});}
function markParaRead(id){if(_markedRead.has(id))return;_markedRead.add(id);_queueProgress({mark_read:id});}
window.addEventListener('beforeunload',_flushProgress);
function loadServerReadState(){const tok=(window.LS&&LS.getToken)?LS.getToken():'';if(!tok)return;fetch('/api/textbooks/'+_TB_SLUG,{headers:{'Authorization':'Bearer '+tok}}).then(r=>r.ok?r.json():null).then(d=>{if(!d||!d.progress)return;(d.progress.read||[]).forEach(k=>{_markedRead.add(k);if((STATE.progress[k]||0)<50)STATE.progress[k]=100;});saveProgress();refreshProgressUI();}).catch(()=>{});}
function addXp(n,src){if(!n)return;const prev=STATE.level;STATE.xp=Math.max(0,(STATE.xp||0)+n);STATE.level=calcLevel(STATE.xp);saveProgress();refreshProgressUI();if(window.LS&&window.LS.xp)window.LS.xp.add(n,'geometry8-ch3-'+(src||'misc'));if(STATE.level>prev){const pop=document.getElementById('ach-popup');if(pop){document.getElementById('ach-text').textContent='Уровень '+STATE.level+'!';pop.classList.add('show');setTimeout(()=>pop.classList.remove('show'),2600);}if(window.confetti)try{confetti();}catch(e){}}}
const TOTAL_PARAS=10;
function refreshProgressUI(){
const total=Math.round(Object.values(STATE.progress).reduce((a,b)=>a+b,0)/TOTAL_PARAS);
const f=document.getElementById('hero-hp-fill');if(f)f.style.width=total+'%';
const t=document.getElementById('hero-hp-text');if(t)t.textContent=total+'% пройдено';
document.querySelectorAll('[data-prog-card]').forEach(el=>{const k=el.dataset.progCard;const fl=el.querySelector('.psel-prog-fill');if(fl)fl.style.width=(STATE.progress[k]||0)+'%';});
const xpBadge=document.getElementById('hero-xp-badge');if(xpBadge){xpBadge.innerHTML='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:13px;height:13px"><polygon points="12,2 22,20 2,20"/></svg> Ур. '+STATE.level+' \xb7 '+(STATE.xp||0)+' XP';}
if(STATE.current&&document.getElementById('sidebar-content')){try{buildSidebar(STATE.current);}catch(e){}}
}
function achievement(id,text){if(STATE.achievements.has(id))return;STATE.achievements.set(id,text||ACH_LABELS[id]||id);saveProgress();const pop=document.getElementById('ach-popup');if(pop){document.getElementById('ach-text').textContent=text||ACH_LABELS[id]||id;pop.classList.add('show');setTimeout(()=>pop.classList.remove('show'),3300);}addXp(20,'ach-'+id);}
const PARAS=[
{id:'p1',num:'§ 1',name:'Теорема Фалеса (обобщённая)',sub:'Параллельные и пропорции'},
{id:'p2',num:'§ 2',name:'Деление отрезка m:n',sub:'Внутреннее и внешнее'},
{id:'p3',num:'§ 3',name:'Подобные треугольники',sub:'Определение и коэффициент'},
{id:'p4',num:'§ 4',name:'Прямая || стороне',sub:'Отсекает подобный треугольник'},
{id:'p5',num:'§ 5',name:'1-й признак подобия',sub:'По двум углам (УУ)'},
{id:'p6',num:'§ 6',name:'2-й признак подобия',sub:'По двум сторонам и углу (СУС)'},
{id:'p7',num:'§ 7',name:'3-й признак подобия',sub:'По трём сторонам (ССС)'},
{id:'p8',num:'§ 8',name:'Биссектриса треугольника',sub:'Делит сторону пропорционально'},
{id:'p9',num:'§ 9',name:'Отношение площадей',sub:'S₁/S₂ = k²'},
{id:'final3',num:'★',name:'Финал главы',sub:'Итоги · Боссы',final:true},
];
function buildParaSelector(){const g=document.getElementById('psel-grid');g.innerHTML='';PARAS.forEach(p=>{const card=document.createElement('div');card.className='psel-card'+(p.final?' final':'');card.dataset.id=p.id;card.dataset.progCard=p.id;card.innerHTML=`<div class="psel-num">${p.num}</div><div class="psel-name">${p.name}</div><div class="psel-prog"><div class="psel-prog-fill"></div></div>`;card.addEventListener('click',()=>goTo(p.id));g.appendChild(card);});}
const BUILT=new Set();
const BUILDERS={p1:()=>buildP1(),p2:()=>buildP2(),p3:()=>buildP3(),p4:()=>buildP4(),p5:()=>buildP5(),p6:()=>buildP6(),p7:()=>buildP7(),p8:()=>buildP8(),p9:()=>buildP9(),final3:()=>buildFinal3()};
function ensureBuilt(id){if(BUILT.has(id))return;const fn=BUILDERS[id];if(fn){fn();BUILT.add(id);}}
function goTo(id){STATE.current=id;ensureBuilt(id);document.querySelectorAll('.sec').forEach(s=>s.classList.remove('active'));const el=document.getElementById('sec-'+id);if(el)el.classList.add('active');document.querySelectorAll('.psel-card').forEach(c=>c.classList.toggle('active',c.dataset.id===id));buildSidebar(id);window.scrollTo({top:0,behavior:'smooth'});if((STATE.progress[id]||0)<10)bumpProgress(id,10);if(window.renderMathInElement)setTimeout(()=>renderMath(el),0);setTimeout(()=>{try{wrapGlossary(el);}catch(e){}},60);markLastPara(id);}
const SIDEBARS={
p1:{title:'Шпаргалка § 1',rows:[['Теорема Фалеса','параллельные прямые пропорционально рассекают две прямые'],['Следствие','прямая || стороне треугольника отсекает подобный треугольник']]},
p2:{title:'Шпаргалка § 2',rows:[['Внутреннее деление','точка между крайними'],['Отношение','$AM:MB = m:n$']]},
p3:{title:'Шпаргалка § 3',rows:[['Подобие','$\\triangle ABC \\sim \\triangle A\'B\'C\'$'],['Коэффициент','$k = AB/A\'B\' = BC/B\'C\'$'],['Углы','попарно равны']]},
p4:{title:'Шпаргалка § 4',rows:[['Прямая || стороне','отсекает треугольник, подобный данному'],['Пропорция','отрезки пропорциональны']]},
p5:{title:'Шпаргалка § 5',rows:[['1-й признак — по двум углам (УУ)','два угла одного равны двум углам другого'],['Следствие','два прямоугольных с общим острым углом подобны']]},
p6:{title:'Шпаргалка § 6',rows:[['2-й признак — по двум сторонам и углу (СУС)','стороны пропорциональны, угол между ними равен'],['$\\dfrac{AB}{A\'B\'} = \\dfrac{AC}{A\'C\'}$, угол $A = A\'$','']]},
p7:{title:'Шпаргалка § 7',rows:[['3-й признак — по трём сторонам (ССС)','все три пары сторон пропорциональны'],['$\\dfrac{a}{a\'}=\\dfrac{b}{b\'}=\\dfrac{c}{c\'}$','']]},
p8:{title:'Шпаргалка § 8',rows:[['Биссектриса','делит противоположную сторону в отношении прилежащих сторон'],['$\\dfrac{BD}{DC}=\\dfrac{AB}{AC}$','']]},
p9:{title:'Шпаргалка § 9',rows:[['Отношение площадей','$\\dfrac{S_1}{S_2} = k^2$'],['$k$','коэффициент подобия']]},
final3:{title:'Финал главы',rows:[['9 параграфов','подобие изучено'],['3 признака','по двум углам, по двум сторонам и углу, по трём сторонам']]},
};
const TIPS=[
{sec:'p1',html:'Теорема Фалеса (обобщённая): несколько параллельных прямых пропорционально пересекают любые две секущие.'},
{sec:'p2',html:'Деление в отношении $m:n$: $AM = \\dfrac{m}{m+n}\\cdot AB$.'},
{sec:'p3',html:'Коэффициент подобия $k$ — отношение <b>соответственных</b> сторон.'},
{sec:'p4',html:'Прямая, параллельная стороне треугольника, отсекает треугольник, подобный исходному.'},
{sec:'p5',html:'1-й признак — по двум углам: два угла равны. Это наиболее часто используемый признак!'},
{sec:'p6',html:'2-й признак — по двум сторонам и углу (СУС): стороны пропорциональны и угол между ними равен.'},
{sec:'p7',html:'3-й признак — по трём сторонам (ССС): все три пары сторон пропорциональны.'},
{sec:'p8',html:'Биссектриса угла треугольника делит противоположную сторону в отношении смежных сторон.'},
{sec:'p9',html:'Площади подобных фигур относятся как <b>квадрат</b> коэффициента подобия: $S_1/S_2 = k^2$.'},
{sec:'final3',html:'Три признака подобия — главный инструмент геометрических доказательств.'},
];
function buildSidebar(id){const box=document.getElementById('sidebar-content');const sb=SIDEBARS[id]||SIDEBARS.p1;let html='';const xpForLv=_xpForLevel(STATE.level),xpNext=_xpForLevel(STATE.level+1);const xpInLv=STATE.xp-xpForLv,xpRange=xpNext-xpForLv,xpPct=xpRange>0?Math.round(xpInLv/xpRange*100):100;html+=`<div class="xp-card" data-gamified><div class="xp-card-title" data-gamified><span>XP-прогресс</span><span class="xp-level">Ур. ${STATE.level}</span></div><div class="xp-bar"><div class="xp-fill" style="width:${xpPct}%"></div></div><div class="xp-nums"><span>${STATE.xp} XP</span><span>${xpNext} XP</span></div></div>`;html+=`<div class="sidecard"><h4>${sb.title}</h4>`;sb.rows.forEach(([k,v])=>{html+=`<div class="sidecard-row"><b>${k}</b>${v?' — '+v:''}</div>`;});html+='</div>';const tip=TIPS.find(t=>t.sec===id)||TIPS[0];html+=`<div class="sidecard" style="background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--pri-soft));border-color:var(--warn,#f59e0b)"><h4 style="color:#4c1d95;display:flex;align-items:center;gap:6px"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px"><polygon points="12,2 22,20 2,20"/></svg>Подсказка</h4><div class="sidecard-row" style="margin-bottom:0;font-size:.84rem;line-height:1.55">${tip.html}</div></div>`;if(STATE.achievements.size>0){html+=`<div class="sidecard"><h4>Достижения <span style="color:var(--warn);float:right">${STATE.achievements.size}</span></h4>`;[...STATE.achievements.values()].slice(-4).forEach(text=>{html+=`<div class="sidecard-row" style="font-size:.78rem;color:var(--ok)">&#10003; ${text}</div>`;});html+='</div>';}box.innerHTML=html;if(window.renderMathInElement)try{renderMath(box);}catch(e){}}
function initTheme(){const t=localStorage.getItem('geometry8_ch3_theme')||'light';if(t==='dark')document.documentElement.classList.add('dark');document.getElementById('theme-lab').textContent=t==='dark'?'Светлая':'Тёмная';document.getElementById('theme-btn').addEventListener('click',()=>{document.documentElement.classList.toggle('dark');const dark=document.documentElement.classList.contains('dark');localStorage.setItem('geometry8_ch3_theme',dark?'dark':'light');document.getElementById('theme-lab').textContent=dark?'Светлая':'Тёмная';});}
function renderMath(root){if(window.renderMathInElement){try{renderMathInElement(root,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false});}catch(e){}}}
function feedback(elm,ok,text){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',final3:'Финал'};let h='<div class="sec-nav">';h+=prev?`<button class="btn" onclick="goTo('${prev}')"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> ${NAMES[prev]}</button>`:'<span></span>';h+=next?`<button class="btn primary" onclick="goTo('${next}')">${NAMES[next]} <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></button>`:'<span></span>';h+='</div>';return h;}
let _confettiCanvas=null,_confettiParticles=[],_confettiRaf=null;
function confetti(){if(!_confettiCanvas){_confettiCanvas=document.createElement('canvas');_confettiCanvas.style.cssText='position:fixed;top:0;left:0;width:100%;height:100%;pointer-events:none;z-index:9999';document.body.appendChild(_confettiCanvas);}const c=_confettiCanvas;c.width=window.innerWidth;c.height=window.innerHeight;const ctx=c.getContext('2d');const colors=['#7c3aed','#8b5cf6','#a78bfa','#f59e0b','#10b981'];for(let i=0;i<80;i++){_confettiParticles.push({x:window.innerWidth/2+(Math.random()-.5)*200,y:window.innerHeight/2,vx:(Math.random()-.5)*14,vy:-10-Math.random()*10,g:.4,life:100,color:colors[i%colors.length],r:4+Math.random()*4,rot:0,vRot:(Math.random()-.5)*.3});}if(_confettiRaf)cancelAnimationFrame(_confettiRaf);function frame(){ctx.clearRect(0,0,c.width,c.height);_confettiParticles=_confettiParticles.filter(p=>{p.x+=p.vx;p.y+=p.vy;p.vy+=p.g;p.life--;p.rot+=p.vRot;ctx.save();ctx.translate(p.x,p.y);ctx.rotate(p.rot);ctx.fillStyle=p.color;ctx.fillRect(-p.r,-p.r/2,p.r*2,p.r);ctx.restore();return p.life>0&&p.y<c.height+50;});if(_confettiParticles.length>0)_confettiRaf=requestAnimationFrame(frame);else{ctx.clearRect(0,0,c.width,c.height);_confettiRaf=null;}}frame();}
const GLOSSARY=[
{term:'пропорциональность',def:'Равенство двух отношений: $a/b = c/d$. Числа $a,b,c,d$ называются пропорциональными.',sec:'p1',aliases:['пропорциональность','пропорциональны','пропорциональные','пропорциональных','пропорциональными']},
{term:'подобные треугольники',def:'Треугольники, у которых углы равны попарно и стороны пропорциональны.',sec:'p3',aliases:['подобные треугольники','подобных треугольников','подобными треугольниками','подобный треугольник','подобного треугольника']},
{term:'коэффициент подобия',def:'Отношение соответственных сторон подобных треугольников.',sec:'p3',aliases:['коэффициент подобия','коэффициента подобия','коэффициенте подобия']},
{term:'биссектриса',def:'Луч из вершины угла, делящий угол пополам.',sec:'p8',aliases:['биссектриса','биссектрисы','биссектрису','биссектрисой']},
{term:'теорема Фалеса',def:'Параллельные прямые, пересекая две секущие, отсекают пропорциональные отрезки.',sec:'p1',aliases:['теорема Фалеса','теореме Фалеса','теоремы Фалеса']},
{term:'многоугольник',def:'Замкнутая ломаная линия, образующая плоскую фигуру.',sec:'p1',aliases:['многоугольник','многоугольника','многоугольников']},
{term:'медиана',def:'Отрезок от вершины до середины противоположной стороны.',sec:'p1',aliases:['медиана','медианы','медиан','медиану']},
{term:'площадь',def:'Числовая мера размера плоской фигуры.',sec:'p9',aliases:['площадь','площади','площадью']},
{term:'параллелограмм',def:'Четырёхугольник, у которого противоположные стороны параллельны.',sec:'p1',aliases:['параллелограмм','параллелограмма','параллелограммы']},
{term:'теорема Пифагора',def:'$a^2+b^2=c^2$ в прямоугольном треугольнике.',sec:'p9',aliases:['теорема Пифагора','теоремы Пифагора','теореме Пифагора']},
{term:'касательная',def:'Прямая, касающаяся окружности в одной точке.',sec:'p1',aliases:['касательная','касательной','касательную']},
{term:'вписанный угол',def:'Угол с вершиной на окружности, стороны которого — хорды.',sec:'p1',aliases:['вписанный угол','вписанного угла']},
{term:'центральный угол',def:'Угол с вершиной в центре окружности.',sec:'p1',aliases:['центральный угол','центрального угла']},
{term:'хорда',def:'Отрезок, соединяющий две точки окружности.',sec:'p1',aliases:['хорда','хорды','хорде','хорд']},
{term:'трапеция',def:'Четырёхугольник с одной парой параллельных сторон.',sec:'p1',aliases:['трапеция','трапеции','трапецию']},
{term:'диагональ',def:'Отрезок, соединяющий несмежные вершины многоугольника.',sec:'p1',aliases:['диагональ','диагонали','диагоналей']},
{term:'гипотенуза',def:'Сторона прямоугольного треугольника напротив прямого угла.',sec:'p1',aliases:['гипотенуза','гипотенузы','гипотенузу']},
{term:'высота',def:'Перпендикуляр из вершины на противоположную сторону.',sec:'p1',aliases:['высота','высоты','высоту']},
{term:'пифагорова тройка',def:'Три натуральных числа $(a,b,c)$ с $a^2+b^2=c^2$.',sec:'p9',aliases:['пифагорова тройка','пифагоровы тройки']},
{term:'секущая',def:'Прямая, пересекающая окружность в двух точках.',sec:'p1',aliases:['секущая','секущей','секущую']},
{term:'хорда',def:'Отрезок, соединяющий две точки окружности.',sec:'p1',aliases:['хорда','хорды','хорд']},
];
function wrapGlossary(root){if(!root||root.__glossDone)return;const allAliases=[];GLOSSARY.forEach((g,i)=>g.aliases.forEach(a=>allAliases.push({a,i})));allAliases.sort((x,y)=>y.a.length-x.a.length);const re=new RegExp('(?<![\\w\\u0400-\\u04ff-])('+allAliases.map(x=>x.a.replace(/[.*+?^${}()|[\]\\]/g,'\\$&')).join('|')+')(?![\\w\\u0400-\\u04ff-])','iu');const walker=document.createTreeWalker(root,NodeFilter.SHOW_TEXT,{acceptNode(node){const p=node.parentElement;if(!p)return NodeFilter.FILTER_REJECT;if(p.closest('.katex,.gloss-term,button,input,select,.wg-badge,.card-icon,.sec-num,.psel-num,.hdr,.ach-popup,script,style,.search-modal,.sidecard,.gloss-tip'))return NodeFilter.FILTER_REJECT;if(!re.test(node.nodeValue))return NodeFilter.FILTER_REJECT;return NodeFilter.FILTER_ACCEPT;}});const nodes=[];let n;while((n=walker.nextNode()))nodes.push(n);nodes.forEach(node=>{const text=node.nodeValue;const out=document.createDocumentFragment();let cursor=0;const global=new RegExp(re.source,'giu');let m;while((m=global.exec(text))!==null){if(m.index>cursor)out.appendChild(document.createTextNode(text.slice(cursor,m.index)));const found=m[0].toLowerCase();const hit=allAliases.find(x=>x.a.toLowerCase()===found);const g=hit?GLOSSARY[hit.i]:null;const sp=document.createElement('span');sp.className='gloss-term';sp.dataset.gloss=g?g.term:'';sp.textContent=m[0];out.appendChild(sp);cursor=m.index+m[0].length;}if(cursor<text.length)out.appendChild(document.createTextNode(text.slice(cursor)));node.parentNode.replaceChild(out,node);});root.__glossDone=true;}
function initGlossaryTip(){const tip=document.getElementById('gloss-tip');if(!tip)return;let lockOpen=null;function show(elm){const g=GLOSSARY.find(x=>x.term===elm.dataset.gloss);if(!g)return;tip.innerHTML='<b>'+g.term[0].toUpperCase()+g.term.slice(1)+'</b><div style="margin-top:4px">'+g.def+'</div><div style="margin-top:6px;font-size:.72rem;color:var(--muted);text-transform:uppercase;letter-spacing:.06em">См. § '+g.sec.replace('p','')+'</div>';if(window.renderMathInElement)renderMath(tip);const r=elm.getBoundingClientRect();tip.classList.add('show');const tw=tip.offsetWidth,th=tip.offsetHeight;let left=r.left,top=r.bottom+8;if(left+tw>window.innerWidth-12)left=window.innerWidth-tw-12;if(top+th>window.innerHeight-12)top=r.top-th-8;tip.style.left=Math.max(8,left)+'px';tip.style.top=Math.max(8,top)+'px';}function hide(){tip.classList.remove('show');}document.addEventListener('mouseover',e=>{const elm=e.target.closest&&e.target.closest('.gloss-term');if(elm&&!lockOpen)show(elm);});document.addEventListener('mouseout',e=>{const elm=e.target.closest&&e.target.closest('.gloss-term');if(elm&&!lockOpen)hide();});document.addEventListener('click',e=>{const elm=e.target.closest&&e.target.closest('.gloss-term');if(elm){if(lockOpen===elm){lockOpen=null;hide();}else{lockOpen=elm;show(elm);}}else if(lockOpen&&!e.target.closest('.gloss-tip')){lockOpen=null;hide();}});}
const SEARCH_INDEX=(function(){const arr=[];PARAS.forEach(p=>arr.push({kind:'Параграф',title:p.num+' '+p.name,desc:p.sub||'',sec:p.id}));GLOSSARY.forEach(g=>arr.push({kind:'Понятие',title:g.term,desc:g.def.replace(/\$/g,''),sec:g.sec,gloss:g.term}));[['Формула','Теорема Фалеса — пропорциональность','§1','p1'],['Формула','1-й признак подобия — два угла','§5','p5'],['Формула','Биссектриса: BD/DC = AB/AC','§8','p8'],['Формула','Отношение площадей = k²','§9','p9']].forEach(([k,t,d,s])=>arr.push({kind:k,title:t,desc:d,sec:s}));return arr;})();
function initSearch(){const modal=document.getElementById('search-modal'),inp=document.getElementById('search-input'),out=document.getElementById('search-results'),btn=document.getElementById('search-btn');if(!modal||!inp||!out)return;let cur=0,rows=[];function score(q,it){const t=(it.title+' '+it.desc).toLowerCase();if(t.includes(q))return 100+(it.title.toLowerCase().startsWith(q)?50:0);let s=0;q.split(/\s+/).forEach(w=>{if(w&&t.includes(w))s+=10;});return s;}function rank(q){q=q.trim().toLowerCase();if(!q)return SEARCH_INDEX.slice(0,12);return SEARCH_INDEX.map(it=>({it,s:score(q,it)})).filter(x=>x.s>0).sort((a,b)=>b.s-a.s).slice(0,20).map(x=>x.it);}function render(){cur=0;if(!rows.length){out.innerHTML='<div class="search-empty">Ничего не найдено</div>';return;}out.innerHTML=rows.map((r,i)=>`<button class="search-row${i===0?' active':''}" data-i="${i}"><div class="sr-kind">${r.kind}</div><div class="sr-title">${r.title}</div>${r.desc?`<div class="sr-desc">${r.desc.length>90?r.desc.slice(0,90)+'…':r.desc}</div>`:''}</button>`).join('');out.querySelectorAll('.search-row').forEach(b=>b.addEventListener('click',()=>{cur=+b.dataset.i;pick();}));}function pick(){const r=rows[cur];if(!r)return;close();goTo(r.sec);if(r.gloss){setTimeout(()=>{const sec=document.getElementById('sec-'+r.sec);const elm=sec&&sec.querySelector('[data-gloss="'+r.gloss+'"]');if(elm){elm.scrollIntoView({behavior:'smooth',block:'center'});elm.style.transition='background .3s';elm.style.background='var(--warn,#f59e0b)';setTimeout(()=>{elm.style.background='';},1400);}},400);}}function move(d){const items=out.querySelectorAll('.search-row');if(!items.length)return;items[cur]&&items[cur].classList.remove('active');cur=(cur+d+items.length)%items.length;items[cur].classList.add('active');items[cur].scrollIntoView({block:'nearest'});}function open(){modal.classList.add('show');inp.value='';rows=rank('');render();setTimeout(()=>inp.focus(),50);}function close(){modal.classList.remove('show');}btn&&btn.addEventListener('click',open);modal.addEventListener('click',e=>{if(e.target===modal)close();});inp.addEventListener('input',()=>{rows=rank(inp.value);render();});inp.addEventListener('keydown',e=>{if(e.key==='ArrowDown'){e.preventDefault();move(1);}else if(e.key==='ArrowUp'){e.preventDefault();move(-1);}else if(e.key==='Enter'){e.preventDefault();pick();}else if(e.key==='Escape'){e.preventDefault();close();}});document.addEventListener('keydown',e=>{if((e.ctrlKey||e.metaKey)&&(e.key==='k'||e.key==='K')){e.preventDefault();if(modal.classList.contains('show'))close();else open();}});}
function initSidebarToggle(){const side=document.getElementById('col-side'),back=document.getElementById('col-side-backdrop'),btn=document.getElementById('sidebar-btn');if(!side||!btn)return;function open(){side.classList.add('open');back.classList.add('show');}function close(){side.classList.remove('open');back.classList.remove('show');}btn.addEventListener('click',()=>{if(side.classList.contains('open'))close();else open();});back.addEventListener('click',close);document.addEventListener('keydown',e=>{if(e.key==='Escape')close();});}
function init(){loadProgress();initTheme();initSidebarToggle();initGlossaryTip();initSearch();buildParaSelector();refreshProgressUI();loadServerReadState();goTo('p1');setTimeout(()=>achievement('start','Начало главы 3!'),600);if(window.LS&&window.LS.xp){window.LS.xp.load().then(function(s){if(s&&s.xp>STATE.xp){STATE.xp=s.xp;STATE.level=calcLevel(STATE.xp);saveProgress();refreshProgressUI();if(STATE.current)buildSidebar(STATE.current);}});}}
document.addEventListener('DOMContentLoaded',init);
function buildP1(){
const box=document.getElementById('p1-body');
let html='';
html+=makeCard('theory','Теорема Фалеса (классическая)','1.1',`
<p><b>Теорема Фалеса.</b> Если параллельные прямые пересекают одну из сторон угла в равных частях, то они отсекают равные части и на другой стороне угла.</p>
<p style="margin-top:8px"><b>Следствие (обобщённая теорема Фалеса).</b> Если несколько параллельных прямых пересекают две секущие прямые, то они отсекают на этих секущих <em>пропорциональные</em> отрезки:</p>
$$\\dfrac{AB}{BC} = \\dfrac{A'B'}{B'C'}$$
<p style="margin-top:8px">где $A,B,C$ — точки на первой секущей, $A',B',C'$ — точки на второй секущей, отсекаемые теми же параллельными прямыми.</p>
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 280 170" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- угол с вершиной O=(30,150); два луча, три параллельные прямые -->
<!-- луч 1: O→(260,60), луч 2: O→(260,150) -->
<line x1="30" y1="150" x2="260" y2="60" stroke="#7c3aed" stroke-width="2"/>
<line x1="30" y1="150" x2="260" y2="150" stroke="#7c3aed" stroke-width="2"/>
<!-- параллельные прямые (3 шт, горизонтальные) пересекают оба луча -->
<!-- луч1: при x=80→y=120.5, при x=160→y=91, при x=240→y=61.5 -->
<!-- луч2: при x=80→y=150, при x=160→y=150, при x=240→y=150 -->
<line x1="80" y1="30" x2="80" y2="160" stroke="#a78bfa" stroke-width="1.5" stroke-dasharray="4,3"/>
<line x1="160" y1="30" x2="160" y2="160" stroke="#a78bfa" stroke-width="1.5" stroke-dasharray="4,3"/>
<line x1="240" y1="30" x2="240" y2="160" stroke="#a78bfa" stroke-width="1.5" stroke-dasharray="4,3"/>
<!-- точки на луче 1 -->
<circle cx="80" cy="121" r="3.5" fill="#7c3aed"/>
<circle cx="160" cy="91" r="3.5" fill="#7c3aed"/>
<circle cx="240" cy="62" r="3.5" fill="#7c3aed"/>
<!-- точки на луче 2 -->
<circle cx="80" cy="150" r="3.5" fill="#7c3aed"/>
<circle cx="160" cy="150" r="3.5" fill="#7c3aed"/>
<circle cx="240" cy="150" r="3.5" fill="#7c3aed"/>
<!-- метки луч1 -->
<text x="76" y="117" text-anchor="end" font-size="10" font-weight="700" fill="#6d28d9">A</text>
<text x="156" y="87" text-anchor="end" font-size="10" font-weight="700" fill="#6d28d9">B</text>
<text x="236" y="58" text-anchor="end" font-size="10" font-weight="700" fill="#6d28d9">C</text>
<!-- метки луч2 -->
<text x="80" y="165" text-anchor="middle" font-size="10" font-weight="700" fill="#6d28d9">A'</text>
<text x="160" y="165" text-anchor="middle" font-size="10" font-weight="700" fill="#6d28d9">B'</text>
<text x="240" y="165" text-anchor="middle" font-size="10" font-weight="700" fill="#6d28d9">C'</text>
<text x="18" y="153" font-size="10" font-weight="700" fill="#6d28d9">O</text>
<text x="140" y="20" text-anchor="middle" font-size="10" fill="#6d28d9" font-style="italic">AB/BC = A'B'/B'C'</text>
</svg>
</div>`);
html+=makeCard('rule','Пропорциональность отрезков','1.2',`
<p>Обозначим точки пересечения параллельных прямых $\\ell_1,\\ell_2,\\ell_3$ со сторонами угла как $A,B,C$ и $A',B',C'$ соответственно. Тогда:</p>
$$\\dfrac{AB}{BC} = \\dfrac{A'B'}{B'C'}, \\quad \\dfrac{AB}{AC} = \\dfrac{A'B'}{A'C'}$$
<p style="margin-top:8px">Это выражает <b>пропорциональность отрезков</b>, отсекаемых параллельными прямыми.</p>
<p style="margin-top:8px"><b>Применение:</b> с помощью теоремы Фалеса доказывают подобие треугольников, делят отрезок в заданном отношении, строят пропорциональные отрезки.</p>
<div style="display:flex;justify-content:center;margin-top:12px">
<svg viewBox="0 0 280 130" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- треугольник ABC с точками D,E на сторонах, DE || BC -->
<polygon points="140,15 30,115 250,115" fill="rgba(124,58,237,.1)" stroke="#7c3aed" stroke-width="2"/>
<!-- DE параллельна BC, D на AB, E на AC -->
<line x1="85" y1="65" x2="195" y2="65" stroke="#a78bfa" stroke-width="2" stroke-dasharray="5,3"/>
<circle cx="85" cy="65" r="3" fill="#7c3aed"/>
<circle cx="195" cy="65" r="3" fill="#7c3aed"/>
<text x="140" y="12" text-anchor="middle" font-size="11" font-weight="700" fill="#6d28d9">A</text>
<text x="24" y="122" font-size="11" font-weight="700" fill="#6d28d9">B</text>
<text x="252" y="122" font-size="11" font-weight="700" fill="#6d28d9">C</text>
<text x="78" y="63" text-anchor="end" font-size="10" font-weight="700" fill="#8b5cf6">D</text>
<text x="201" y="63" font-size="10" font-weight="700" fill="#8b5cf6">E</text>
<text x="140" y="82" text-anchor="middle" font-size="9" fill="#6d28d9">DE ∥ BC</text>
<text x="140" y="126" text-anchor="middle" font-size="9" fill="#6d28d9" font-style="italic">AD/DB = AE/EC</text>
</svg>
</div>`);
html+=makeCard('example','Пример применения','1.3',`
<p><b>Пример.</b> Параллельные прямые отсекают на первой стороне угла отрезки $AB = 6$ и $BC = 9$. На второй стороне $A'B' = 4$. Найти $B'C'$.</p>
<p style="margin-top:8px"><b>Решение:</b> по теореме Фалеса $\\dfrac{AB}{BC}=\\dfrac{A'B'}{B'C'}$, то есть $\\dfrac{6}{9}=\\dfrac{4}{B'C'}$.</p>
<p style="margin-top:4px">$B'C' = \\dfrac{4 \\cdot 9}{6} = 6$.</p>`);
/* ИНТЕРАКТИВ 1 — SVG-угол с параллельными прямыми */
html+=`<div class="wg" id="p1-angle-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Угол с параллельными прямыми</div></div>
<div class="wg-help">Меняй количество параллельных прямых и угол наклона второй стороны. Видишь, как отношения остаются равными!</div>
<div class="sliders">
<label>Кол-во параллельных: <b id="p1-npar-val">3</b>
<input type="range" min="2" max="6" value="3" id="p1-npar-sl">
</label>
<label>Угол второй стороны: <b id="p1-angle-val">30</b>°
<input type="range" min="10" max="60" value="30" id="p1-angle-sl">
</label>
</div>
<div id="p1-angle-svg" style="display:flex;justify-content:center"></div>
<div id="p1-angle-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.92rem;line-height:1.8"></div>
</div>`;
/* ИНТЕРАКТИВ 2 — Пошаговое доказательство */
html+=`<div class="wg" id="p1-proof-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Доказательство теоремы Фалеса — по шагам</div></div>
<div class="wg-help">Нажимай «Далее» — каждый шаг показывает ключевую идею.</div>
<div id="p1-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p1-proof-desc" style="padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.6;margin-bottom:10px;min-height:60px"></div>
<div style="display:flex;gap:8px">
<button class="btn primary" id="p1-proof-next">Далее</button>
<button class="btn" id="p1-proof-reset">Сначала</button>
</div>
</div>`;
/* ИНТЕРАКТИВ 3 — Калькулятор пропорций */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Калькулятор пропорций (Теорема Фалеса)</div></div>
<div class="wg-help">Дано три отрезка из пропорции $\\dfrac{a}{b}=\\dfrac{c}{x}$ — найди четвёртый.</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
<div style="display:flex;flex-direction:column;align-items:center;gap:4px">
<input type="number" id="p1-pa" class="tinp" placeholder="a" style="width:70px" min="0.001">
<span style="font-size:.8rem;color:var(--muted)">AB</span>
</div>
<span style="font-size:1.3rem;color:var(--muted)">/</span>
<div style="display:flex;flex-direction:column;align-items:center;gap:4px">
<input type="number" id="p1-pb" class="tinp" placeholder="b" style="width:70px" min="0.001">
<span style="font-size:.8rem;color:var(--muted)">BC</span>
</div>
<span style="font-size:1.3rem;color:var(--muted)">=</span>
<div style="display:flex;flex-direction:column;align-items:center;gap:4px">
<input type="number" id="p1-pc" class="tinp" placeholder="c" style="width:70px" min="0.001">
<span style="font-size:.8rem;color:var(--muted)">A'B'</span>
</div>
<span style="font-size:1.3rem;color:var(--muted)">/</span>
<div style="display:flex;flex-direction:column;align-items:center;gap:4px">
<input type="number" id="p1-px" class="tinp" placeholder="x" style="width:70px" disabled style="background:var(--sec-acc-soft)">
<span style="font-size:.8rem;color:var(--muted)">B'C'</span>
</div>
<button class="btn primary" id="p1-pcalc">Найти x</button>
</div>
<div id="p1-pcalc-out" style="display:none;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem"></div>
</div>`;
/* ИНТЕРАКТИВ 4 — Тренажёр */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр §1 — Теорема Фалеса</div></div>
<div class="wg-help">5 задач на пропорциональность отрезков. Введи ответ и нажми «Проверить».</div>
<div class="score-display"><span>Задача <b id="p1-tr-i">1</b> / 5</span><span>Очки: <b id="p1-tr-score">0</b></span></div>
<div id="p1-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.02rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p1-tr-ans" class="tinp" placeholder="Ответ" style="width:120px">
<button class="btn primary" id="p1-tr-go">Проверить</button>
<button class="btn" id="p1-tr-start">Начать</button>
</div>
<div class="feedback" id="p1-tr-fb" style="display:none"></div>
</div>`;
/* ИНТЕРАКТИВ 5 — DnD-сортер */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Определи: пропорциональные или нет?</div></div>
<div class="wg-help">Перетащи каждую карточку в нужную колонку.</div>
<div id="p1-dnd-pool" class="dnd-pool"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:10px">
<div class="drop-box" id="p1-drop-yes"><h5>Пропорциональные</h5><div class="drop-items" id="p1-drop-yes-items"></div></div>
<div class="drop-box" id="p1-drop-no"><h5>Непропорциональные</h5><div class="drop-items" id="p1-drop-no-items"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p1-dnd-check">Проверить</button><button class="btn" id="p1-dnd-reset">Сбросить</button></div>
<div class="feedback" id="p1-dnd-fb" style="display:none"></div>
</div>`;
/* ИНТЕРАКТИВ 6 — Босс §1 */
html+=`<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §1</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">4 задачи — каждая верная даёт +5 XP.</div>
<div id="p1-boss-tasks"></div>
</div>`;
html+=`<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p1-read-btn" onclick="addXp(10,'p1-read');bumpProgress('p1',40);this.textContent='Прочитано!';this.disabled=true;">
<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>
Я прочитал §1 (+10 XP)
</button>
</div>`;
html+=secNav(null,'p2');
box.innerHTML=html;
if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
/* == INIT ИНТЕРАКТИВ 1: угол с параллелями == */
(function(){
const nSl=document.getElementById('p1-npar-sl');
const nVal=document.getElementById('p1-npar-val');
const aSl=document.getElementById('p1-angle-sl');
const aVal=document.getElementById('p1-angle-val');
const svgWrap=document.getElementById('p1-angle-svg');
const info=document.getElementById('p1-angle-info');
function draw(){
const n=+nSl.value;
const angDeg=+aSl.value;
nVal.textContent=n;
aVal.textContent=angDeg;
const W=320, H=180;
const Ox=20, Oy=H-20;
const ang=angDeg*Math.PI/180;
const L=280;
// луч 1: горизонталь вправо
// луч 2: под углом ang вверх
const pts1=[]; // точки на луче1 (горизональном)
const pts2=[]; // точки на луче2
const step=L/(n);
for(let i=1;i<=n;i++){
const x=Ox+step*i;
pts1.push({x, y:Oy});
pts2.push({x, y:Oy-step*i*Math.tan(ang)});
}
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
// лучи
s+=`<line x1="${Ox}" y1="${Oy}" x2="${Ox+L+10}" y2="${Oy}" stroke="#7c3aed" stroke-width="2"/>`;
s+=`<line x1="${Ox}" y1="${Oy}" x2="${Ox+L+10}" y2="${Oy-(L+10)*Math.tan(ang)}" stroke="#7c3aed" stroke-width="2"/>`;
// параллельные прямые (вертикальные)
for(let i=0;i<n;i++){
const cx=pts1[i].x;
s+=`<line x1="${cx}" y1="5" x2="${cx}" y2="${H-5}" stroke="#a78bfa" stroke-width="1.5" stroke-dasharray="4,3"/>`;
s+=`<circle cx="${pts1[i].x}" cy="${pts1[i].y}" r="4" fill="#7c3aed"/>`;
s+=`<circle cx="${pts2[i].x}" cy="${pts2[i].y}" r="4" fill="#7c3aed"/>`;
const lbl=String.fromCharCode(65+i);
s+=`<text x="${pts1[i].x}" y="${Oy+14}" text-anchor="middle" font-size="10" font-weight="700" fill="#6d28d9">${lbl}</text>`;
s+=`<text x="${pts2[i].x+4}" y="${pts2[i].y-5}" font-size="10" font-weight="700" fill="#6d28d9">${lbl}'</text>`;
}
s+=`<text x="${Ox-4}" y="${Oy+4}" font-size="10" font-weight="700" fill="#6d28d9">O</text>`;
s+='</svg>';
svgWrap.innerHTML=s;
// вычисляем отрезки
let infoHtml='';
for(let i=0;i<n-1;i++){
const seg1=Math.round((pts1[i+1].x-pts1[i].x)*10)/10;
const dy2=pts2[i+1].y-pts2[i].y, dx2=pts2[i+1].x-pts2[i].x;
const seg2=Math.round(Math.sqrt(dx2*dx2+dy2*dy2)*10)/10;
const lA=String.fromCharCode(65+i), lB=String.fromCharCode(66+i);
infoHtml+=`<span style="margin-right:16px">${lA}${lB} = <b>${seg1}</b> px; ${lA}'${lB}' = <b>${fmt(seg2)}</b> px</span>`;
}
if(n>=2){
const r1=Math.round((pts1[1].x-pts1[0].x)/(pts1[0].x-pts1[0].x+1)*1000)/1000;
const dy0=pts2[1].y-pts2[0].y, dx0=pts2[1].x-pts2[0].x;
const s1=pts1[1].x-pts1[0].x;
const s2=Math.sqrt(dx0*dx0+dy0*dy0);
infoHtml+=`<div style="margin-top:6px;color:var(--sec-acc-d,var(--pri2));font-weight:700">Отношение AB/A'B' = ${fmt(s1/s2)} ≈ постоянно для всех пар</div>`;
}
info.innerHTML=infoHtml;
addXp(1,'p1-angle');
}
nSl.addEventListener('input',draw);
aSl.addEventListener('input',draw);
draw();
})();
/* == INIT ИНТЕРАКТИВ 2: пошаговое доказательство == */
(function(){
const steps=[
{desc:'<b>Шаг 1.</b> Даны два луча из вершины O и три параллельные прямые, пересекающие их в точках A, B, C и A\', B\', C\'.',
svg:()=>{
// Ray 1: horizontal from O=(20,140) to (250,140)
// Ray 2: from O=(20,140) to (250,60), slope = (60-140)/(250-20) = -80/230
// Parallel transversals at x=70,150,230 (vertical)
// Intersection with ray 2: y = 140 + (x-20)*(-80/230)
// x=70: y = 140 - 50*80/230 ≈ 123
// x=150: y = 140 - 130*80/230 ≈ 95
// x=230: y = 140 - 210*80/230 ≈ 67
const W=260,H=160,Ox=20,Oy=140;
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
s+=`<line x1="${Ox}" y1="${Oy}" x2="250" y2="${Oy}" stroke="#7c3aed" stroke-width="2"/>`;
s+=`<line x1="${Ox}" y1="${Oy}" x2="250" y2="60" stroke="#7c3aed" stroke-width="2"/>`;
// parallels: connect point on ray1 to correctly computed point on ray2
[[70,140,70,123],[150,140,150,95],[230,140,230,67]].forEach(([x1,y1,x2,y2])=>{
s+=`<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="#a78bfa" stroke-width="1.5" stroke-dasharray="4,3"/>`;
});
s+=`<text x="${Ox-4}" y="${Oy+4}" font-size="10" fill="#6d28d9" font-weight="700">O</text>`;
['A','B','C'].forEach((l,i)=>s+=`<text x="${70+i*80}" y="${Oy+14}" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="700">${l}</text>`);
['A\'','B\'','C\''].forEach((l,i)=>s+=`<text x="${70+i*80+4}" y="${[119,91,63][i]-4}" font-size="10" fill="#6d28d9" font-weight="700">${l}</text>`);
s+='</svg>';return s;}},
{desc:'<b>Шаг 2.</b> Через точки A, B, C проводим прямые, параллельные второму лучу. Получаем параллелограммы.',
svg:()=>{
// Same geometry as step 1. Corrected y values on ray 2.
// Ray 2 direction vector: (230,-80). Auxiliary lines from A,B parallel to ray 2:
// From A=(70,140) in dir (230,-80) scaled: Δx=80 → Δy=-80*80/230≈-28 → meets x=150 at y≈112
// From B=(150,140) in dir (230,-80): meets x=230 at y≈112
const W=260,H=160,Ox=20,Oy=140;
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
s+=`<line x1="${Ox}" y1="${Oy}" x2="250" y2="${Oy}" stroke="#7c3aed" stroke-width="2"/>`;
s+=`<line x1="${Ox}" y1="${Oy}" x2="250" y2="60" stroke="#7c3aed" stroke-width="2"/>`;
[[70,140,70,123],[150,140,150,95],[230,140,230,67]].forEach(([x1,y1,x2,y2])=>{
s+=`<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="#a78bfa" stroke-width="1.5" stroke-dasharray="4,3"/>`;
});
// auxiliary lines from A and B parallel to ray 2 (direction (230,-80))
s+=`<line x1="70" y1="140" x2="150" y2="112" stroke="#f59e0b" stroke-width="1.5" stroke-dasharray="3,3"/>`;
s+=`<line x1="150" y1="140" x2="230" y2="112" stroke="#f59e0b" stroke-width="1.5" stroke-dasharray="3,3"/>`;
s+=`<text x="100" y="131" font-size="9" fill="#b45309" font-weight="700">параллелограмм</text>`;
s+='</svg>';return s;}},
{desc:'<b>Шаг 3.</b> В параллелограмме противоположные стороны равны: $A_1B_1 = AB_2 = AB$, поэтому параллельная прямая откладывает равные отрезки на обоих лучах (если исходные части равны).',
svg:()=>{
// Parallelogram: A=(70,140), B=(150,140) on ray1; A'=(70,123), aux=(150,112) from auxiliary line
// This forms parallelogram A-B-aux-A' with A'B' (on ray2) as the top side
const W=260,H=160;
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
s+=`<polygon points="70,140 150,140 150,112 70,123" fill="rgba(124,58,237,.12)" stroke="#7c3aed" stroke-width="1.5"/>`;
// tick marks on parallel sides AB and A'B'' to show equality
s+=`<line x1="107" y1="140" x2="110" y2="134" stroke="#7c3aed" stroke-width="1.5"/>`;
s+=`<line x1="113" y1="140" x2="116" y2="134" stroke="#7c3aed" stroke-width="1.5"/>`;
s+=`<line x1="107" y1="118" x2="110" y2="112" stroke="#7c3aed" stroke-width="1.5"/>`;
s+=`<line x1="113" y1="118" x2="116" y2="112" stroke="#7c3aed" stroke-width="1.5"/>`;
s+=`<text x="110" y="110" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="700">Параллелограмм</text>`;
s+=`<text x="110" y="150" text-anchor="middle" font-size="9" fill="#6d28d9">AB = A\'B\' (пр. стор.)</text>`;
s+='</svg>';return s;}},
{desc:'<b>Шаг 4.</b> Если параллельные прямые делят луч на <em>равные</em> части, то и второй луч делится на равные части. Это и есть теорема Фалеса в классической форме.',
svg:()=>{
const W=260,H=160,Ox=20,Oy=140;
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
s+=`<line x1="${Ox}" y1="${Oy}" x2="250" y2="${Oy}" stroke="#7c3aed" stroke-width="2"/>`;
s+=`<line x1="${Ox}" y1="${Oy}" x2="250" y2="60" stroke="#7c3aed" stroke-width="2"/>`;
// corrected y values on ray2: y=140+(x-20)*(-80/230)
[[70,140,70,123],[150,140,150,95],[230,140,230,67]].forEach(([x1,y1,x2,y2])=>{
s+=`<line x1="${x1}" y1="${y1}" x2="${x2}" y2="${y2}" stroke="#a78bfa" stroke-width="1.5"/>`;
});
// равные отрезки — tick marks on ray1 segments
[[110,140],[190,140]].forEach(([x,y])=>{
s+=`<line x1="${x-4}" y1="${y-5}" x2="${x}" y2="${y+5}" stroke="#10b981" stroke-width="1.5"/>`;
});
// tick marks on ray2 segments
[[110,109],[190,81]].forEach(([x,y])=>{
s+=`<line x1="${x-4}" y1="${y-5}" x2="${x}" y2="${y+5}" stroke="#10b981" stroke-width="1.5"/>`;
});
s+=`<text x="110" y="${Oy+14}" text-anchor="middle" font-size="9" fill="#10b981" font-weight="700">AB=AB</text>`;
s+=`<text x="190" y="${Oy+14}" text-anchor="middle" font-size="9" fill="#10b981" font-weight="700">BC=BC</text>`;
s+='</svg>';return s;}},
{desc:'<b>Шаг 5.</b> Обобщённая теорема Фалеса: при <em>произвольных</em> параллельных прямых (не обязательно с равными частями) отрезки на двух лучах всё равно пропорциональны: $\\dfrac{AB}{BC}=\\dfrac{A\'B\'}{B\'C\'}$. <b>Доказано.</b>',
svg:()=>{
// Ray1: O=(20,140) horizontal to (250,140)
// Ray2: O=(20,140) to (250,50), slope=-90/230
// 2 parallel verticals at x=80 and x=170
// Ray2 intersections: x=80 → y=140-60*90/230≈117; x=170 → y=140-150*90/230≈81
const W=260,H=160;
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
s+=`<line x1="20" y1="140" x2="250" y2="140" stroke="#7c3aed" stroke-width="2"/>`;
s+=`<line x1="20" y1="140" x2="250" y2="50" stroke="#7c3aed" stroke-width="2"/>`;
// parallel transversals from ray1 to ray2 (not full vertical lines - just the segments)
s+=`<line x1="80" y1="140" x2="80" y2="117" stroke="#a78bfa" stroke-width="1.8" stroke-dasharray="4,3"/>`;
s+=`<line x1="170" y1="140" x2="170" y2="81" stroke="#a78bfa" stroke-width="1.8" stroke-dasharray="4,3"/>`;
// dot intersections on both rays
s+=`<circle cx="80" cy="140" r="3" fill="#7c3aed"/>`;
s+=`<circle cx="170" cy="140" r="3" fill="#7c3aed"/>`;
s+=`<circle cx="80" cy="117" r="3" fill="#7c3aed"/>`;
s+=`<circle cx="170" cy="81" r="3" fill="#7c3aed"/>`;
s+=`<text x="80" y="152" text-anchor="middle" font-size="9" fill="#6d28d9">A</text>`;
s+=`<text x="170" y="152" text-anchor="middle" font-size="9" fill="#6d28d9">B</text>`;
s+=`<text x="84" y="114" font-size="9" fill="#6d28d9">A'</text>`;
s+=`<text x="174" y="78" font-size="9" fill="#6d28d9">B'</text>`;
s+=`<text x="20" y="152" text-anchor="middle" font-size="9" fill="#6d28d9">O</text>`;
s+=`<text x="130" y="20" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="800">AB/A'B' = OA/OA'</text>`;
s+=`<text x="130" y="100" text-anchor="middle" font-size="9" fill="#10b981" font-weight="700">QED ∎</text>`;
s+='</svg>';return s;}},
];
let step=0;
const svgEl=document.getElementById('p1-proof-svg');
const descEl=document.getElementById('p1-proof-desc');
function show(){
svgEl.innerHTML=steps[step].svg();
descEl.innerHTML=steps[step].desc;
renderMath(descEl);
}
document.getElementById('p1-proof-next').addEventListener('click',()=>{
if(step<steps.length-1){step++;show();addXp(1,'p1-proof-step');}
else{addXp(5,'p1-proof-done');bumpProgress('p1',10);}
});
document.getElementById('p1-proof-reset').addEventListener('click',()=>{step=0;show();});
show();
})();
/* == INIT ИНТЕРАКТИВ 3: калькулятор пропорций == */
(function(){
document.getElementById('p1-pcalc').addEventListener('click',()=>{
const a=parseFloat(document.getElementById('p1-pa').value);
const b=parseFloat(document.getElementById('p1-pb').value);
const c=parseFloat(document.getElementById('p1-pc').value);
const out=document.getElementById('p1-pcalc-out');
if(!isFinite(a)||!isFinite(b)||!isFinite(c)||a<=0||b<=0||c<=0){
out.style.display='block';out.innerHTML='<span style="color:var(--bad)">Введи три положительных числа.</span>';return;
}
const x=b*c/a;
out.style.display='block';
out.innerHTML=`По теореме Фалеса: $x = \\dfrac{b \\cdot c}{a} = \\dfrac{${fmt(b)} \\cdot ${fmt(c)}}{${fmt(a)}} = ${fmt(x)}$`;
renderMath(out);
addXp(2,'p1-pcalc');bumpProgress('p1',5);
});
})();
/* == INIT ИНТЕРАКТИВ 4: тренажёр == */
(function(){
const tasks=[
{q:'Параллельные прямые отсекают на первом луче отрезки $AB=4$, $BC=6$. На втором луче $A\'B\'=6$. Найди $B\'C\'$.', ans:9, hint:'BC·A\'B\'/AB = 6·6/4 = 9.'},
{q:'$AB=3$, $BC=9$, $A\'B\'=5$. Найди $B\'C\'$.', ans:15, hint:'9·5/3 = 15.'},
{q:'Параллельные прямые отсекают $AB=8$, $BC=12$, $B\'C\'=9$. Найди $A\'B\'$.', ans:6, hint:'A\'B\' = AB·B\'C\'/BC = 8·9/12 = 6.'},
{q:'$AB=5$, $A\'B\'=7$, $B\'C\'=14$. Найди $BC$.', ans:10, hint:'BC = AB·B\'C\'/A\'B\' = 5·14/7 = 10.'},
{q:'Три параллельные прямые отсекают на луче отрезки $2$ и $3$. На другом луче первый отрезок равен $4$. Найди второй отрезок.', ans:6, hint:'4·3/2 = 6.'},
];
let idx=0,score=0;
function show(){
document.getElementById('p1-tr-i').textContent=idx+1;
const t=document.getElementById('p1-tr-task');
t.innerHTML=tasks[idx].q;
renderMath(t);
document.getElementById('p1-tr-ans').value='';
document.getElementById('p1-tr-fb').style.display='none';
}
document.getElementById('p1-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p1-tr-score').textContent=0;show();});
document.getElementById('p1-tr-go').addEventListener('click',()=>{
if(idx>=tasks.length)return;
const ans=+document.getElementById('p1-tr-ans').value;
const fb=document.getElementById('p1-tr-fb');
if(Math.abs(ans-tasks[idx].ans)<0.01){
score++;document.getElementById('p1-tr-score').textContent=score;
addXp(3,'p1-tr-'+idx);bumpProgress('p1',4);
if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}
else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p1-tr-all');bumpProgress('p1',10);confetti();}
} else {
feedback(fb,false,'Неверно. '+tasks[idx].hint);
}
});
document.getElementById('p1-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p1-tr-go').click();});
show();
})();
/* == INIT ИНТЕРАКТИВ 5: DnD-сортер == */
(function(){
const items=[
{text:'AB=3, BC=6, A\'B\'=4, B\'C\'=8',yes:true},
{text:'AB=2, BC=4, A\'B\'=3, B\'C\'=7',yes:false},
{text:'AB=5, BC=10, A\'B\'=1, B\'C\'=2',yes:true},
{text:'AB=6, BC=3, A\'B\'=8, B\'C\'=5',yes:false},
{text:'AB=4, BC=8, A\'B\'=3, B\'C\'=6',yes:true},
];
const pool=document.getElementById('p1-dnd-pool');
const yesBox=document.getElementById('p1-drop-yes-items');
const noBox=document.getElementById('p1-drop-no-items');
let dragging=null;
function makeChip(it,idx){
const chip=document.createElement('div');
chip.className='dnd-chip';
chip.dataset.idx=idx;
chip.textContent=it.text;
chip.draggable=true;
chip.addEventListener('dragstart',e=>{dragging=chip;chip.classList.add('dragging');e.dataTransfer.effectAllowed='move';});
chip.addEventListener('dragend',()=>{chip.classList.remove('dragging');dragging=null;});
return chip;
}
items.forEach((it,i)=>pool.appendChild(makeChip(it,i)));
[document.getElementById('p1-drop-yes'),document.getElementById('p1-drop-no'),pool].forEach(box=>{
box.addEventListener('dragover',e=>{e.preventDefault();box.classList.add('over');});
box.addEventListener('dragleave',()=>box.classList.remove('over'));
box.addEventListener('drop',e=>{
e.preventDefault();box.classList.remove('over');
if(!dragging)return;
const target=box===document.getElementById('p1-drop-yes')?yesBox:box===document.getElementById('p1-drop-no')?noBox:pool;
target.appendChild(dragging);
});
});
document.getElementById('p1-dnd-check').addEventListener('click',()=>{
const fb=document.getElementById('p1-dnd-fb');
const yChips=[...yesBox.querySelectorAll('.dnd-chip')];
const nChips=[...noBox.querySelectorAll('.dnd-chip')];
if(yChips.length+nChips.length<items.length){feedback(fb,false,'Разложи все карточки.');return;}
let ok=true;
yChips.forEach(c=>{if(!items[+c.dataset.idx].yes)ok=false;});
nChips.forEach(c=>{if(items[+c.dataset.idx].yes)ok=false;});
if(ok){feedback(fb,true,'Верно! +5 XP');addXp(5,'p1-dnd');bumpProgress('p1',8);}
else{feedback(fb,false,'Есть ошибки — перепроверь отношения AB/BC и A\'B\'/B\'C\'.');}
});
document.getElementById('p1-dnd-reset').addEventListener('click',()=>{
[...yesBox.children].forEach(c=>pool.appendChild(c));
[...noBox.children].forEach(c=>pool.appendChild(c));
document.getElementById('p1-dnd-fb').style.display='none';
});
})();
/* == INIT: Босс §1 == */
(function(){
const tasks=[
{q:'<svg viewBox="0 0 240 130" style="display:block;max-width:250px;margin:0 auto 8px;background:#f5f3ff;border:1px solid #ddd6fe;border-radius:8px"><line x1="20" y1="110" x2="220" y2="110" stroke="#7c3aed" stroke-width="2"/><line x1="20" y1="110" x2="220" y2="30" stroke="#7c3aed" stroke-width="2"/><line x1="80" y1="10" x2="80" y2="118" stroke="#a78bfa" stroke-width="1.5" stroke-dasharray="4,3"/><line x1="160" y1="10" x2="160" y2="118" stroke="#a78bfa" stroke-width="1.5" stroke-dasharray="4,3"/><text x="120" y="125" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="700">AB=10, BC=?, A\'B\'=6, B\'C\'=9</text></svg>$AB=10$, $A\'B\'=6$, $B\'C\'=9$. Найди $BC$.', ans:15, hint:'BC = AB·B\'C\'/A\'B\' = 10·9/6 = 15.'},
{q:'Параллельные прямые отсекают на двух лучах: луч 1 — отрезки $4$ и $x$, луч 2 — отрезки $6$ и $12$. Найди $x$.', ans:8, hint:'4/x = 6/12 → x = 4·12/6 = 8.'},
{q:'Прямая $DE \\parallel BC$ в треугольнике $ABC$. $AD=6$, $DB=9$, $AE=8$. Найди $EC$.', ans:12, hint:'AD/DB = AE/EC → EC = DB·AE/AD = 9·8/6 = 12.'},
{q:'$AB=15$, $BC=25$, $B\'C\'=20$. Найди $A\'B\'$.', ans:12, hint:'A\'B\' = AB·B\'C\'/BC = 15·20/25 = 12.'},
];
const bossBox=document.getElementById('p1-boss-tasks');
bossBox.innerHTML=tasks.map((t,i)=>`
<div style="padding:14px;background:var(--card);border-radius:10px;border:1px solid var(--border);margin-bottom:10px">
<div style="margin-bottom:8px;font-size:.95rem">${t.q}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
<input type="number" class="tinp" id="p1b-a${i}" placeholder="Ответ" style="width:110px">
<button class="btn primary small" onclick="(function(){
const v=+document.getElementById('p1b-a${i}').value;
const fb=document.getElementById('p1b-fb${i}');
if(Math.abs(v-${t.ans})<0.01){
feedback(fb,true,'Верно! +5 XP');
if(!window.p1BossSolved.has(${i})){ window.p1BossSolved.add(${i}); addXp(5,'p1-boss${i}'); bumpProgress('p1',8); }
} else feedback(fb,false,'Неверно. ${t.hint}');
})()">Проверить</button>
</div>
<div class="feedback" id="p1b-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p1BossSolved=new Set();
renderMath(bossBox);
})();
}
function buildP2(){
const box=document.getElementById('p2-body');
let html='';
html+=makeCard('theory','Деление отрезка в заданном отношении','2.1',`
<p><b>Определение.</b> Говорят, что точка $C$ делит отрезок $AB$ <em>в отношении $m:n$</em> (<em>внутреннее деление</em>), если $C$ лежит между $A$ и $B$, и:</p>
$$\\dfrac{AC}{CB} = \\dfrac{m}{n}, \\quad m,n > 0$$
<p style="margin-top:8px"><b>Формула координат.</b> Если $A = (x_A, y_A)$, $B = (x_B, y_B)$, то точка деления:</p>
$$C = \\left(\\dfrac{n\\,x_A + m\\,x_B}{m+n},\\; \\dfrac{n\\,y_A + m\\,y_B}{m+n}\\right)$$
<p style="margin-top:8px"><b>Частный случай $m=n$:</b> точка $C$ — середина $AB$.</p>
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 280 80" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- отрезок AB с точкой C -->
<line x1="20" y1="40" x2="260" y2="40" stroke="#7c3aed" stroke-width="2.5"/>
<circle cx="20" cy="40" r="4" fill="#7c3aed"/>
<circle cx="260" cy="40" r="4" fill="#7c3aed"/>
<!-- C делит в 2:3: x = 20 + 2/(2+3)*240 = 20+96 = 116 -->
<circle cx="116" cy="40" r="5" fill="#f59e0b"/>
<!-- скобки -->
<line x1="20" y1="52" x2="116" y2="52" stroke="#10b981" stroke-width="1.5"/>
<line x1="116" y1="52" x2="260" y2="52" stroke="#8b5cf6" stroke-width="1.5"/>
<text x="68" y="65" text-anchor="middle" font-size="11" fill="#10b981" font-weight="700">m = 2</text>
<text x="188" y="65" text-anchor="middle" font-size="11" fill="#8b5cf6" font-weight="700">n = 3</text>
<text x="20" y="32" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700">A</text>
<text x="116" y="32" text-anchor="middle" font-size="11" fill="#b45309" font-weight="700">C</text>
<text x="260" y="32" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700">B</text>
</svg>
</div>`);
html+=makeCard('algo','Построение циркулем и линейкой','2.2',`
<p>Чтобы разделить отрезок $AB$ в отношении $m:n$:</p>
<ol style="margin-left:18px;margin-top:6px;line-height:2">
<li>Из точки $A$ проводим луч $AK$, не совпадающий с $AB$.</li>
<li>На луче $AK$ последовательно откладываем $m+n$ равных отрезков единичной длины: точки $P_1, P_2, \\ldots, P_{m+n}$.</li>
<li>Соединяем точку $P_{m+n}$ с точкой $B$.</li>
<li>Через $P_m$ проводим прямую, параллельную $P_{m+n}B$ — она пересекает $AB$ в нужной точке $C$.</li>
</ol>
<p style="margin-top:8px">По теореме Фалеса $\\dfrac{AC}{CB} = \\dfrac{m}{n}$. <b>ч.т.д.</b></p>
<div style="display:flex;justify-content:center;margin-top:12px">
<svg viewBox="0 0 280 160" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- отрезок AB -->
<line x1="20" y1="60" x2="260" y2="60" stroke="#7c3aed" stroke-width="2"/>
<circle cx="20" cy="60" r="4" fill="#7c3aed"/>
<circle cx="260" cy="60" r="4" fill="#7c3aed"/>
<text x="16" y="52" font-size="10" fill="#6d28d9" font-weight="700">A</text>
<text x="256" y="52" font-size="10" fill="#6d28d9" font-weight="700">B</text>
<!-- луч AK: из A=(20,60) под углом вниз -->
<line x1="20" y1="60" x2="170" y2="140" stroke="#f59e0b" stroke-width="1.5"/>
<!-- 5 точек (m+n=2+3) на луче, шаг=30px по лучу -->
<!-- P1=(50,76), P2=(80,92), P3=(110,108), P4=(140,124), P5=(170,140) -->
<circle cx="50" cy="76" r="3" fill="#f59e0b"/>
<circle cx="80" cy="92" r="3" fill="#f59e0b"/>
<circle cx="110" cy="108" r="3" fill="#10b981"/>
<circle cx="140" cy="124" r="3" fill="#f59e0b"/>
<circle cx="170" cy="140" r="3" fill="#f59e0b"/>
<text x="50" y="72" text-anchor="middle" font-size="8" fill="#b45309">P₁</text>
<text x="80" y="88" text-anchor="middle" font-size="8" fill="#b45309">P₂</text>
<text x="110" y="104" text-anchor="middle" font-size="8" fill="#047857" font-weight="700">P_m</text>
<text x="165" y="155" text-anchor="middle" font-size="8" fill="#b45309">P₅</text>
<!-- линия P5B -->
<line x1="170" y1="140" x2="260" y2="60" stroke="#8b5cf6" stroke-width="1.2" stroke-dasharray="4,3"/>
<!-- параллельная через Pm: C на AB -->
<!-- C: x = 20 + 2/5*(260-20) = 20+96 = 116 -->
<line x1="110" y1="108" x2="116" y2="60" stroke="#10b981" stroke-width="1.8" stroke-dasharray="4,3"/>
<circle cx="116" cy="60" r="5" fill="#10b981"/>
<text x="116" y="52" text-anchor="middle" font-size="10" fill="#047857" font-weight="700">C</text>
</svg>
</div>`);
html+=makeCard('example','Пример','2.3',`
<p><b>Пример 1.</b> $A=(0,0)$, $B=(10,0)$, разделить в отношении $3:2$.</p>
<p>$C_x = \\dfrac{2\\cdot 0 + 3\\cdot 10}{3+2} = \\dfrac{30}{5} = 6$. Ответ: $C=(6,0)$.</p>
<p style="margin-top:8px"><b>Пример 2.</b> $A=(1,2)$, $B=(7,8)$, разделить в отношении $1:2$.</p>
<p>$C_x = \\dfrac{2\\cdot 1+1\\cdot 7}{3}=3$, $C_y=\\dfrac{2\\cdot 2+1\\cdot 8}{3}=4$. Ответ: $C=(3,4)$.</p>`);
/* ИНТЕРАКТИВ 1 — SVG-построение */
html+=`<div class="wg" id="p2-build-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Построение деления отрезка m:n</div></div>
<div class="wg-help">Меняй m и n — смотри, как перемещается точка C на отрезке AB.</div>
<div class="sliders">
<label>m = <b id="p2-m-val">2</b>
<input type="range" min="1" max="6" value="2" id="p2-m-sl">
</label>
<label>n = <b id="p2-n-val">3</b>
<input type="range" min="1" max="6" value="3" id="p2-n-sl">
</label>
</div>
<div id="p2-build-svg" style="display:flex;justify-content:center"></div>
<div id="p2-build-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.93rem;line-height:1.8"></div>
</div>`;
/* ИНТЕРАКТИВ 2 — Калькулятор */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор деления отрезка</div></div>
<div class="wg-help">Введи длину отрезка и отношение m:n — получи расстояние AC от начала.</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
<div style="display:flex;flex-direction:column;gap:4px">
<span style="font-size:.8rem;color:var(--muted)">AB</span>
<input type="number" id="p2-lab" class="tinp" placeholder="AB" style="width:80px" min="0.001">
</div>
<div style="display:flex;flex-direction:column;gap:4px">
<span style="font-size:.8rem;color:var(--muted)">m</span>
<input type="number" id="p2-lm" class="tinp" placeholder="m" style="width:60px" min="1">
</div>
<div style="display:flex;flex-direction:column;gap:4px">
<span style="font-size:.8rem;color:var(--muted)">n</span>
<input type="number" id="p2-ln" class="tinp" placeholder="n" style="width:60px" min="1">
</div>
<button class="btn primary" id="p2-lcalc">Вычислить</button>
</div>
<div id="p2-lcalc-out" style="display:none;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem"></div>
</div>`;
/* ИНТЕРАКТИВ 3 — Пошаговое доказательство */
html+=`<div class="wg" id="p2-proof-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Доказательство формулы — по шагам</div></div>
<div class="wg-help">4 шага — вывод формулы координат точки деления через теорему Фалеса.</div>
<div id="p2-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p2-proof-desc" style="padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.6;margin-bottom:10px;min-height:60px"></div>
<div style="display:flex;gap:8px">
<button class="btn primary" id="p2-proof-next">Далее</button>
<button class="btn" id="p2-proof-reset">Сначала</button>
</div>
</div>`;
/* ИНТЕРАКТИВ 4 — Тренажёр */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр §2</div></div>
<div class="wg-help">5 задач на деление отрезка. Введи числовой ответ.</div>
<div class="score-display"><span>Задача <b id="p2-tr-i">1</b> / 5</span><span>Очки: <b id="p2-tr-score">0</b></span></div>
<div id="p2-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.02rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p2-tr-ans" class="tinp" placeholder="Ответ" style="width:120px">
<button class="btn primary" id="p2-tr-go">Проверить</button>
<button class="btn" id="p2-tr-start">Начать</button>
</div>
<div class="feedback" id="p2-tr-fb" style="display:none"></div>
</div>`;
/* ИНТЕРАКТИВ 5 — Босс §2 */
html+=`<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §2</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">4 задачи — каждая верная даёт +5 XP.</div>
<div id="p2-boss-tasks"></div>
</div>`;
html+=`<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p2-read-btn" onclick="addXp(10,'p2-read');bumpProgress('p2',40);this.textContent='Прочитано!';this.disabled=true;">
<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>
Я прочитал §2 (+10 XP)
</button>
</div>`;
html+=secNav('p1','p3');
box.innerHTML=html;
if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
/* == INIT ИНТЕРАКТИВ 1: построение == */
(function(){
const mSl=document.getElementById('p2-m-sl');
const nSl=document.getElementById('p2-n-sl');
const mVal=document.getElementById('p2-m-val');
const nVal=document.getElementById('p2-n-val');
const svgWrap=document.getElementById('p2-build-svg');
const infoEl=document.getElementById('p2-build-info');
function draw(){
const m=+mSl.value, n=+nSl.value;
mVal.textContent=m; nVal.textContent=n;
const W=320, H=140;
const Ax=24, Ay=50, Bx=296, By=50;
const AB=Bx-Ax;
const Cx=Ax+m/(m+n)*AB;
const Cy=50;
// луч AK: вниз под углом 30°
const rayLen=(m+n)*22;
const rayAngle=Math.PI/6;
const Kx=Ax+rayLen*Math.cos(rayAngle);
const Ky=Ay+rayLen*Math.sin(rayAngle);
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
// отрезок AB
s+=`<line x1="${Ax}" y1="${Ay}" x2="${Bx}" y2="${By}" stroke="#7c3aed" stroke-width="2.5"/>`;
s+=`<circle cx="${Ax}" cy="${Ay}" r="4" fill="#7c3aed"/>`;
s+=`<circle cx="${Bx}" cy="${By}" r="4" fill="#7c3aed"/>`;
s+=`<text x="${Ax}" y="${Ay-8}" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700">A</text>`;
s+=`<text x="${Bx}" y="${By-8}" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700">B</text>`;
// луч AK
s+=`<line x1="${Ax}" y1="${Ay}" x2="${Kx}" y2="${Ky}" stroke="#f59e0b" stroke-width="1.5"/>`;
// точки P1..P(m+n) на луче
for(let i=1;i<=m+n;i++){
const px=Ax+i*22*Math.cos(rayAngle);
const py=Ay+i*22*Math.sin(rayAngle);
const isM=(i===m);
s+=`<circle cx="${px}" cy="${py}" r="${isM?5:3}" fill="${isM?'#10b981':'#f59e0b'}"/>`;
s+=`<text x="${px+6}" y="${py+4}" font-size="8" fill="${isM?'#047857':'#b45309'}" font-weight="${isM?'700':'400'}">P${i}${isM?' (m)':''}</text>`;
}
// линия P(m+n)→B
const Pmn_x=Ax+(m+n)*22*Math.cos(rayAngle);
const Pmn_y=Ay+(m+n)*22*Math.sin(rayAngle);
s+=`<line x1="${Pmn_x}" y1="${Pmn_y}" x2="${Bx}" y2="${By}" stroke="#8b5cf6" stroke-width="1.3" stroke-dasharray="4,3"/>`;
// параллельная через Pm → C
const Pm_x=Ax+m*22*Math.cos(rayAngle);
const Pm_y=Ay+m*22*Math.sin(rayAngle);
s+=`<line x1="${Pm_x}" y1="${Pm_y}" x2="${Cx}" y2="${Cy}" stroke="#10b981" stroke-width="2" stroke-dasharray="4,3"/>`;
// точка C
s+=`<circle cx="${Cx}" cy="${Cy}" r="6" fill="#10b981"/>`;
s+=`<text x="${Cx}" y="${Cy-10}" text-anchor="middle" font-size="11" fill="#047857" font-weight="700">C</text>`;
// метки AC, CB
s+=`<line x1="${Ax}" y1="${Ay+14}" x2="${Cx}" y2="${Cy+14}" stroke="#10b981" stroke-width="1.5"/>`;
s+=`<line x1="${Cx}" y1="${Cy+14}" x2="${Bx}" y2="${By+14}" stroke="#8b5cf6" stroke-width="1.5"/>`;
s+=`<text x="${(Ax+Cx)/2}" y="${Cy+26}" text-anchor="middle" font-size="10" fill="#047857" font-weight="700">m=${m}</text>`;
s+=`<text x="${(Cx+Bx)/2}" y="${Cy+26}" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="700">n=${n}</text>`;
s+='</svg>';
svgWrap.innerHTML=s;
const AClen=fmt(m/(m+n)*100);
const CBlen=fmt(n/(m+n)*100);
infoEl.innerHTML=`Отношение $AC:CB = ${m}:${n}$. Если $AB=100$, то $AC=${AClen}$, $CB=${CBlen}$.`;
renderMath(infoEl);
addXp(1,'p2-build');
}
mSl.addEventListener('input',draw);
nSl.addEventListener('input',draw);
draw();
})();
/* == INIT ИНТЕРАКТИВ 2: калькулятор == */
(function(){
document.getElementById('p2-lcalc').addEventListener('click',()=>{
const AB=parseFloat(document.getElementById('p2-lab').value);
const m=parseFloat(document.getElementById('p2-lm').value);
const n=parseFloat(document.getElementById('p2-ln').value);
const out=document.getElementById('p2-lcalc-out');
if(!isFinite(AB)||!isFinite(m)||!isFinite(n)||AB<=0||m<=0||n<=0){
out.style.display='block';out.innerHTML='<span style="color:var(--bad)">Введи положительные числа.</span>';return;
}
const AC=m/(m+n)*AB;
const CB=n/(m+n)*AB;
out.style.display='block';
out.innerHTML=`$AC = \\dfrac{m}{m+n}\\cdot AB = \\dfrac{${fmt(m)}}{${fmt(m+n)}}\\cdot ${fmt(AB)} = ${fmt(AC)}$<br>$CB = \\dfrac{${fmt(n)}}{${fmt(m+n)}}\\cdot ${fmt(AB)} = ${fmt(CB)}$`;
renderMath(out);
addXp(2,'p2-calc');bumpProgress('p2',5);
});
})();
/* == INIT ИНТЕРАКТИВ 3: доказательство == */
(function(){
const steps=[
{desc:'<b>Шаг 1.</b> Даны точки $A$ и $B$ на прямой. Хотим найти точку $C$ такую, что $AC:CB = m:n$.',
svg:`<svg viewBox="0 0 280 70" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><line x1="20" y1="35" x2="260" y2="35" stroke="#7c3aed" stroke-width="2"/><circle cx="20" cy="35" r="4" fill="#7c3aed"/><circle cx="260" cy="35" r="4" fill="#7c3aed"/><text x="20" y="27" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700">A</text><text x="260" y="27" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700">B</text><text x="140" y="55" text-anchor="middle" font-size="10" fill="#6d28d9">AC : CB = m : n = ?</text></svg>`},
{desc:'<b>Шаг 2.</b> Проводим из $A$ луч $AK$ и откладываем $m+n$ равных единичных отрезков. Последняя точка — $P_{m+n}$, средняя (после $m$ шагов) — $P_m$.',
svg:`<svg viewBox="0 0 280 130" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><line x1="20" y1="40" x2="260" y2="40" stroke="#7c3aed" stroke-width="2"/><line x1="20" y1="40" x2="140" y2="120" stroke="#f59e0b" stroke-width="1.5"/><circle cx="44" cy="57" r="3" fill="#f59e0b"/><circle cx="68" cy="73" r="3" fill="#f59e0b"/><circle cx="92" cy="90" r="5" fill="#10b981"/><circle cx="116" cy="106" r="3" fill="#f59e0b"/><circle cx="140" cy="120" r="3" fill="#f59e0b"/><text x="96" y="88" font-size="8" fill="#047857" font-weight="700">P_m</text><text x="142" y="118" font-size="8" fill="#b45309">P_{m+n}</text><text x="20" y="32" font-size="10" fill="#6d28d9" font-weight="700">A</text><text x="256" y="32" font-size="10" fill="#6d28d9" font-weight="700">B</text></svg>`},
{desc:'<b>Шаг 3.</b> Соединяем $P_{m+n}$ с $B$. Через $P_m$ проводим прямую, параллельную $P_{m+n}B$. По теореме Фалеса она пересекает $AB$ в точке $C$, делящей $AB$ в отношении $m:n$.',
svg:`<svg viewBox="0 0 280 130" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><line x1="20" y1="40" x2="260" y2="40" stroke="#7c3aed" stroke-width="2"/><line x1="20" y1="40" x2="140" y2="120" stroke="#f59e0b" stroke-width="1.5"/><line x1="140" y1="120" x2="260" y2="40" stroke="#8b5cf6" stroke-width="1.3" stroke-dasharray="4,3"/><line x1="92" y1="90" x2="116" y2="40" stroke="#10b981" stroke-width="2" stroke-dasharray="4,3"/><circle cx="116" cy="40" r="6" fill="#10b981"/><text x="116" y="32" text-anchor="middle" font-size="11" fill="#047857" font-weight="700">C</text><circle cx="92" cy="90" r="4" fill="#10b981"/><circle cx="140" cy="120" r="4" fill="#f59e0b"/><text x="92" y="88" font-size="8" fill="#047857">P_m</text><text x="20" y="32" font-size="10" fill="#6d28d9" font-weight="700">A</text><text x="256" y="32" font-size="10" fill="#6d28d9" font-weight="700">B</text></svg>`},
{desc:'<b>Шаг 4.</b> Итог: по теореме Фалеса $\\dfrac{AC}{CB}=\\dfrac{m}{n}$. Координаты точки $C$: $C_x = \\dfrac{n\\,x_A+m\\,x_B}{m+n}$, $C_y = \\dfrac{n\\,y_A+m\\,y_B}{m+n}$. <b>Доказано.</b>',
svg:`<svg viewBox="0 0 280 70" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><line x1="20" y1="35" x2="260" y2="35" stroke="#7c3aed" stroke-width="2"/><circle cx="20" cy="35" r="4" fill="#7c3aed"/><circle cx="260" cy="35" r="4" fill="#7c3aed"/><circle cx="116" cy="35" r="6" fill="#10b981"/><text x="20" y="27" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700">A</text><text x="116" y="27" text-anchor="middle" font-size="11" fill="#047857" font-weight="700">C</text><text x="260" y="27" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700">B</text><text x="140" y="58" text-anchor="middle" font-size="10" fill="#047857" font-weight="700">C = (n·A + m·B) / (m+n)</text></svg>`},
];
let step=0;
const svgEl=document.getElementById('p2-proof-svg');
const descEl=document.getElementById('p2-proof-desc');
function show(){
svgEl.innerHTML=steps[step].svg;
descEl.innerHTML=steps[step].desc;
renderMath(descEl);
}
document.getElementById('p2-proof-next').addEventListener('click',()=>{
if(step<steps.length-1){step++;show();addXp(1,'p2-proof-step');}
else{addXp(5,'p2-proof-done');bumpProgress('p2',10);}
});
document.getElementById('p2-proof-reset').addEventListener('click',()=>{step=0;show();});
show();
})();
/* == INIT ИНТЕРАКТИВ 4: тренажёр == */
(function(){
const tasks=[
{q:'Отрезок $AB=20$ делится точкой $C$ в отношении $1:4$. Найди $AC$.', ans:4, hint:'AC = 1/(1+4)·20 = 4.'},
{q:'$AB=15$, $AC:CB=2:3$. Найди $CB$.', ans:9, hint:'CB = 3/(2+3)·15 = 9.'},
{q:'$A=(0,0)$, $B=(8,0)$, деление $3:1$. Найди $x$-координату точки $C$.', ans:6, hint:'C_x = (1·0+3·8)/4 = 6.'},
{q:'Отрезок $AB=30$ делится в отношении $2:1$. Найди расстояние от $C$ до $B$.', ans:10, hint:'CB = 1/(2+1)·30 = 10.'},
{q:'$A=(0,0)$, $B=(0,12)$, деление $1:2$. Найди $y$-координату точки $C$.', ans:4, hint:'C_y = (2·0+1·12)/3 = 4.'},
];
let idx=0,score=0;
function show(){
document.getElementById('p2-tr-i').textContent=idx+1;
const t=document.getElementById('p2-tr-task');
t.innerHTML=tasks[idx].q;
renderMath(t);
document.getElementById('p2-tr-ans').value='';
document.getElementById('p2-tr-fb').style.display='none';
}
document.getElementById('p2-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p2-tr-score').textContent=0;show();});
document.getElementById('p2-tr-go').addEventListener('click',()=>{
if(idx>=tasks.length)return;
const ans=+document.getElementById('p2-tr-ans').value;
const fb=document.getElementById('p2-tr-fb');
if(Math.abs(ans-tasks[idx].ans)<0.01){
score++;document.getElementById('p2-tr-score').textContent=score;
addXp(3,'p2-tr-'+idx);bumpProgress('p2',4);
if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}
else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p2-tr-all');bumpProgress('p2',10);confetti();}
} else {
feedback(fb,false,'Неверно. '+tasks[idx].hint);
}
});
document.getElementById('p2-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p2-tr-go').click();});
show();
})();
/* == INIT: Босс §2 == */
(function(){
const tasks=[
{q:'$AB=24$, деление в отношении $5:3$. Найди $AC$.', ans:15, hint:'AC = 5/8·24 = 15.'},
{q:'$A=(0,0)$, $B=(10,10)$, деление $3:2$. Найди $x_C+y_C$.', ans:12, hint:'C_x=6, C_y=6, сумма=12.'},
{q:'Точка $C$ делит $AB$ в отношении $AC:CB=4:1$. $CB=7$. Найди $AB$.', ans:35, hint:'AB = (4+1)·7 = 35.'},
{q:'$AB=100$, $C$ делит $AB$ в отношении $3:7$. Найди $BC$.', ans:70, hint:'BC = 7/10·100 = 70.'},
];
const bossBox=document.getElementById('p2-boss-tasks');
bossBox.innerHTML=tasks.map((t,i)=>`
<div style="padding:14px;background:var(--card);border-radius:10px;border:1px solid var(--border);margin-bottom:10px">
<div style="margin-bottom:8px;font-size:.95rem">${t.q}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
<input type="number" class="tinp" id="p2b-a${i}" placeholder="Ответ" style="width:110px">
<button class="btn primary small" onclick="(function(){
const v=+document.getElementById('p2b-a${i}').value;
const fb=document.getElementById('p2b-fb${i}');
if(Math.abs(v-${t.ans})<0.01){
feedback(fb,true,'Верно! +5 XP');
if(!window.p2BossSolved.has(${i})){ window.p2BossSolved.add(${i}); addXp(5,'p2-boss${i}'); bumpProgress('p2',8); }
} else feedback(fb,false,'Неверно. ${t.hint}');
})()">Проверить</button>
</div>
<div class="feedback" id="p2b-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p2BossSolved=new Set();
renderMath(bossBox);
})();
}
function buildP3(){
const box=document.getElementById('p3-body');
let html='';
html+=makeCard('theory','Определение подобных треугольников','3.1',`
<p><b>Определение.</b> Треугольники $\\triangle ABC$ и $\\triangle A'B'C'$ называются <b>подобными</b> ($\\triangle ABC \\sim \\triangle A'B'C'$), если:</p>
<ol style="margin-left:18px;margin-top:6px;line-height:2">
<li>Соответственные углы равны: $\\angle A = \\angle A'$, $\\angle B = \\angle B'$, $\\angle C = \\angle C'$.</li>
<li>Соответственные стороны пропорциональны: $\\dfrac{a}{a'} = \\dfrac{b}{b'} = \\dfrac{c}{c'} = k$,</li>
</ol>
<p style="margin-top:6px">где $k > 0$ — <b>коэффициент подобия</b>.</p>
<p style="margin-top:8px"><b>Обозначения:</b> стороны треугольника: $a=BC$, $b=CA$, $c=AB$ и $a'=B'C'$, $b'=C'A'$, $c'=A'B'$.</p>
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 360 155" style="max-width:380px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- большой треугольник ABC: A=(80,25), B=(20,135), C=(195,135) -->
<!-- BC=175, AB≈sqrt(60²+110²)≈125, AC≈sqrt(115²+110²)≈160 -->
<polygon points="80,25 20,135 195,135" fill="rgba(124,58,237,.12)" stroke="#7c3aed" stroke-width="2"/>
<text x="80" y="17" text-anchor="middle" font-size="11" font-weight="700" fill="#6d28d9">A</text>
<text x="8" y="148" font-size="11" font-weight="700" fill="#6d28d9">B</text>
<text x="198" y="148" font-size="11" font-weight="700" fill="#6d28d9">C</text>
<!-- малый треугольник A'B'C' (k=2.5): вправо от большого, нет перекрытий -->
<!-- B'=(245,135), C'=(315,135), A'=(B'+35,135-88)=(280,47) -->
<!-- B'C'=70=175/2.5✓, |A'B'|=sqrt(35²+88²)≈95/2.5=50≈50✓, |A'C'|=sqrt(35²+88²)≈64≈160/2.5=64✓ -->
<polygon points="280,47 245,135 315,135" fill="rgba(139,92,246,.15)" stroke="#8b5cf6" stroke-width="1.8"/>
<text x="280" y="39" text-anchor="middle" font-size="10" font-weight="700" fill="#8b5cf6">A'</text>
<text x="233" y="148" font-size="10" font-weight="700" fill="#8b5cf6">B'</text>
<text x="318" y="148" font-size="10" font-weight="700" fill="#8b5cf6">C'</text>
<!-- коэффициент подобия -->
<text x="178" y="14" text-anchor="middle" font-size="9" fill="#6d28d9">k≈2.5: a/a'=b/b'=c/c'</text>
<!-- равенство углов -->
<text x="28" y="128" font-size="8" fill="#6d28d9">∠B</text>
<text x="253" y="128" font-size="8" fill="#8b5cf6">∠B'</text>
</svg>
</div>`);
html+=makeCard('rule','Коэффициент подобия и свойства','3.2',`
<p><b>Коэффициент подобия</b> $k = \\dfrac{a}{a'} = \\dfrac{b}{b'} = \\dfrac{c}{c'}$ показывает, во сколько раз стороны одного треугольника больше соответственных сторон другого.</p>
<p style="margin-top:8px"><b>Свойства подобия (отношение эквивалентности):</b></p>
<ul style="margin-left:18px;margin-top:6px;line-height:1.9">
<li><b>Рефлексивность:</b> $\\triangle ABC \\sim \\triangle ABC$ (с $k=1$).</li>
<li><b>Симметричность:</b> если $\\triangle ABC \\sim \\triangle A'B'C'$ с коэффициентом $k$, то $\\triangle A'B'C' \\sim \\triangle ABC$ с коэффициентом $\\tfrac{1}{k}$.</li>
<li><b>Транзитивность:</b> если $\\triangle ABC \\sim \\triangle A'B'C'$ и $\\triangle A'B'C' \\sim \\triangle A''B''C''$, то $\\triangle ABC \\sim \\triangle A''B''C''$.</li>
</ul>`);
html+=makeCard('example','Пример','3.3',`
<p><b>Пример.</b> $\\triangle ABC$: стороны $6, 8, 10$. $\\triangle A'B'C'$: стороны $3, 4, 5$. Подобны ли треугольники?</p>
<p style="margin-top:6px">$\\dfrac{6}{3}=2,\\; \\dfrac{8}{4}=2,\\; \\dfrac{10}{5}=2$ — отношения равны. Значит, $\\triangle ABC \\sim \\triangle A'B'C'$ с коэффициентом $k=2$.</p>
<p style="margin-top:8px"><b>Пример 2.</b> Треугольники $5,7,9$ и $10,15,18$ — подобны ли?</p>
<p>$10/5=2$, $15/7\\approx 2.14$ — отношения неравны. <b>Не подобны.</b></p>`);
/* ИНТЕРАКТИВ 1 — SVG два треугольника с slider k */
html+=`<div class="wg" id="p3-sim-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Два подобных треугольника — коэффициент k</div></div>
<div class="wg-help">Меняй коэффициент подобия $k$ — второй треугольник масштабируется. Углы всегда равны, стороны пропорциональны.</div>
<div class="sliders">
<label>Коэффициент k = <b id="p3-k-val">1.5</b>
<input type="range" min="5" max="30" value="15" id="p3-k-sl" step="1">
</label>
</div>
<div id="p3-sim-svg" style="display:flex;justify-content:center"></div>
<div id="p3-sim-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.92rem;line-height:1.8"></div>
</div>`;
/* ИНТЕРАКТИВ 2 — Калькулятор */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор подобия</div></div>
<div class="wg-help">Введи стороны одного треугольника и коэффициент $k$ — получи стороны подобного.</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(80px,1fr));gap:8px;margin-bottom:10px">
<div><span style="font-size:.8rem;color:var(--muted);display:block">a</span><input type="number" id="p3-ca" class="tinp" placeholder="a" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">b</span><input type="number" id="p3-cb" class="tinp" placeholder="b" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">c</span><input type="number" id="p3-cc" class="tinp" placeholder="c" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">k</span><input type="number" id="p3-ck" class="tinp" placeholder="k" style="width:100%" min="0.01"></div>
<div style="display:flex;align-items:flex-end"><button class="btn primary" id="p3-ccalc" style="width:100%">Вычислить</button></div>
</div>
<div id="p3-ccalc-out" style="display:none;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem"></div>
</div>`;
/* ИНТЕРАКТИВ 3 — DnD-сортер подобные / неподобные */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Подобные или нет? — Сортировка</div></div>
<div class="wg-help">Перетащи каждую пару треугольников в нужную колонку.</div>
<div id="p3-dnd-pool" class="dnd-pool"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:10px">
<div class="drop-box" id="p3-drop-yes"><h5>Подобные</h5><div class="drop-items" id="p3-drop-yes-items"></div></div>
<div class="drop-box" id="p3-drop-no"><h5>Не подобные</h5><div class="drop-items" id="p3-drop-no-items"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p3-dnd-check">Проверить</button><button class="btn" id="p3-dnd-reset">Сбросить</button></div>
<div class="feedback" id="p3-dnd-fb" style="display:none"></div>
</div>`;
/* ИНТЕРАКТИВ 4 — Тренажёр */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр §3 — Подобные треугольники</div></div>
<div class="wg-help">5 задач на коэффициент подобия и неизвестные стороны.</div>
<div class="score-display"><span>Задача <b id="p3-tr-i">1</b> / 5</span><span>Очки: <b id="p3-tr-score">0</b></span></div>
<div id="p3-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.02rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p3-tr-ans" class="tinp" placeholder="Ответ" style="width:120px">
<button class="btn primary" id="p3-tr-go">Проверить</button>
<button class="btn" id="p3-tr-start">Начать</button>
</div>
<div class="feedback" id="p3-tr-fb" style="display:none"></div>
</div>`;
/* ИНТЕРАКТИВ 5 — Мини-квиз */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Мини-квиз: теория подобия</div></div>
<div class="wg-help">4 вопроса на проверку понимания теории.</div>
<div id="p3-quiz-body"></div>
<div class="actions"><button class="btn primary" id="p3-quiz-check">Проверить квиз</button></div>
<div class="feedback" id="p3-quiz-fb" style="display:none;margin-top:8px"></div>
</div>`;
/* ИНТЕРАКТИВ 6 — Босс §3 */
html+=`<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §3</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">4 задачи — каждая верная даёт +5 XP.</div>
<div id="p3-boss-tasks"></div>
</div>`;
html+=`<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p3-read-btn" onclick="addXp(10,'p3-read');bumpProgress('p3',40);this.textContent='Прочитано!';this.disabled=true;">
<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>
Я прочитал §3 (+10 XP)
</button>
</div>`;
html+=secNav('p2','p4');
box.innerHTML=html;
if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
/* == INIT ИНТЕРАКТИВ 1: два треугольника == */
(function(){
const kSl=document.getElementById('p3-k-sl');
const kVal=document.getElementById('p3-k-val');
const svgWrap=document.getElementById('p3-sim-svg');
const infoEl=document.getElementById('p3-sim-info');
// базовый треугольник T1: B=(20,120), C=(160,120), A=(80,20)
const Ax=80,Ay=20,Bx=20,By=120,Cx=160,Cy=120;
const a=Math.hypot(Cx-Bx,Cy-By); // BC
const b=Math.hypot(Ax-Cx,Ay-Cy); // CA
const c=Math.hypot(Bx-Ax,By-Ay); // AB
function draw(){
const k=+kSl.value/10;
kVal.textContent=k.toFixed(1);
// второй треугольник — те же пропорции, масштаб 1/k
// T2 anchor: B2x = Cx + 50px gap, base2 = (Cx-Bx)/k
const base1=Cx-Bx; // 140
const base2=base1/k;
const B2x=Cx+50, B2y=120;
const C2x=B2x+base2, C2y=120;
// A2: same relative shape — horizontal offset of A from B in T1 = (Ax-Bx)/base1
const A2x=B2x+(Ax-Bx)*base2/base1;
const A2y=B2y+(Ay-By)*base2/base1;
const W=Math.max(380, Math.ceil(C2x+30));
const H=160;
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
// k-label at top centre
s+=`<text x="${W/2}" y="14" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="800">k = ${k.toFixed(1)}</text>`;
// большой треугольник T1
s+=`<polygon points="${Ax},${Ay} ${Bx},${By} ${Cx},${Cy}" fill="rgba(124,58,237,.12)" stroke="#7c3aed" stroke-width="2"/>`;
s+=`<text x="${Ax}" y="${Ay-6}" text-anchor="middle" font-size="11" fill="#6d28d9" font-weight="700">A</text>`;
s+=`<text x="${Bx-12}" y="${By+4}" font-size="11" fill="#6d28d9" font-weight="700">B</text>`;
s+=`<text x="${Cx+4}" y="${Cy+4}" font-size="11" fill="#6d28d9" font-weight="700">C</text>`;
// подписи сторон T1 — OUTSIDE
s+=`<text x="${(Bx+Cx)/2}" y="${By+16}" text-anchor="middle" font-size="9" fill="#6d28d9">a=${fmt(a/10)}</text>`;
s+=`<text x="${(Ax+Bx)/2-12}" y="${(Ay+By)/2}" text-anchor="end" font-size="9" fill="#6d28d9">c=${fmt(c/10)}</text>`;
s+=`<text x="${(Ax+Cx)/2+8}" y="${(Ay+Cy)/2}" font-size="9" fill="#6d28d9">b=${fmt(b/10)}</text>`;
// второй треугольник T2 — separated to the right
const a2=a/k,b2=b/k,c2=c/k;
s+=`<polygon points="${A2x},${A2y} ${B2x},${B2y} ${C2x},${C2y}" fill="rgba(139,92,246,.15)" stroke="#8b5cf6" stroke-width="1.8"/>`;
s+=`<text x="${A2x}" y="${A2y-6}" text-anchor="middle" font-size="10" fill="#8b5cf6" font-weight="700">A'</text>`;
s+=`<text x="${B2x-12}" y="${B2y+4}" font-size="10" fill="#8b5cf6" font-weight="700">B'</text>`;
s+=`<text x="${C2x+4}" y="${C2y+4}" font-size="10" fill="#8b5cf6" font-weight="700">C'</text>`;
// подписи сторон T2 — OUTSIDE
s+=`<text x="${(B2x+C2x)/2}" y="${B2y+16}" text-anchor="middle" font-size="9" fill="#8b5cf6">a'=${fmt(a2/10)}</text>`;
s+=`<text x="${(A2x+B2x)/2-10}" y="${(A2y+B2y)/2}" text-anchor="end" font-size="9" fill="#8b5cf6">c'=${fmt(c2/10)}</text>`;
s+=`<text x="${(A2x+C2x)/2+8}" y="${(A2y+C2y)/2}" font-size="9" fill="#8b5cf6">b'=${fmt(b2/10)}</text>`;
s+='</svg>';
svgWrap.innerHTML=s;
infoEl.innerHTML=`$k=${k.toFixed(1)}$: $a/a'=k=${k.toFixed(1)} \\to a'=${fmt(a/10/k)}$, $b'=${fmt(b/10/k)}$, $c'=${fmt(c/10/k)}$. Углы всегда равны: $\\angle A=\\angle A', \\angle B=\\angle B', \\angle C=\\angle C'$.`;
renderMath(infoEl);
addXp(1,'p3-sim');
}
kSl.addEventListener('input',draw);
draw();
})();
/* == INIT ИНТЕРАКТИВ 2: калькулятор == */
(function(){
document.getElementById('p3-ccalc').addEventListener('click',()=>{
const a=parseFloat(document.getElementById('p3-ca').value);
const b=parseFloat(document.getElementById('p3-cb').value);
const c=parseFloat(document.getElementById('p3-cc').value);
const k=parseFloat(document.getElementById('p3-ck').value);
const out=document.getElementById('p3-ccalc-out');
if([a,b,c,k].some(v=>!isFinite(v)||v<=0)){
out.style.display='block';out.innerHTML='<span style="color:var(--bad)">Введи положительные числа для a, b, c, k.</span>';return;
}
out.style.display='block';
out.innerHTML=`Стороны подобного треугольника при $k=${fmt(k)}$:<br>$a'=a/k=${fmt(a/k)}$, $b'=b/k=${fmt(b/k)}$, $c'=c/k=${fmt(c/k)}$`;
renderMath(out);
addXp(2,'p3-calc');bumpProgress('p3',5);
});
})();
/* == INIT ИНТЕРАКТИВ 3: DnD == */
(function(){
const items=[
{text:'(3,4,5) и (6,8,10)',yes:true},
{text:'(5,7,9) и (10,15,18)',yes:false},
{text:'(2,3,4) и (4,6,8)',yes:true},
{text:'(1,2,3) и (2,4,7)',yes:false},
{text:'(6,8,10) и (3,4,5)',yes:true},
];
const pool=document.getElementById('p3-dnd-pool');
const yesBox=document.getElementById('p3-drop-yes-items');
const noBox=document.getElementById('p3-drop-no-items');
let dragging=null;
function makeChip(it,idx){
const chip=document.createElement('div');
chip.className='dnd-chip';
chip.dataset.idx=idx;
chip.textContent=it.text;
chip.draggable=true;
chip.addEventListener('dragstart',e=>{dragging=chip;chip.classList.add('dragging');e.dataTransfer.effectAllowed='move';});
chip.addEventListener('dragend',()=>{chip.classList.remove('dragging');dragging=null;});
return chip;
}
items.forEach((it,i)=>pool.appendChild(makeChip(it,i)));
[document.getElementById('p3-drop-yes'),document.getElementById('p3-drop-no'),pool].forEach(box=>{
box.addEventListener('dragover',e=>{e.preventDefault();box.classList.add('over');});
box.addEventListener('dragleave',()=>box.classList.remove('over'));
box.addEventListener('drop',e=>{
e.preventDefault();box.classList.remove('over');
if(!dragging)return;
const target=box===document.getElementById('p3-drop-yes')?yesBox:box===document.getElementById('p3-drop-no')?noBox:pool;
target.appendChild(dragging);
});
});
document.getElementById('p3-dnd-check').addEventListener('click',()=>{
const fb=document.getElementById('p3-dnd-fb');
const yChips=[...yesBox.querySelectorAll('.dnd-chip')];
const nChips=[...noBox.querySelectorAll('.dnd-chip')];
if(yChips.length+nChips.length<items.length){feedback(fb,false,'Разложи все карточки.');return;}
let ok=true;
yChips.forEach(c=>{if(!items[+c.dataset.idx].yes)ok=false;});
nChips.forEach(c=>{if(items[+c.dataset.idx].yes)ok=false;});
if(ok){feedback(fb,true,'Верно! +5 XP');addXp(5,'p3-dnd');bumpProgress('p3',8);}
else{feedback(fb,false,'Есть ошибки — проверь пропорциональность сторон.');}
});
document.getElementById('p3-dnd-reset').addEventListener('click',()=>{
[...yesBox.children].forEach(c=>pool.appendChild(c));
[...noBox.children].forEach(c=>pool.appendChild(c));
document.getElementById('p3-dnd-fb').style.display='none';
});
})();
/* == INIT ИНТЕРАКТИВ 4: тренажёр == */
(function(){
const tasks=[
{q:'$\\triangle ABC$ со сторонами $6,8,10$ подобен $\\triangle A\'B\'C\'$ со сторонами $3,4,5$. Чему равен коэффициент подобия?', ans:2, hint:'k = 6/3 = 8/4 = 10/5 = 2.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$, коэффициент $k=3$. Сторона $a=15$. Найди $a\'$.', ans:5, hint:'a\' = a/k = 15/3 = 5.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$, $k=2$. Сторона $b\'=7$. Найди $b$.', ans:14, hint:'b = k·b\' = 2·7 = 14.'},
{q:'Стороны треугольников $4,6,8$ и $6,9,12$. Найди коэффициент подобия.', ans:1.5, hint:'k = 6/4 = 9/6 = 12/8 = 1.5.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$, $a=20$, $a\'=4$. Чему равен $k$?', ans:5, hint:'k = a/a\' = 20/4 = 5.'},
];
let idx=0,score=0;
function show(){
document.getElementById('p3-tr-i').textContent=idx+1;
const t=document.getElementById('p3-tr-task');
t.innerHTML=tasks[idx].q;
renderMath(t);
document.getElementById('p3-tr-ans').value='';
document.getElementById('p3-tr-fb').style.display='none';
}
document.getElementById('p3-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p3-tr-score').textContent=0;show();});
document.getElementById('p3-tr-go').addEventListener('click',()=>{
if(idx>=tasks.length)return;
const ans=+document.getElementById('p3-tr-ans').value;
const fb=document.getElementById('p3-tr-fb');
if(Math.abs(ans-tasks[idx].ans)<0.05){
score++;document.getElementById('p3-tr-score').textContent=score;
addXp(3,'p3-tr-'+idx);bumpProgress('p3',4);
if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}
else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p3-tr-all');bumpProgress('p3',10);confetti();}
} else {
feedback(fb,false,'Неверно. '+tasks[idx].hint);
}
});
document.getElementById('p3-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p3-tr-go').click();});
show();
})();
/* == INIT ИНТЕРАКТИВ 5: мини-квиз == */
(function(){
const qs=[
{q:'Коэффициент подобия равен 1. Что это означает?', opts:['Треугольники конгруэнтны','Треугольники не подобны','Треугольники равновелики по площади','Треугольники прямоугольные'], ans:0},
{q:'Если $\\triangle ABC \\sim \\triangle A\'B\'C\'$ с $k=3$, то $\\triangle A\'B\'C\' \\sim \\triangle ABC$ с коэффициентом:', opts:['3','1/3','9','1/9'], ans:1},
{q:'Что является достаточным условием подобия треугольников?', opts:['Равенство двух углов','Равенство одной стороны','Равенство периметров','Равенство площадей'], ans:0},
{q:'Подобие — это:', opts:['Отношение эквивалентности','Только рефлексивное отношение','Только транзитивное отношение','Отношение порядка'], ans:0},
];
const body=document.getElementById('p3-quiz-body');
body.innerHTML=qs.map((q,qi)=>`
<div style="margin-bottom:14px;padding:12px;background:var(--card);border-radius:10px;border:1px solid var(--border)">
<div style="font-weight:700;margin-bottom:8px;font-size:.95rem">${qi+1}. ${q.q}</div>
${q.opts.map((o,oi)=>`<label style="display:flex;align-items:center;gap:8px;margin-bottom:6px;cursor:pointer;font-size:.92rem"><input type="radio" name="p3q${qi}" value="${oi}" style="accent-color:var(--sec-acc,var(--pri))"> ${o}</label>`).join('')}
</div>`).join('');
document.getElementById('p3-quiz-check').addEventListener('click',()=>{
const fb=document.getElementById('p3-quiz-fb');
let correct=0;
qs.forEach((q,qi)=>{
const sel=document.querySelector(`input[name="p3q${qi}"]:checked`);
if(sel&&+sel.value===q.ans)correct++;
});
if(correct===qs.length){
feedback(fb,true,`Все ${qs.length} верно! +10 XP`);
addXp(10,'p3-quiz');bumpProgress('p3',10);confetti();
} else {
feedback(fb,false,`Верно ${correct} из ${qs.length}. Перечитай карточки теории.`);
}
});
})();
/* == INIT: Босс §3 == */
(function(){
const tasks=[
{q:'$\\triangle ABC$ со сторонами $9,12,15$. Коэффициент подобия с $\\triangle A\'B\'C\'$ равен $3$. Найди наименьшую сторону $\\triangle A\'B\'C\'$.', ans:3, hint:'a\' = 9/3 = 3.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$. Стороны $\\triangle A\'B\'C\'$: $5,7,x$, стороны $\\triangle ABC$: $10,14,16$. Найди $x$.', ans:8, hint:'k=2, x = 16/2 = 8.'},
{q:'Периметр $\\triangle ABC = 36$, $k=3$. Найди периметр подобного треугольника $\\triangle A\'B\'C\'$.', ans:12, hint:'P\' = P/k = 36/3 = 12.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$, $k=4$. Сторона $c=20$. Найди $c\'$.', ans:5, hint:'c\' = c/k = 20/4 = 5.'},
];
const bossBox=document.getElementById('p3-boss-tasks');
bossBox.innerHTML=tasks.map((t,i)=>`
<div style="padding:14px;background:var(--card);border-radius:10px;border:1px solid var(--border);margin-bottom:10px">
<div style="margin-bottom:8px;font-size:.95rem">${t.q}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
<input type="number" class="tinp" id="p3b-a${i}" placeholder="Ответ" style="width:110px">
<button class="btn primary small" onclick="(function(){
const v=+document.getElementById('p3b-a${i}').value;
const fb=document.getElementById('p3b-fb${i}');
if(Math.abs(v-${t.ans})<0.05){
feedback(fb,true,'Верно! +5 XP');
if(!window.p3BossSolved.has(${i})){ window.p3BossSolved.add(${i}); addXp(5,'p3-boss${i}'); bumpProgress('p3',8); }
} else feedback(fb,false,'Неверно. ${t.hint}');
})()">Проверить</button>
</div>
<div class="feedback" id="p3b-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p3BossSolved=new Set();
renderMath(bossBox);
})();
}
function buildP4(){
const box=document.getElementById('p4-body');
let html='';
/* ---- Theory cards ---- */
html+=makeCard('theory','Теорема о прямой, параллельной стороне треугольника','4.1',`
<p><b>Теорема.</b> Прямая, параллельная одной из сторон треугольника и пересекающая две другие стороны, отсекает треугольник, подобный исходному.</p>
<p style="margin-top:8px"><b>Формально:</b> в $\\triangle ABC$ прямая $MN \\parallel BC$ пересекает $AB$ в точке $M$ и $AC$ в точке $N$. Тогда $\\triangle AMN \\sim \\triangle ABC$ с коэффициентом подобия:</p>
$$k = \\dfrac{AM}{AB} = \\dfrac{AN}{AC} = \\dfrac{MN}{BC}$$
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 280 175" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- triangle ABC -->
<polygon points="140,15 30,155 250,155" fill="rgba(99,102,241,.10)" stroke="#6366f1" stroke-width="2"/>
<!-- MN line at t=0.45 -->
<!-- M on AB: M = A + 0.45*(B-A) = (140+0.45*(30-140), 15+0.45*(155-15)) = (89.5, 78) -->
<!-- N on AC: N = A + 0.45*(C-A) = (140+0.45*(250-140), 15+0.45*(155-15)) = (189.5, 78) -->
<polygon points="140,15 89.5,78 189.5,78" fill="rgba(99,102,241,.22)" stroke="#4f46e5" stroke-width="2"/>
<line x1="72" y1="78" x2="210" y2="78" stroke="#4f46e5" stroke-width="1.8" stroke-dasharray="5,3"/>
<!-- labels -->
<text x="140" y="10" text-anchor="middle" font-size="11" font-weight="700" fill="#4338ca">A</text>
<text x="22" y="165" font-size="11" font-weight="700" fill="#4338ca">B</text>
<text x="253" y="165" font-size="11" font-weight="700" fill="#4338ca">C</text>
<text x="82" y="76" text-anchor="end" font-size="10" font-weight="700" fill="#4f46e5">M</text>
<text x="196" y="76" font-size="10" font-weight="700" fill="#4f46e5">N</text>
<text x="140" y="95" text-anchor="middle" font-size="9" fill="#4f46e5">MN ∥ BC</text>
<text x="140" y="170" text-anchor="middle" font-size="9" fill="#4338ca" font-style="italic">△AMN △ABC</text>
<!-- k label -->
<text x="265" y="30" font-size="10" fill="#4f46e5" font-weight="700">k=AM/AB</text>
</svg>
</div>`);
html+=makeCard('rule','Следствие — теорема Фалеса для треугольника','4.2',`
<p><b>Следствие.</b> Если $MN \\parallel BC$ в $\\triangle ABC$, то:</p>
$$\\dfrac{AM}{MB} = \\dfrac{AN}{NC}$$
<p style="margin-top:8px">То есть прямая, параллельная стороне треугольника, делит две другие стороны пропорционально.</p>
<p style="margin-top:8px">Это прямое следствие обобщённой теоремы Фалеса: прямые $BC$, $MN$ и $A$ порождают пропорциональное деление сторон $AB$ и $AC$.</p>
<div style="display:flex;justify-content:center;margin-top:12px">
<svg viewBox="0 0 280 155" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<polygon points="140,12 30,140 250,140" fill="rgba(99,102,241,.08)" stroke="#6366f1" stroke-width="2"/>
<!-- MN at t=0.5 -->
<line x1="85" y1="76" x2="195" y2="76" stroke="#4f46e5" stroke-width="2"/>
<circle cx="85" cy="76" r="3.5" fill="#4f46e5"/>
<circle cx="195" cy="76" r="3.5" fill="#4f46e5"/>
<text x="140" y="8" text-anchor="middle" font-size="11" font-weight="700" fill="#4338ca">A</text>
<text x="22" y="150" font-size="11" font-weight="700" fill="#4338ca">B</text>
<text x="253" y="150" font-size="11" font-weight="700" fill="#4338ca">C</text>
<text x="78" y="74" text-anchor="end" font-size="10" font-weight="700" fill="#4f46e5">M</text>
<text x="200" y="74" font-size="10" font-weight="700" fill="#4f46e5">N</text>
<!-- AM, MB labels -->
<text x="62" y="44" text-anchor="middle" font-size="9" fill="#4f46e5">AM</text>
<text x="56" y="112" text-anchor="middle" font-size="9" fill="#6366f1">MB</text>
<!-- AN, NC labels -->
<text x="221" y="44" text-anchor="middle" font-size="9" fill="#4f46e5">AN</text>
<text x="228" y="112" text-anchor="middle" font-size="9" fill="#6366f1">NC</text>
<text x="140" y="148" text-anchor="middle" font-size="9" fill="#4338ca" font-style="italic">AM/MB = AN/NC</text>
</svg>
</div>`);
html+=makeCard('example','Пример вычисления','4.3',`
<p><b>Пример.</b> В $\\triangle ABC$ прямая $MN \\parallel BC$. $AM = 6$, $AB = 10$, $BC = 15$. Найти $MN$.</p>
<p style="margin-top:8px"><b>Решение:</b> коэффициент подобия $k = AM/AB = 6/10 = 0{,}6$.</p>
<p style="margin-top:4px">$MN = k \\cdot BC = 0{,}6 \\cdot 15 = 9$.</p>
<p style="margin-top:8px"><b>Пример 2.</b> $AM = 4$, $MB = 6$. Найти $AN/NC$.</p>
<p style="margin-top:4px">По следствию: $AN/NC = AM/MB = 4/6 = 2/3$.</p>`);
/* ---- ИНТЕРАКТИВ 1: SVG треугольник со slider ---- */
html+=`<div class="wg" id="p4-par-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Параллельная прямая отсекает подобный треугольник</div></div>
<div class="wg-help">Перемещай прямую $MN \\parallel BC$ по высоте треугольника. Коэффициент подобия $k$ меняется — $\\triangle AMN \\sim \\triangle ABC$.</div>
<div class="sliders">
<label>Положение $MN$ (t = AM/AB): <b id="p4-t-val">0.50</b>
<input type="range" min="10" max="90" value="50" id="p4-t-sl" step="1">
</label>
</div>
<div id="p4-par-svg" style="display:flex;justify-content:center"></div>
<div id="p4-par-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.92rem;line-height:1.8"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 2: Пошаговое доказательство ---- */
html+=`<div class="wg" id="p4-proof-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Доказательство — по шагам</div></div>
<div class="wg-help">Нажимай «Далее» — каждый шаг раскрывает ключевую идею доказательства.</div>
<div id="p4-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p4-proof-desc" style="padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.6;margin-bottom:10px;min-height:60px"></div>
<div style="display:flex;gap:8px">
<button class="btn primary" id="p4-proof-next">Далее</button>
<button class="btn" id="p4-proof-reset">Сначала</button>
</div>
</div>`;
/* ---- ИНТЕРАКТИВ 3: Калькулятор ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Калькулятор: параллельная сторона</div></div>
<div class="wg-help">Введи $AM$, $AB$, $BC$ → найди $MN = BC \\cdot AM/AB$. Или задай $MN$, $BC$ → найди $AM/AB$.</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(80px,1fr));gap:8px;margin-bottom:10px">
<div><span style="font-size:.8rem;color:var(--muted);display:block">AM</span><input type="number" id="p4-cAM" class="tinp" placeholder="AM" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">AB</span><input type="number" id="p4-cAB" class="tinp" placeholder="AB" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">BC</span><input type="number" id="p4-cBC" class="tinp" placeholder="BC" style="width:100%"></div>
<div style="display:flex;align-items:flex-end"><button class="btn primary" id="p4-ccalc" style="width:100%">Вычислить MN</button></div>
</div>
<div id="p4-ccalc-out" style="display:none;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 4: Тренажёр ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр §4</div></div>
<div class="wg-help">5 задач на нахождение MN, AN, AM и отношений.</div>
<div class="score-display"><span>Задача <b id="p4-tr-i">1</b> / 5</span><span>Очки: <b id="p4-tr-score">0</b></span></div>
<div id="p4-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.02rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p4-tr-ans" class="tinp" placeholder="Ответ" style="width:120px">
<button class="btn primary" id="p4-tr-go">Проверить</button>
<button class="btn" id="p4-tr-start">Начать</button>
</div>
<div class="feedback" id="p4-tr-fb" style="display:none"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 5: DnD-сортер ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Соотнеси параллельные прямые и пропорции</div></div>
<div class="wg-help">Перетащи каждую карточку: «Пропорция верна» или «Пропорция неверна».</div>
<div id="p4-dnd-pool" class="dnd-pool"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:10px">
<div class="drop-box" id="p4-drop-yes"><h5>Пропорция верна</h5><div class="drop-items" id="p4-drop-yes-items"></div></div>
<div class="drop-box" id="p4-drop-no"><h5>Пропорция неверна</h5><div class="drop-items" id="p4-drop-no-items"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p4-dnd-check">Проверить</button><button class="btn" id="p4-dnd-reset">Сбросить</button></div>
<div class="feedback" id="p4-dnd-fb" style="display:none"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 6: Босс §4 ---- */
html+=`<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §4</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">4 задачи — каждая верная даёт +5 XP.</div>
<div id="p4-boss-tasks"></div>
</div>`;
html+=`<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p4-read-btn" onclick="addXp(10,'p4-read');bumpProgress('p4',40);this.textContent='Прочитано!';this.disabled=true;">
<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>
Я прочитал §4 (+10 XP)
</button>
</div>`;
html+=secNav('p3','p5');
box.innerHTML=html;
if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
/* == INIT ИНТЕРАКТИВ 1: slider параллельной прямой == */
(function(){
const tSl=document.getElementById('p4-t-sl');
const tVal=document.getElementById('p4-t-val');
const svgWrap=document.getElementById('p4-par-svg');
const infoEl=document.getElementById('p4-par-info');
// base triangle: A=(140,15), B=(30,155), C=(250,155)
const Ax=140,Ay=15,Bx=30,By=155,Cx=250,Cy=155;
const BC=Math.hypot(Cx-Bx,Cy-By);
const AB=Math.hypot(Bx-Ax,By-Ay);
const AC=Math.hypot(Cx-Ax,Cy-Ay);
function draw(){
const t=+tSl.value/100;
tVal.textContent=t.toFixed(2);
const Mx=Ax+t*(Bx-Ax), My=Ay+t*(By-Ay);
const Nx=Ax+t*(Cx-Ax), Ny=Ay+t*(Cy-Ay);
const MN=Math.hypot(Nx-Mx,Ny-My);
const AM=Math.hypot(Mx-Ax,My-Ay);
const AN=Math.hypot(Nx-Ax,Ny-Ay);
const W=300, H=180;
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
// full triangle light
s+=`<polygon points="${Ax},${Ay} ${Bx},${By} ${Cx},${Cy}" fill="rgba(99,102,241,.07)" stroke="#6366f1" stroke-width="1.5"/>`;
// inner triangle AMN highlighted
s+=`<polygon points="${Ax},${Ay} ${Mx},${My} ${Nx},${Ny}" fill="rgba(79,70,229,.22)" stroke="#4f46e5" stroke-width="2"/>`;
// MN line
s+=`<line x1="${Mx}" y1="${My}" x2="${Nx}" y2="${Ny}" stroke="#4f46e5" stroke-width="2"/>`;
// points
s+=`<circle cx="${Mx}" cy="${My}" r="4" fill="#4f46e5"/>`;
s+=`<circle cx="${Nx}" cy="${Ny}" r="4" fill="#4f46e5"/>`;
// labels
s+=`<text x="${Ax}" y="${Ay-5}" text-anchor="middle" font-size="11" font-weight="700" fill="#4338ca">A</text>`;
s+=`<text x="${Bx-8}" y="${By+2}" font-size="11" font-weight="700" fill="#4338ca">B</text>`;
s+=`<text x="${Cx+4}" y="${Cy+2}" font-size="11" font-weight="700" fill="#4338ca">C</text>`;
s+=`<text x="${Mx-8}" y="${My}" text-anchor="end" font-size="10" font-weight="700" fill="#4f46e5">M</text>`;
s+=`<text x="${Nx+4}" y="${Ny}" font-size="10" font-weight="700" fill="#4f46e5">N</text>`;
// k badge
s+=`<text x="${W/2}" y="14" text-anchor="middle" font-size="11" font-weight="800" fill="#4338ca">k = ${t.toFixed(2)}</text>`;
s+='</svg>';
svgWrap.innerHTML=s;
infoEl.innerHTML=`$k = AM/AB = ${t.toFixed(3)}$. $MN = k \\cdot BC = ${t.toFixed(3)} \\cdot ${fmt(BC/10)} = ${fmt(MN/10)}$. <br> $AM/MB = ${fmt(t/(1-t+1e-9))}$. По следствию: $AN/NC = AM/MB = ${fmt(t/(1-t+1e-9))}$.`;
renderMath(infoEl);
addXp(1,'p4-par');
}
tSl.addEventListener('input',draw);
draw();
})();
/* == INIT ИНТЕРАКТИВ 2: пошаговое доказательство == */
(function(){
const steps=[
{desc:'<b>Шаг 1.</b> Дан $\\triangle ABC$. Прямая $MN \\parallel BC$ пересекает $AB$ в $M$ и $AC$ в $N$. Требуется доказать $\\triangle AMN \\sim \\triangle ABC$.',
svg:`<svg viewBox="0 0 260 160" style="max-width:260px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><polygon points="130,14 25,148 235,148" fill="rgba(99,102,241,.08)" stroke="#6366f1" stroke-width="2"/><line x1="78" y1="81" x2="182" y2="81" stroke="#4f46e5" stroke-width="2" stroke-dasharray="5,3"/><circle cx="78" cy="81" r="3.5" fill="#4f46e5"/><circle cx="182" cy="81" r="3.5" fill="#4f46e5"/><text x="130" y="10" text-anchor="middle" font-size="11" font-weight="700" fill="#4338ca">A</text><text x="17" y="157" font-size="11" font-weight="700" fill="#4338ca">B</text><text x="237" y="157" font-size="11" font-weight="700" fill="#4338ca">C</text><text x="70" y="79" text-anchor="end" font-size="10" font-weight="700" fill="#4f46e5">M</text><text x="186" y="79" font-size="10" font-weight="700" fill="#4f46e5">N</text><text x="130" y="97" text-anchor="middle" font-size="9" fill="#4f46e5">MN ∥ BC</text></svg>`},
{desc:'<b>Шаг 2.</b> $\\angle AMN = \\angle ABC$, так как $MN \\parallel BC$ и $AB$ — секущая: это соответственные углы при параллельных прямых и секущей.',
svg:`<svg viewBox="0 0 260 160" style="max-width:260px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><polygon points="130,14 25,148 235,148" fill="rgba(99,102,241,.08)" stroke="#6366f1" stroke-width="2"/><line x1="78" y1="81" x2="182" y2="81" stroke="#4f46e5" stroke-width="2"/><circle cx="78" cy="81" r="3.5" fill="#4f46e5"/><circle cx="182" cy="81" r="3.5" fill="#4f46e5"/><text x="130" y="10" text-anchor="middle" font-size="11" font-weight="700" fill="#4338ca">A</text><text x="17" y="157" font-size="11" font-weight="700" fill="#4338ca">B</text><text x="237" y="157" font-size="11" font-weight="700" fill="#4338ca">C</text><text x="70" y="79" text-anchor="end" font-size="10" font-weight="700" fill="#4f46e5">M</text><text x="186" y="79" font-size="10" font-weight="700" fill="#4f46e5">N</text><path d="M78,81 Q88,88 83,95" fill="none" stroke="#f59e0b" stroke-width="2"/><path d="M25,148 Q38,138 33,128" fill="none" stroke="#f59e0b" stroke-width="2"/><text x="96" y="100" font-size="9" fill="#b45309" font-weight="700">∠AMN = ∠ABC</text></svg>`},
{desc:'<b>Шаг 3.</b> Аналогично $\\angle ANM = \\angle ACB$: $MN \\parallel BC$ и $AC$ — секущая, получаем соответственные углы.',
svg:`<svg viewBox="0 0 260 160" style="max-width:260px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><polygon points="130,14 25,148 235,148" fill="rgba(99,102,241,.08)" stroke="#6366f1" stroke-width="2"/><line x1="78" y1="81" x2="182" y2="81" stroke="#4f46e5" stroke-width="2"/><circle cx="78" cy="81" r="3.5" fill="#4f46e5"/><circle cx="182" cy="81" r="3.5" fill="#4f46e5"/><text x="130" y="10" text-anchor="middle" font-size="11" font-weight="700" fill="#4338ca">A</text><text x="17" y="157" font-size="11" font-weight="700" fill="#4338ca">B</text><text x="237" y="157" font-size="11" font-weight="700" fill="#4338ca">C</text><text x="70" y="79" text-anchor="end" font-size="10" font-weight="700" fill="#4f46e5">M</text><text x="186" y="79" font-size="10" font-weight="700" fill="#4f46e5">N</text><path d="M182,81 Q172,88 177,95" fill="none" stroke="#10b981" stroke-width="2"/><path d="M235,148 Q222,138 227,128" fill="none" stroke="#10b981" stroke-width="2"/><text x="130" y="100" text-anchor="middle" font-size="9" fill="#047857" font-weight="700">∠ANM = ∠ACB</text></svg>`},
{desc:'<b>Шаг 4.</b> Угол $\\angle A$ общий у $\\triangle AMN$ и $\\triangle ABC$. Два угла $\\triangle AMN$ равны соответствующим двум углам $\\triangle ABC$ → по признаку по двум углам $\\triangle AMN \\sim \\triangle ABC$.',
svg:`<svg viewBox="0 0 260 160" style="max-width:260px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><polygon points="130,14 25,148 235,148" fill="rgba(99,102,241,.08)" stroke="#6366f1" stroke-width="1.5"/><polygon points="130,14 78,81 182,81" fill="rgba(79,70,229,.22)" stroke="#4f46e5" stroke-width="2"/><text x="130" y="10" text-anchor="middle" font-size="11" font-weight="700" fill="#4338ca">A</text><text x="17" y="157" font-size="11" font-weight="700" fill="#4338ca">B</text><text x="237" y="157" font-size="11" font-weight="700" fill="#4338ca">C</text><text x="70" y="79" text-anchor="end" font-size="10" font-weight="700" fill="#4f46e5">M</text><text x="186" y="79" font-size="10" font-weight="700" fill="#4f46e5">N</text><text x="130" y="95" text-anchor="middle" font-size="9" fill="#4f46e5">△AMN △ABC</text></svg>`},
{desc:'<b>Шаг 5.</b> Из подобия следует пропорциональность сторон: $AM/AB = AN/AC = MN/BC = k$. Следствие: $AM/MB = AN/NC$ — прямое следствие теоремы Фалеса. <b>Доказано.</b>',
svg:`<svg viewBox="0 0 260 80" style="max-width:260px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="130" y="24" text-anchor="middle" font-size="11" font-weight="800" fill="#4338ca">AM/AB = AN/AC = MN/BC = k</text><text x="130" y="46" text-anchor="middle" font-size="10" fill="#4f46e5" font-weight="700">AM/MB = AN/NC</text><text x="130" y="68" text-anchor="middle" font-size="10" fill="#10b981" font-weight="800">QED ∎</text></svg>`},
];
let step=0;
const svgEl=document.getElementById('p4-proof-svg');
const descEl=document.getElementById('p4-proof-desc');
function show(){
svgEl.innerHTML=steps[step].svg;
descEl.innerHTML=steps[step].desc;
renderMath(descEl);
}
document.getElementById('p4-proof-next').addEventListener('click',()=>{
if(step<steps.length-1){step++;show();addXp(1,'p4-proof-step');}
else{addXp(5,'p4-proof-done');bumpProgress('p4',10);}
});
document.getElementById('p4-proof-reset').addEventListener('click',()=>{step=0;show();});
show();
})();
/* == INIT ИНТЕРАКТИВ 3: калькулятор == */
(function(){
document.getElementById('p4-ccalc').addEventListener('click',()=>{
const AM=parseFloat(document.getElementById('p4-cAM').value);
const AB=parseFloat(document.getElementById('p4-cAB').value);
const BC=parseFloat(document.getElementById('p4-cBC').value);
const out=document.getElementById('p4-ccalc-out');
if(!isFinite(AM)||!isFinite(AB)||!isFinite(BC)||AM<=0||AB<=0||BC<=0||AM>AB){
out.style.display='block';out.innerHTML='<span style="color:var(--bad)">Введи положительные числа; AM должно быть ≤ AB.</span>';return;
}
const k=AM/AB;
const MN=k*BC;
const AN_over_NC=AM/(AB-AM);
out.style.display='block';
out.innerHTML=`$k = AM/AB = ${fmt(AM)}/${fmt(AB)} = ${fmt(k)}$<br>$MN = k \\cdot BC = ${fmt(k)} \\cdot ${fmt(BC)} = ${fmt(MN)}$<br>Следствие: $AM/MB = AN/NC = ${fmt(AM)}/${fmt(AB-AM)} = ${fmt(AN_over_NC)}$`;
renderMath(out);
addXp(2,'p4-calc');bumpProgress('p4',5);
});
})();
/* == INIT ИНТЕРАКТИВ 4: тренажёр == */
(function(){
const tasks=[
{q:'В $\\triangle ABC$ прямая $MN \\parallel BC$, $AM=4$, $AB=10$, $BC=20$. Найди $MN$.',ans:8,hint:'k=4/10=0.4; MN=0.4·20=8.'},
{q:'В $\\triangle ABC$ прямая $MN \\parallel BC$, $AM=6$, $AB=9$, $BC=12$. Найди $MN$.',ans:8,hint:'k=6/9=2/3; MN=2/3·12=8.'},
{q:'$MN \\parallel BC$, $AM=5$, $MB=10$. Найди $AN/NC$.',ans:0.5,hint:'AN/NC = AM/MB = 5/10 = 0.5.'},
{q:'$MN \\parallel BC$, $k=0.4$, $BC=30$. Найди $MN$.',ans:12,hint:'MN=k·BC=0.4·30=12.'},
{q:'$MN \\parallel BC$, $AM=3$, $MB=6$. Чему равно $AN/AC$?',ans:0.333,hint:'AN/AC = AM/AB = 3/(3+6) = 1/3 ≈ 0.333.'},
];
let idx=0,score=0;
function show(){
document.getElementById('p4-tr-i').textContent=idx+1;
const t=document.getElementById('p4-tr-task');
t.innerHTML=tasks[idx].q;
renderMath(t);
document.getElementById('p4-tr-ans').value='';
document.getElementById('p4-tr-fb').style.display='none';
}
document.getElementById('p4-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p4-tr-score').textContent=0;show();});
document.getElementById('p4-tr-go').addEventListener('click',()=>{
if(idx>=tasks.length)return;
const ans=+document.getElementById('p4-tr-ans').value;
const fb=document.getElementById('p4-tr-fb');
if(Math.abs(ans-tasks[idx].ans)<0.01){
score++;document.getElementById('p4-tr-score').textContent=score;
addXp(3,'p4-tr-'+idx);bumpProgress('p4',4);
if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}
else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p4-tr-all');bumpProgress('p4',10);confetti();}
} else {
feedback(fb,false,'Неверно. '+tasks[idx].hint);
}
});
document.getElementById('p4-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p4-tr-go').click();});
show();
})();
/* == INIT ИНТЕРАКТИВ 5: DnD-сортер == */
(function(){
const items=[
{text:'MN∥BC, AM=3, AB=6, AN=4, AC=8 → AM/MB=AN/NC',yes:true},
{text:'MN∥BC, AM=4, MB=6, AN=3, NC=5 → AM/MB≠AN/NC',yes:false},
{text:'MN∥BC, AM/AB=0.5, MN/BC=0.5',yes:true},
{text:'MN∥BC, AM=2, MB=4, AN=3, NC=6 → AM/MB=AN/NC',yes:true},
{text:'MN∥BC, k=1/3, MN=BC/2',yes:false},
];
const pool=document.getElementById('p4-dnd-pool');
const yesBox=document.getElementById('p4-drop-yes-items');
const noBox=document.getElementById('p4-drop-no-items');
let dragging=null;
function makeChip(it,idx){
const chip=document.createElement('div');
chip.className='dnd-chip';
chip.dataset.idx=idx;
chip.textContent=it.text;
chip.draggable=true;
chip.addEventListener('dragstart',e=>{dragging=chip;chip.classList.add('dragging');e.dataTransfer.effectAllowed='move';});
chip.addEventListener('dragend',()=>{chip.classList.remove('dragging');dragging=null;});
return chip;
}
items.forEach((it,i)=>pool.appendChild(makeChip(it,i)));
[document.getElementById('p4-drop-yes'),document.getElementById('p4-drop-no'),pool].forEach(box=>{
box.addEventListener('dragover',e=>{e.preventDefault();box.classList.add('over');});
box.addEventListener('dragleave',()=>box.classList.remove('over'));
box.addEventListener('drop',e=>{
e.preventDefault();box.classList.remove('over');
if(!dragging)return;
const target=box===document.getElementById('p4-drop-yes')?yesBox:box===document.getElementById('p4-drop-no')?noBox:pool;
target.appendChild(dragging);
});
});
document.getElementById('p4-dnd-check').addEventListener('click',()=>{
const fb=document.getElementById('p4-dnd-fb');
const yChips=[...yesBox.querySelectorAll('.dnd-chip')];
const nChips=[...noBox.querySelectorAll('.dnd-chip')];
if(yChips.length+nChips.length<items.length){feedback(fb,false,'Разложи все карточки.');return;}
let ok=true;
yChips.forEach(c=>{if(!items[+c.dataset.idx].yes)ok=false;});
nChips.forEach(c=>{if(items[+c.dataset.idx].yes)ok=false;});
if(ok){feedback(fb,true,'Верно! +5 XP');addXp(5,'p4-dnd');bumpProgress('p4',8);}
else{feedback(fb,false,'Есть ошибки. Проверь: MN∥BC → AM/MB=AN/NC и MN/BC=AM/AB.');}
});
document.getElementById('p4-dnd-reset').addEventListener('click',()=>{
[...yesBox.children].forEach(c=>pool.appendChild(c));
[...noBox.children].forEach(c=>pool.appendChild(c));
document.getElementById('p4-dnd-fb').style.display='none';
});
})();
/* == INIT: Босс §4 == */
(function(){
const tasks=[
{q:'<svg viewBox="0 0 240 140" style="display:block;max-width:240px;margin:0 auto 8px;background:#e0e7ff;border:1px solid #a5b4fc;border-radius:8px"><polygon points="120,12 20,128 220,128" fill="rgba(99,102,241,.12)" stroke="#6366f1" stroke-width="2"/><line x1="70" y1="70" x2="170" y2="70" stroke="#4f46e5" stroke-width="2"/><text x="120" y="8" text-anchor="middle" font-size="10" fill="#4338ca" font-weight="700">A</text><text x="12" y="136" font-size="10" fill="#4338ca" font-weight="700">B</text><text x="222" y="136" font-size="10" fill="#4338ca" font-weight="700">C</text><text x="62" y="68" text-anchor="end" font-size="10" fill="#4f46e5" font-weight="700">M</text><text x="174" y="68" font-size="10" fill="#4f46e5" font-weight="700">N</text><text x="120" y="90" text-anchor="middle" font-size="9" fill="#4f46e5">AM=5, AB=8, BC=24</text></svg>$MN \\parallel BC$. $AM=5$, $AB=8$, $BC=24$. Найди $MN$.',ans:15,hint:'k=5/8; MN=5/8·24=15.'},
{q:'$MN \\parallel BC$, $AM=6$, $MB=9$. Найди $AN/NC$.',ans:0.667,hint:'AN/NC = AM/MB = 6/9 = 2/3 ≈ 0.667.'},
{q:'$MN \\parallel BC$, $MN=8$, $BC=12$. Найди $AM/AB$.',ans:0.667,hint:'AM/AB = MN/BC = 8/12 = 2/3 ≈ 0.667.'},
{q:'В $\\triangle ABC$ прямая $MN \\parallel BC$, $AM=3$, $AB=9$, $BC=18$. Найди $MN$.',ans:6,hint:'k=3/9=1/3; MN=1/3·18=6.'},
];
const bossBox=document.getElementById('p4-boss-tasks');
bossBox.innerHTML=tasks.map((t,i)=>`
<div style="padding:14px;background:var(--card);border-radius:10px;border:1px solid var(--border);margin-bottom:10px">
<div style="margin-bottom:8px;font-size:.95rem">${t.q}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
<input type="number" class="tinp" id="p4b-a${i}" placeholder="Ответ" style="width:110px">
<button class="btn primary small" onclick="(function(){
const v=+document.getElementById('p4b-a${i}').value;
const fb=document.getElementById('p4b-fb${i}');
if(Math.abs(v-${t.ans})<0.01){
feedback(fb,true,'Верно! +5 XP');
if(!window.p4BossSolved.has(${i})){ window.p4BossSolved.add(${i}); addXp(5,'p4-boss${i}'); bumpProgress('p4',8); }
} else feedback(fb,false,'Неверно. ${t.hint}');
})()">Проверить</button>
</div>
<div class="feedback" id="p4b-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p4BossSolved=new Set();
renderMath(bossBox);
})();
}
function buildP5(){
const box=document.getElementById('p5-body');
let html='';
/* ---- Theory cards ---- */
html+=makeCard('theory','Первый признак подобия треугольников — по двум углам (УУ)','5.1',`
<p><b>Теорема (1-й признак подобия).</b> Если два угла одного треугольника соответственно равны двум углам другого треугольника, то такие треугольники подобны.</p>
<p style="margin-top:8px">Если $\\angle A = \\angle A'$ и $\\angle B = \\angle B'$, то $\\triangle ABC \\sim \\triangle A'B'C'$.</p>
<p style="margin-top:8px"><b>Почему достаточно двух углов?</b> Сумма углов треугольника равна $180°$. Если два угла равны, третий автоматически тоже равен: $\\angle C = 180° - \\angle A - \\angle B = 180° - \\angle A' - \\angle B' = \\angle C'$.</p>
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 360 160" style="max-width:380px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- большой треугольник ABC: A=(100,30), B=(30,145), C=(195,145) -->
<!-- BC=165, AB≈sqrt(70²+115²)≈135, AC≈sqrt(95²+115²)≈149 -->
<polygon points="100,30 30,145 195,145" fill="rgba(79,70,229,.11)" stroke="#4f46e5" stroke-width="2"/>
<text x="100" y="22" text-anchor="middle" font-size="11" font-weight="700" fill="#4338ca">A</text>
<text x="18" y="155" font-size="11" font-weight="700" fill="#4338ca">B</text>
<text x="198" y="155" font-size="11" font-weight="700" fill="#4338ca">C</text>
<!-- угловые метки (∠A orange, ∠B green) -->
<path d="M100,30 Q112,42 105,52" fill="none" stroke="#f59e0b" stroke-width="2"/>
<path d="M30,145 Q44,134 52,140" fill="none" stroke="#10b981" stroke-width="2"/>
<!-- малый треугольник A'B'C' (k=2.5), well separated to the right -->
<!-- B'=(240,145), C'=(306,145), A'=B'+(27,46)=(267,99) -->
<!-- B'C'=66=165/2.5✓, |A'B'|=sqrt(27²+46²)≈54=135/2.5✓ -->
<polygon points="267,99 240,145 306,145" fill="rgba(99,102,241,.15)" stroke="#6366f1" stroke-width="1.8"/>
<text x="267" y="91" text-anchor="middle" font-size="10" font-weight="700" fill="#4f46e5">A'</text>
<text x="228" y="155" font-size="10" font-weight="700" fill="#4f46e5">B'</text>
<text x="309" y="155" font-size="10" font-weight="700" fill="#4f46e5">C'</text>
<!-- угловые метки (∠A'=∠A orange, ∠B'=∠B green) -->
<path d="M267,99 Q275,108 270,116" fill="none" stroke="#f59e0b" stroke-width="1.5"/>
<path d="M240,145 Q253,135 259,140" fill="none" stroke="#10b981" stroke-width="1.5"/>
<text x="178" y="14" text-anchor="middle" font-size="9" fill="#4338ca">∠A=∠A', ∠B=∠B' → △ABC∼△A'B'C'</text>
</svg>
</div>`);
html+=makeCard('rule','Следствие: прямоугольные треугольники','5.2',`
<p><b>Следствие.</b> Два прямоугольных треугольника подобны, если у них равны острые углы (достаточно одной пары).</p>
<p style="margin-top:8px">Если $\\angle C = \\angle C' = 90°$ и $\\angle A = \\angle A'$, то $\\triangle ABC \\sim \\triangle A'B'C'$.</p>
<p style="margin-top:8px">Это потому, что прямой угол — один из двух равных углов, а второй угол задан условием.</p>
<div style="display:flex;justify-content:center;margin-top:12px">
<svg viewBox="0 0 280 130" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- прямоугольный треугольник 1: A=(40,20), B=(40,110), C=(160,110) -->
<polygon points="40,20 40,110 160,110" fill="rgba(79,70,229,.10)" stroke="#4f46e5" stroke-width="2"/>
<!-- marker угла B (прямой) — inside polygon -->
<polyline points="40,100 50,100 50,110" fill="none" stroke="#4f46e5" stroke-width="1.5"/>
<text x="34" y="16" font-size="11" font-weight="700" fill="#4338ca">A</text>
<text x="28" y="120" font-size="11" font-weight="700" fill="#4338ca">B</text>
<text x="163" y="120" font-size="11" font-weight="700" fill="#4338ca">C</text>
<text x="36" y="116" font-size="8" fill="#4f46e5">90°</text>
<!-- прямоугольный треугольник 2: A'=(185,50), B'=(185,110), C'=(250,110) -->
<polygon points="185,50 185,110 250,110" fill="rgba(99,102,241,.15)" stroke="#6366f1" stroke-width="1.8"/>
<!-- marker прямого угла B' -->
<polyline points="185,100 195,100 195,110" fill="none" stroke="#6366f1" stroke-width="1.5"/>
<text x="178" y="46" font-size="10" font-weight="700" fill="#4f46e5">A'</text>
<text x="172" y="120" font-size="10" font-weight="700" fill="#4f46e5">B'</text>
<text x="253" y="120" font-size="10" font-weight="700" fill="#4f46e5">C'</text>
<text x="140" y="14" text-anchor="middle" font-size="9" fill="#4338ca">∠B=∠B'=90°, ∠A=∠A' → подобны</text>
</svg>
</div>`);
html+=makeCard('example','Пример применения — признак по двум углам','5.3',`
<p><b>Пример 1.</b> В $\\triangle ABC$: $\\angle A = 50°$, $\\angle B = 70°$. В $\\triangle A'B'C'$: $\\angle A' = 50°$, $\\angle C' = 60°$. Подобны ли треугольники?</p>
<p style="margin-top:6px">$\\angle C = 180° - 50° - 70° = 60°$. $\\angle B' = 180° - 50° - 60° = 70°$. Углы: $50°, 70°, 60°$ в обоих. По признаку по двум углам: $\\triangle ABC \\sim \\triangle A'C'B'$.</p>
<p style="margin-top:8px"><b>Пример 2.</b> Два прямоугольных треугольника, у одного острый угол $35°$, у другого $35°$. Подобны ли?</p>
<p style="margin-top:4px">Да. Оба имеют $90°$ и $35°$ — два совпадающих угла.</p>`);
/* ---- ИНТЕРАКТИВ 1: SVG два треугольника, слайдер углов ---- */
html+=`<div class="wg" id="p5-ang-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Два угла задают форму — первый признак</div></div>
<div class="wg-help">Задай углы $\\alpha$ и $\\beta$. Оба треугольника строятся с этими углами — они подобны. Коэффициент $k$ определяется масштабом.</div>
<div class="sliders">
<label>Угол $\\alpha$: <b id="p5-a-val">50</b>°
<input type="range" min="20" max="130" value="50" id="p5-a-sl" step="1">
</label>
<label>Угол $\\beta$: <b id="p5-b-val">60</b>°
<input type="range" min="20" max="130" value="60" id="p5-b-sl" step="1">
</label>
<label>Масштаб $k$: <b id="p5-k-val">1.8</b>
<input type="range" min="12" max="30" value="18" id="p5-k-sl" step="1">
</label>
</div>
<div id="p5-ang-svg" style="display:flex;justify-content:center"></div>
<div id="p5-ang-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.92rem;line-height:1.8"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 2: Пошаговое доказательство ---- */
html+=`<div class="wg" id="p5-proof-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Доказательство 1-го признака — по шагам</div></div>
<div class="wg-help">Нажимай «Далее» — шаг за шагом увидишь логику доказательства.</div>
<div id="p5-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p5-proof-desc" style="padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.6;margin-bottom:10px;min-height:60px"></div>
<div style="display:flex;gap:8px">
<button class="btn primary" id="p5-proof-next">Далее</button>
<button class="btn" id="p5-proof-reset">Сначала</button>
</div>
</div>`;
/* ---- ИНТЕРАКТИВ 3: Тренажёр ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Тренажёр §5 — Признак по двум углам</div></div>
<div class="wg-help">5 задач на подобие по углам и нахождение сторон.</div>
<div class="score-display"><span>Задача <b id="p5-tr-i">1</b> / 5</span><span>Очки: <b id="p5-tr-score">0</b></span></div>
<div id="p5-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.02rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p5-tr-ans" class="tinp" placeholder="Ответ" style="width:120px">
<button class="btn primary" id="p5-tr-go">Проверить</button>
<button class="btn" id="p5-tr-start">Начать</button>
</div>
<div class="feedback" id="p5-tr-fb" style="display:none"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 4: DnD-сортер ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Подобные пары или нет? — Сортировка по углам</div></div>
<div class="wg-help">Перетащи каждую пару треугольников в нужную колонку.</div>
<div id="p5-dnd-pool" class="dnd-pool"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:10px">
<div class="drop-box" id="p5-drop-yes"><h5>Подобны (по двум углам)</h5><div class="drop-items" id="p5-drop-yes-items"></div></div>
<div class="drop-box" id="p5-drop-no"><h5>Не подобны</h5><div class="drop-items" id="p5-drop-no-items"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p5-dnd-check">Проверить</button><button class="btn" id="p5-dnd-reset">Сбросить</button></div>
<div class="feedback" id="p5-dnd-fb" style="display:none"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 5: Калькулятор ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Калькулятор: найти сторону через подобие по двум углам</div></div>
<div class="wg-help">Введи два угла обоих треугольников и одну сторону первого — получи соответствующую сторону второго.</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(100px,1fr));gap:8px;margin-bottom:10px">
<div><span style="font-size:.8rem;color:var(--muted);display:block">∠A₁ (°)</span><input type="number" id="p5-cA1" class="tinp" placeholder="напр. 50" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">∠B₁ (°)</span><input type="number" id="p5-cB1" class="tinp" placeholder="напр. 70" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">∠A₂ (°)</span><input type="number" id="p5-cA2" class="tinp" placeholder="напр. 50" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">∠B₂ (°)</span><input type="number" id="p5-cB2" class="tinp" placeholder="напр. 70" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">сторона a₁</span><input type="number" id="p5-ca1" class="tinp" placeholder="напр. 12" style="width:100%"></div>
<div style="display:flex;align-items:flex-end"><button class="btn primary" id="p5-ccalc" style="width:100%">Найти a₂</button></div>
</div>
<div id="p5-ccalc-out" style="display:none;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 6: Босс §5 ---- */
html+=`<div class="wg" style="border-color:var(--sec-acc-d,var(--pri2))">
<div class="wg-header"><span class="wg-badge" style="background:var(--sec-acc-d,var(--pri2))">БОСС §5</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">4 задачи — каждая верная даёт +5 XP.</div>
<div id="p5-boss-tasks"></div>
</div>`;
html+=`<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="p5-read-btn" onclick="addXp(10,'p5-read');bumpProgress('p5',40);this.textContent='Прочитано!';this.disabled=true;">
<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>
Я прочитал §5 (+10 XP)
</button>
</div>`;
html+=secNav('p4','p6');
box.innerHTML=html;
if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
/* == INIT ИНТЕРАКТИВ 1: слайдер углов == */
(function(){
const aSl=document.getElementById('p5-a-sl');
const bSl=document.getElementById('p5-b-sl');
const kSl=document.getElementById('p5-k-sl');
const aVal=document.getElementById('p5-a-val');
const bVal=document.getElementById('p5-b-val');
const kVal=document.getElementById('p5-k-val');
const svgWrap=document.getElementById('p5-ang-svg');
const infoEl=document.getElementById('p5-ang-info');
function buildTriFromAngles(alpha,beta,baseLen,ox,oy){
// alpha at A (left), beta at B (right)
// base AB horizontal, C above
const gamma=Math.PI-alpha-beta;
if(gamma<=0.05) return null;
// by sine rule: a/sin(alpha) = base/sin(gamma)
const sinG=Math.sin(gamma);
const AB=baseLen;
const BC=AB*Math.sin(alpha)/sinG;
const AC=AB*Math.sin(beta)/sinG;
// place A at (ox,oy), B at (ox+AB,oy)
const Ax=ox, Ay=oy;
const Bx=ox+AB, By=oy;
// C: from A at angle alpha above AB
const Cx=Ax+AC*Math.cos(alpha);
const Cy=Ay-AC*Math.sin(alpha);
return {Ax,Ay,Bx,By,Cx,Cy,AB,BC,AC};
}
function draw(){
const alpha=+aSl.value*Math.PI/180;
const beta=+bSl.value*Math.PI/180;
const k=+kSl.value/10;
aVal.textContent=+aSl.value;
bVal.textContent=+bSl.value;
kVal.textContent=k.toFixed(1);
const gamma=(Math.PI-alpha-beta)*180/Math.PI;
if(gamma<=3){
svgWrap.innerHTML='<div style="color:var(--bad);padding:10px">Сумма углов превышает 180°. Уменьши углы.</div>';
infoEl.innerHTML='Сумма углов не должна превышать 180°.';
return;
}
const W=360, H=170;
const t1=buildTriFromAngles(alpha,beta,90,20,145);
if(!t1){svgWrap.innerHTML='';return;}
const t2=buildTriFromAngles(alpha,beta,90/k,240,145);
if(!t2){svgWrap.innerHTML='';return;}
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
// triangle 1
s+=`<polygon points="${t1.Ax},${t1.Ay} ${t1.Bx},${t1.By} ${t1.Cx},${t1.Cy}" fill="rgba(79,70,229,.12)" stroke="#4f46e5" stroke-width="2"/>`;
s+=`<text x="${t1.Cx}" y="${t1.Cy-5}" text-anchor="middle" font-size="10" font-weight="700" fill="#4338ca">A</text>`;
s+=`<text x="${t1.Ax-8}" y="${t1.Ay+4}" font-size="10" font-weight="700" fill="#4338ca">B</text>`;
s+=`<text x="${t1.Bx+4}" y="${t1.By+4}" font-size="10" font-weight="700" fill="#4338ca">C</text>`;
// angle markers t1
s+=`<path d="M${t1.Cx},${t1.Cy} Q${t1.Cx+(t1.Ax-t1.Cx)*0.18+3},${t1.Cy+(t1.Ay-t1.Cy)*0.18} ${t1.Cx+(t1.Ax-t1.Cx)*0.22},${t1.Cy+(t1.Ay-t1.Cy)*0.22}" fill="none" stroke="#f59e0b" stroke-width="1.5"/>`;
s+=`<path d="M${t1.Ax},${t1.Ay} Q${t1.Ax+(t1.Cx-t1.Ax)*0.12+(t1.Bx-t1.Ax)*0.08},${t1.Ay+(t1.Cy-t1.Ay)*0.12} ${t1.Ax+(t1.Bx-t1.Ax)*0.15},${t1.Ay}" fill="none" stroke="#10b981" stroke-width="1.5"/>`;
// triangle 2 (smaller)
s+=`<polygon points="${t2.Ax},${t2.Ay} ${t2.Bx},${t2.By} ${t2.Cx},${t2.Cy}" fill="rgba(99,102,241,.18)" stroke="#6366f1" stroke-width="1.8"/>`;
s+=`<text x="${t2.Cx}" y="${t2.Cy-5}" text-anchor="middle" font-size="10" font-weight="700" fill="#4f46e5">A'</text>`;
s+=`<text x="${t2.Ax-10}" y="${t2.Ay+4}" font-size="10" font-weight="700" fill="#4f46e5">B'</text>`;
s+=`<text x="${t2.Bx+4}" y="${t2.By+4}" font-size="10" font-weight="700" fill="#4f46e5">C'</text>`;
// angle markers t2
s+=`<path d="M${t2.Cx},${t2.Cy} Q${t2.Cx+(t2.Ax-t2.Cx)*0.22+2},${t2.Cy+(t2.Ay-t2.Cy)*0.22} ${t2.Cx+(t2.Ax-t2.Cx)*0.28},${t2.Cy+(t2.Ay-t2.Cy)*0.28}" fill="none" stroke="#f59e0b" stroke-width="1.5"/>`;
s+=`<path d="M${t2.Ax},${t2.Ay} Q${t2.Ax+(t2.Cx-t2.Ax)*0.14+(t2.Bx-t2.Ax)*0.10},${t2.Ay+(t2.Cy-t2.Ay)*0.14} ${t2.Ax+(t2.Bx-t2.Ax)*0.18},${t2.Ay}" fill="none" stroke="#10b981" stroke-width="1.5"/>`;
// label
s+=`<text x="${W/2}" y="12" text-anchor="middle" font-size="10" font-weight="800" fill="#4338ca">k = ${k.toFixed(1)}</text>`;
s+='</svg>';
svgWrap.innerHTML=s;
infoEl.innerHTML=`$\\alpha=${+aSl.value}°$, $\\beta=${+bSl.value}°$, $\\gamma=${fmt(gamma)}°$. Оба треугольника имеют одинаковые углы → по признаку по двум углам они подобны. Коэффициент подобия $k=${k.toFixed(1)}$.`;
renderMath(infoEl);
addXp(1,'p5-ang');
}
aSl.addEventListener('input',draw);
bSl.addEventListener('input',draw);
kSl.addEventListener('input',draw);
draw();
})();
/* == INIT ИНТЕРАКТИВ 2: пошаговое доказательство == */
(function(){
const steps=[
{desc:'<b>Шаг 1.</b> Даны $\\triangle ABC$ и $\\triangle A\'B\'C\'$ с $\\angle A = \\angle A\'$ и $\\angle B = \\angle B\'$. Требуется доказать $\\triangle ABC \\sim \\triangle A\'B\'C\'$.',
svg:`<svg viewBox="0 0 320 170" style="max-width:320px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="160" y="14" text-anchor="middle" font-size="9" fill="#4338ca" font-weight="700">∠A=∠A\', ∠B=∠B\' — условие, k≈5/3</text><polygon points="82,34 22,148 168,148" fill="rgba(79,70,229,.10)" stroke="#4f46e5" stroke-width="2"/><polygon points="238,78 205,148 280,148" fill="rgba(99,102,241,.15)" stroke="#6366f1" stroke-width="1.8"/><text x="82" y="27" text-anchor="middle" font-size="11" font-weight="700" fill="#4338ca">A</text><text x="10" y="160" font-size="11" font-weight="700" fill="#4338ca">B</text><text x="170" y="160" font-size="11" font-weight="700" fill="#4338ca">C</text><text x="238" y="71" text-anchor="middle" font-size="10" font-weight="700" fill="#4f46e5">A\'</text><text x="193" y="160" font-size="10" font-weight="700" fill="#4f46e5">B\'</text><text x="282" y="160" font-size="10" font-weight="700" fill="#4f46e5">C\'</text></svg>`},
{desc:'<b>Шаг 2.</b> Из суммы углов треугольника: $\\angle C = 180° - \\angle A - \\angle B$ и $\\angle C\' = 180° - \\angle A\' - \\angle B\'$. Так как $\\angle A=\\angle A\'$ и $\\angle B=\\angle B\'$, получаем $\\angle C = \\angle C\'$.',
svg:`<svg viewBox="0 0 280 90" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="140" y="26" text-anchor="middle" font-size="11" fill="#4338ca" font-weight="700">∠A+∠B+∠C = 180°</text><text x="140" y="46" text-anchor="middle" font-size="11" fill="#4f46e5" font-weight="700">∠A'+∠B'+∠C' = 180°</text><text x="140" y="72" text-anchor="middle" font-size="11" fill="#10b981" font-weight="800">∠C = 180°−∠A−∠B = 180°−∠A'−∠B' = ∠C'</text></svg>`},
{desc:'<b>Шаг 3.</b> На луче $A\'B\'$ откладываем отрезок $A\'M = AB$. Через $M$ проводим прямую, параллельную $B\'C\'$ — она встречает $A\'C\'$ в точке $N$. По теореме §4: $\\triangle A\'MN \\sim \\triangle A\'B\'C\'$.',
svg:`<svg viewBox="0 0 280 145" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><polygon points="185,130 255,130 220,68" fill="rgba(99,102,241,.10)" stroke="#6366f1" stroke-width="1.5"/><polygon points="185,130 230,130 207,99" fill="rgba(79,70,229,.22)" stroke="#4f46e5" stroke-width="2"/><line x1="207" y1="99" x2="230" y2="130" stroke="#4f46e5" stroke-width="1.5" stroke-dasharray="4,3"/><text x="218" y="63" font-size="10" font-weight="700" fill="#4f46e5">A'</text><text x="177" y="140" font-size="10" font-weight="700" fill="#4338ca">B'</text><text x="257" y="140" font-size="10" font-weight="700" fill="#4338ca">C'</text><text x="228" y="138" font-size="9" font-weight="700" fill="#4f46e5">M</text><text x="209" y="97" font-size="9" font-weight="700" fill="#4f46e5">N</text><text x="50" y="80" font-size="9" fill="#4338ca">△A'MN∼△A'B'C'</text></svg>`},
{desc:'<b>Шаг 4.</b> Поскольку $A\'M = AB$ и углы треугольников $\\triangle A\'MN$ и $\\triangle ABC$ попарно равны (все три угла), а $\\angle A\' = \\angle A$ — вершины совпадают, то $\\triangle A\'MN \\cong \\triangle ABC$ (по условию и построению).',
svg:`<svg viewBox="0 0 280 90" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="140" y="26" text-anchor="middle" font-size="11" fill="#4338ca" font-weight="700">A'M = AB, ∠A'=∠A, ∠B'=∠B</text><text x="140" y="46" text-anchor="middle" font-size="11" fill="#4f46e5" font-weight="700">→ △A'MN ≅ △ABC (признак у-с-у)</text><text x="140" y="70" text-anchor="middle" font-size="10" fill="#6366f1">Следовательно MN = BC, A'N = AC</text></svg>`},
{desc:'<b>Шаг 5.</b> Итог: $\\triangle A\'MN \\cong \\triangle ABC$ и $\\triangle A\'MN \\sim \\triangle A\'B\'C\'$ — отсюда $\\triangle ABC \\sim \\triangle A\'B\'C\'$ (транзитивность подобия). <b>Первый признак подобия доказан.</b>',
svg:`<svg viewBox="0 0 280 80" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="140" y="26" text-anchor="middle" font-size="12" fill="#4338ca" font-weight="800">∠A=∠A', ∠B=∠B' → △ABC △A'B'C'</text><text x="140" y="50" text-anchor="middle" font-size="10" fill="#4f46e5">Признак по двум углам — первый признак подобия</text><text x="140" y="70" text-anchor="middle" font-size="10" fill="#10b981" font-weight="800">QED ∎</text></svg>`},
];
let step=0;
const svgEl=document.getElementById('p5-proof-svg');
const descEl=document.getElementById('p5-proof-desc');
function show(){
svgEl.innerHTML=steps[step].svg;
descEl.innerHTML=steps[step].desc;
renderMath(descEl);
}
document.getElementById('p5-proof-next').addEventListener('click',()=>{
if(step<steps.length-1){step++;show();addXp(1,'p5-proof-step');}
else{addXp(5,'p5-proof-done');bumpProgress('p5',10);}
});
document.getElementById('p5-proof-reset').addEventListener('click',()=>{step=0;show();});
show();
})();
/* == INIT ИНТЕРАКТИВ 3: тренажёр == */
(function(){
const tasks=[
{q:'В $\\triangle ABC$: $\\angle A=40°$, $\\angle B=70°$. В $\\triangle A\'B\'C\'$: $\\angle A\'=40°$, $\\angle B\'=70°$. Чему равен $\\angle C\'$?',ans:70,hint:'∠C\'=1804070=70°.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$ по признаку по двум углам. $AB=12$, $A\'B\'=4$. Найди коэффициент подобия $k$.',ans:3,hint:'k=AB/A\'B\'=12/4=3.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$, $k=2.5$. Сторона $a\'=6$. Найди $a$.',ans:15,hint:'a=k·a\'=2.5·6=15.'},
{q:'Два прямоугольных треугольника. В первом острый угол $37°$, во втором $37°$. Сторона при прямом угле в первом — $10$, во втором — $6$. Найди коэффициент подобия.',ans:1.667,hint:'k=10/6≈1.667.'},
{q:'$\\angle A=\\angle A\'=55°$, $\\angle B=\\angle B\'=65°$. Сторона $c=18$ в $\\triangle ABC$, $c\'=9$ в $\\triangle A\'B\'C\'$. Найди $k$.',ans:2,hint:'k=c/c\'=18/9=2.'},
];
let idx=0,score=0;
function show(){
document.getElementById('p5-tr-i').textContent=idx+1;
const t=document.getElementById('p5-tr-task');
t.innerHTML=tasks[idx].q;
renderMath(t);
document.getElementById('p5-tr-ans').value='';
document.getElementById('p5-tr-fb').style.display='none';
}
document.getElementById('p5-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p5-tr-score').textContent=0;show();});
document.getElementById('p5-tr-go').addEventListener('click',()=>{
if(idx>=tasks.length)return;
const ans=+document.getElementById('p5-tr-ans').value;
const fb=document.getElementById('p5-tr-fb');
if(Math.abs(ans-tasks[idx].ans)<0.02){
score++;document.getElementById('p5-tr-score').textContent=score;
addXp(3,'p5-tr-'+idx);bumpProgress('p5',4);
if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}
else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p5-tr-all');bumpProgress('p5',10);confetti();}
} else {
feedback(fb,false,'Неверно. '+tasks[idx].hint);
}
});
document.getElementById('p5-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p5-tr-go').click();});
show();
})();
/* == INIT ИНТЕРАКТИВ 4: DnD-сортер == */
(function(){
const items=[
{text:'(50°,70°,60°) и (50°,70°,60°)',yes:true},
{text:'(40°,60°,80°) и (40°,50°,90°)',yes:false},
{text:'(90°,35°,55°) и (90°,35°,55°)',yes:true},
{text:'(30°,60°,90°) и (45°,45°,90°)',yes:false},
{text:'(80°,60°,40°) и (80°,40°,60°)',yes:true},
];
const pool=document.getElementById('p5-dnd-pool');
const yesBox=document.getElementById('p5-drop-yes-items');
const noBox=document.getElementById('p5-drop-no-items');
let dragging=null;
function makeChip(it,idx){
const chip=document.createElement('div');
chip.className='dnd-chip';
chip.dataset.idx=idx;
chip.textContent=it.text;
chip.draggable=true;
chip.addEventListener('dragstart',e=>{dragging=chip;chip.classList.add('dragging');e.dataTransfer.effectAllowed='move';});
chip.addEventListener('dragend',()=>{chip.classList.remove('dragging');dragging=null;});
return chip;
}
items.forEach((it,i)=>pool.appendChild(makeChip(it,i)));
[document.getElementById('p5-drop-yes'),document.getElementById('p5-drop-no'),pool].forEach(box=>{
box.addEventListener('dragover',e=>{e.preventDefault();box.classList.add('over');});
box.addEventListener('dragleave',()=>box.classList.remove('over'));
box.addEventListener('drop',e=>{
e.preventDefault();box.classList.remove('over');
if(!dragging)return;
const target=box===document.getElementById('p5-drop-yes')?yesBox:box===document.getElementById('p5-drop-no')?noBox:pool;
target.appendChild(dragging);
});
});
document.getElementById('p5-dnd-check').addEventListener('click',()=>{
const fb=document.getElementById('p5-dnd-fb');
const yChips=[...yesBox.querySelectorAll('.dnd-chip')];
const nChips=[...noBox.querySelectorAll('.dnd-chip')];
if(yChips.length+nChips.length<items.length){feedback(fb,false,'Разложи все карточки.');return;}
let ok=true;
yChips.forEach(c=>{if(!items[+c.dataset.idx].yes)ok=false;});
nChips.forEach(c=>{if(items[+c.dataset.idx].yes)ok=false;});
if(ok){feedback(fb,true,'Верно! +5 XP');addXp(5,'p5-dnd');bumpProgress('p5',8);}
else{feedback(fb,false,'Есть ошибки. Два треугольника подобны, если у них есть хотя бы две пары равных углов.');}
});
document.getElementById('p5-dnd-reset').addEventListener('click',()=>{
[...yesBox.children].forEach(c=>pool.appendChild(c));
[...noBox.children].forEach(c=>pool.appendChild(c));
document.getElementById('p5-dnd-fb').style.display='none';
});
})();
/* == INIT ИНТЕРАКТИВ 5: калькулятор == */
(function(){
document.getElementById('p5-ccalc').addEventListener('click',()=>{
const A1=parseFloat(document.getElementById('p5-cA1').value);
const B1=parseFloat(document.getElementById('p5-cB1').value);
const A2=parseFloat(document.getElementById('p5-cA2').value);
const B2=parseFloat(document.getElementById('p5-cB2').value);
const a1=parseFloat(document.getElementById('p5-ca1').value);
const out=document.getElementById('p5-ccalc-out');
if([A1,B1,A2,B2,a1].some(v=>!isFinite(v)||v<=0)){
out.style.display='block';out.innerHTML='<span style="color:var(--bad)">Введи все поля.</span>';return;
}
const C1=180-A1-B1, C2=180-A2-B2;
if(C1<=0||C2<=0){
out.style.display='block';out.innerHTML='<span style="color:var(--bad)">Сумма углов превышает 180°.</span>';return;
}
// check similarity: sort angles and compare
const ang1=[A1,B1,C1].sort((a,b)=>a-b);
const ang2=[A2,B2,C2].sort((a,b)=>a-b);
const similar=ang1.every((v,i)=>Math.abs(v-ang2[i])<0.5);
if(!similar){
out.style.display='block';out.innerHTML=`$\\angle C_1=${fmt(C1)}°$, $\\angle C_2=${fmt(C2)}°$. Наборы углов различаются — треугольники <b>не подобны</b>. Признак по двум углам не выполнен.`;
renderMath(out);return;
}
// find matching side using sine rule: a/sin(A)=b/sin(B)
// k = a1 / (2R sin(A)) ... use ratio of sides
// match angles: find which angle in t2 corresponds to A1
// simplification: by ratio of corresponding sides via sine rule
// a1/sin(A1) = a2/sin(A2) → a2 = a1*sin(A2*pi/180)/sin(A1*pi/180)
const a2=a1*Math.sin(A2*Math.PI/180)/Math.sin(A1*Math.PI/180);
const k=a1/a2;
out.style.display='block';
out.innerHTML=`Треугольники подобны по признаку по двум углам.<br>$\\angle C_1 = ${fmt(C1)}°$, $\\angle C_2 = ${fmt(C2)}°$.<br>По теореме синусов: $a_2 = a_1 \\cdot \\dfrac{\\sin \\angle A_2}{\\sin \\angle A_1} = ${fmt(a1)} \\cdot \\dfrac{${fmt(Math.sin(A2*Math.PI/180).toFixed(4))}}{${fmt(Math.sin(A1*Math.PI/180).toFixed(4))}} = ${fmt(a2)}$.<br>Коэффициент подобия $k = a_1/a_2 = ${fmt(k)}$.`;
renderMath(out);
addXp(3,'p5-calc');bumpProgress('p5',5);
});
})();
/* == INIT: Босс §5 == */
(function(){
const tasks=[
{q:'<svg viewBox="0 0 240 140" style="display:block;max-width:240px;margin:0 auto 8px;background:#e0e7ff;border:1px solid #a5b4fc;border-radius:8px"><polygon points="100,30 50,125 165,125" fill="rgba(79,70,229,.12)" stroke="#4f46e5" stroke-width="2"/><polygon points="192,93 175,125 213,125" fill="rgba(99,102,241,.18)" stroke="#6366f1" stroke-width="1.8"/><text x="100" y="24" text-anchor="middle" font-size="10" fill="#4338ca" font-weight="700">A</text><text x="41" y="134" font-size="10" fill="#4338ca" font-weight="700">B</text><text x="167" y="134" font-size="10" fill="#4338ca" font-weight="700">C</text><text x="192" y="87" text-anchor="middle" font-size="9" fill="#4f46e5" font-weight="700">A\'</text><text x="168" y="134" font-size="9" fill="#4f46e5" font-weight="700">B\'</text><text x="215" y="134" font-size="9" fill="#4f46e5" font-weight="700">C\'</text><text x="120" y="12" text-anchor="middle" font-size="8" fill="#4338ca">∠A=∠A\'=60°, ∠B=∠B\'=80°, k=3</text></svg>$\\angle A=\\angle A\'=60°$, $\\angle B=\\angle B\'=80°$. $AB=15$, $A\'B\'=5$. Найди $k$.',ans:3,hint:'k=AB/A\'B\'=15/5=3.'},
{q:'Два прямоугольных треугольника. Острый угол одного $42°$, другого $42°$. Подобны ли? Гипотенуза первого $13$, второго $6.5$. Найди $k$.',ans:2,hint:'Подобны по двум углам. k=13/6.5=2.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$ по двум углам. $k=4$. Периметр $\\triangle A\'B\'C\'=18$. Найди периметр $\\triangle ABC$.',ans:72,hint:'P=k·P\'=4·18=72.'},
{q:'В $\\triangle ABC$ и $\\triangle DEF$: $\\angle A=\\angle D=55°$, $\\angle B=\\angle E=75°$. $BC=20$, $EF=8$. Найди $k=BC/EF$.',ans:2.5,hint:'k=20/8=2.5.'},
];
const bossBox=document.getElementById('p5-boss-tasks');
bossBox.innerHTML=tasks.map((t,i)=>`
<div style="padding:14px;background:var(--card);border-radius:10px;border:1px solid var(--border);margin-bottom:10px">
<div style="margin-bottom:8px;font-size:.95rem">${t.q}</div>
<div style="display:flex;gap:8px;flex-wrap:wrap;align-items:center">
<input type="number" class="tinp" id="p5b-a${i}" placeholder="Ответ" style="width:110px">
<button class="btn primary small" onclick="(function(){
const v=+document.getElementById('p5b-a${i}').value;
const fb=document.getElementById('p5b-fb${i}');
if(Math.abs(v-${t.ans})<0.02){
feedback(fb,true,'Верно! +5 XP');
if(!window.p5BossSolved.has(${i})){ window.p5BossSolved.add(${i}); addXp(5,'p5-boss${i}'); bumpProgress('p5',8); }
} else feedback(fb,false,'Неверно. ${t.hint}');
})()">Проверить</button>
</div>
<div class="feedback" id="p5b-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p5BossSolved=new Set();
renderMath(bossBox);
})();
}
function buildP6(){
const box=document.getElementById('p6-body');
let html='';
/* ---- Theory cards ---- */
html+=makeCard('theory','Второй признак подобия треугольников — по двум сторонам и углу (СУС — сторона-угол-сторона)','6.1',`
<p><b>Теорема (2-й признак подобия — по двум сторонам и углу, СУС).</b> Если две стороны одного треугольника пропорциональны двум сторонам другого треугольника, а углы между этими сторонами равны, то такие треугольники подобны.</p>
<p style="margin-top:8px">Если $\\dfrac{AB}{A'B'} = \\dfrac{AC}{A'C'} = k$ и $\\angle A = \\angle A'$, то $\\triangle ABC \\sim \\triangle A'B'C'$.</p>
<p style="margin-top:8px">Это означает: двух пропорциональных сторон и равного угла между ними <b>достаточно</b> для подобия — третью сторону и остальные углы можно не проверять.</p>
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 300 192" style="max-width:320px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Triangle 1: A=(100,35), B=(30,155), C=(195,155) -->
<!-- AB≈138.9, AC≈153.1, ∠A≈68.6° -->
<polygon points="100,35 30,155 195,155" fill="rgba(124,58,237,.11)" stroke="#7c3aed" stroke-width="2"/>
<!-- Angle mark at A -->
<path d="M88,51 Q100,59 112,52" fill="none" stroke="#f59e0b" stroke-width="2"/>
<!-- Vertex labels outside polygon -->
<text x="100" y="27" text-anchor="middle" font-size="11" font-weight="800" fill="#6d28d9">A</text>
<text x="18" y="165" font-size="11" font-weight="800" fill="#6d28d9">B</text>
<text x="198" y="165" font-size="11" font-weight="800" fill="#6d28d9">C</text>
<!-- Side labels -->
<text x="57" y="100" text-anchor="end" font-size="10" fill="#7c3aed" font-style="italic">AB</text>
<text x="155" y="98" font-size="10" fill="#7c3aed" font-style="italic">AC</text>
<!-- Triangle 2 (k=2): A'=(235,73), B'=(200,133), C'=(283,133) -->
<polygon points="235,73 200,133 283,133" fill="rgba(99,102,241,.18)" stroke="#6366f1" stroke-width="1.8"/>
<!-- Angle mark at A' -->
<path d="M226,87 Q235,93 244,88" fill="none" stroke="#f59e0b" stroke-width="1.8"/>
<text x="235" y="65" text-anchor="middle" font-size="10" font-weight="800" fill="#4f46e5">A'</text>
<text x="188" y="143" font-size="10" font-weight="800" fill="#4f46e5">B'</text>
<text x="285" y="143" font-size="10" font-weight="800" fill="#4f46e5">C'</text>
<text x="212" y="107" text-anchor="middle" font-size="9" fill="#6366f1" font-style="italic">A'B'</text>
<text x="263" y="102" font-size="9" fill="#6366f1" font-style="italic">A'C'</text>
<!-- Equal angle and ratio labels moved to bottom -->
<text x="150" y="170" text-anchor="middle" font-size="9" fill="#f59e0b" font-weight="800">∠A = ∠A' (равны)</text>
<text x="150" y="181" text-anchor="middle" font-size="8" fill="#7c3aed">AB/A'B' = AC/A'C' = k=2 → подобны</text>
</svg>
</div>`);
html+=makeCard('rule','Доказательство 2-го признака — схема','6.2',`
<p>На луче $A'B'$ откладываем $A'M = AB$. Через $M$ проводим прямую $MN \\parallel B'C'$, встречающую $A'C'$ в точке $N$.</p>
<p style="margin-top:6px">По теореме о прямой, параллельной стороне (§4): $\\dfrac{A'M}{A'B'} = \\dfrac{A'N}{A'C'}$, то есть $A'N = A'C' \\cdot \\dfrac{A'M}{A'B'} = A'C' \\cdot \\dfrac{AB}{A'B'} = AC$.</p>
<p style="margin-top:6px">Итак $A'M=AB$, $A'N=AC$, $\\angle A' = \\angle A$ → по признаку SAS: $\\triangle A'MN \\cong \\triangle ABC$.</p>
<p style="margin-top:6px">Также $\\triangle A'MN \\sim \\triangle A'B'C'$ (§4). Значит $\\triangle ABC \\sim \\triangle A'B'C'$. <b>QED.</b></p>
<div style="display:flex;justify-content:center;margin-top:12px">
<svg viewBox="0 0 280 140" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- large triangle A'B'C': A'=(140,15) B'=(30,130) C'=(250,130) -->
<polygon points="140,15 30,130 250,130" fill="rgba(99,102,241,.10)" stroke="#6366f1" stroke-width="1.5"/>
<!-- inner triangle A'MN: M on A'B', N on A'C' -->
<!-- M on A'B' at ratio 0.55: M=(140+(30-140)*0.55, 15+(130-15)*0.55)=(79.5, 78.25) -->
<!-- N on A'C' at ratio 0.55: N=(140+(250-140)*0.55, 15+(130-15)*0.55)=(200.5, 78.25) -->
<polygon points="140,15 80,78 200,78" fill="rgba(124,58,237,.22)" stroke="#7c3aed" stroke-width="2"/>
<line x1="80" y1="78" x2="200" y2="78" stroke="#7c3aed" stroke-width="2" stroke-dasharray="5,3"/>
<text x="140" y="10" text-anchor="middle" font-size="10" font-weight="800" fill="#4f46e5">A'</text>
<text x="20" y="138" font-size="10" font-weight="800" fill="#4f46e5">B'</text>
<text x="252" y="138" font-size="10" font-weight="800" fill="#4f46e5">C'</text>
<text x="74" y="88" font-size="10" font-weight="800" fill="#7c3aed">M</text>
<text x="202" y="88" font-size="10" font-weight="800" fill="#7c3aed">N</text>
<text x="140" y="125" text-anchor="middle" font-size="8" fill="#6366f1">MN ∥ B'C'</text>
<text x="30" y="14" font-size="8" fill="#7c3aed">△A'MN≅△ABC и △A'MN∼△A'B'C'</text>
</svg>
</div>`);
html+=makeCard('example','Примеры применения признака по двум сторонам и углу (СУС)','6.3',`
<p><b>Пример 1.</b> $AB=6$, $AC=9$, $A'B'=4$, $A'C'=6$, $\\angle A=\\angle A'=50°$. Подобны ли треугольники?</p>
<p style="margin-top:4px">$\\dfrac{AB}{A'B'}=\\dfrac{6}{4}=1{,}5$; $\\dfrac{AC}{A'C'}=\\dfrac{9}{6}=1{,}5$. Отношения равны, углы между ними равны → по двум сторонам и углу (СУС): $\\triangle ABC \\sim \\triangle A'B'C'$, $k=1{,}5$.</p>
<p style="margin-top:8px"><b>Пример 2.</b> $PQ=10$, $PR=15$, $P'Q'=6$, $P'R'=9$, $\\angle P=\\angle P'$. $PQ/P'Q'=10/6=5/3$, $PR/P'R'=15/9=5/3$. Подобны ($k=5/3$).</p>
<p style="margin-top:8px"><b>Пример 3 (не подобны).</b> $AB=8$, $AC=6$, $A'B'=4$, $A'C'=5$, $\\angle A=\\angle A'$. $AB/A'B'=2$, $AC/A'C'=1{,}2$ — отношения неравны → <b>не подобны</b>.</p>`);
/* ---- ИНТЕРАКТИВ 1: SVG два треугольника, слайдер k ---- */
html+=`<div class="wg" id="p6-svg-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Два треугольника — признак СУС</div></div>
<div class="wg-help">Задай стороны $AB$, $AC$ и угол $\\angle A$ первого треугольника. Второй строится автоматически с тем же углом и пропорциональными сторонами. Меняй коэффициент $k$.</div>
<div class="sliders">
<label>Сторона $AB$: <b id="p6-ab-val">8</b>
<input type="range" min="4" max="14" value="8" id="p6-ab-sl" step="1">
</label>
<label>Сторона $AC$: <b id="p6-ac-val">10</b>
<input type="range" min="4" max="14" value="10" id="p6-ac-sl" step="1">
</label>
<label>Угол $\\angle A$ (°): <b id="p6-ang-val">55</b>°
<input type="range" min="20" max="140" value="55" id="p6-ang-sl" step="1">
</label>
<label>Коэффициент $k$: <b id="p6-k-val">2.0</b>
<input type="range" min="12" max="35" value="20" id="p6-k-sl" step="1">
</label>
</div>
<div id="p6-svg-out" style="display:flex;justify-content:center"></div>
<div id="p6-svg-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.92rem;line-height:1.8"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 2: Пошаговое доказательство ---- */
html+=`<div class="wg" id="p6-proof-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Доказательство 2-го признака — по шагам</div></div>
<div class="wg-help">Нажимай «Далее» для каждого шага доказательства.</div>
<div id="p6-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p6-proof-desc" style="padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.6;margin-bottom:10px;min-height:60px"></div>
<div style="display:flex;gap:8px">
<button class="btn primary" id="p6-proof-next">Далее</button>
<button class="btn" id="p6-proof-reset">Сначала</button>
</div>
</div>`;
/* ---- ИНТЕРАКТИВ 3: Калькулятор СУС ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Калькулятор признака СУС</div></div>
<div class="wg-help">Введи $AB, AC, \\angle A$ первого треугольника и коэффициент $k$ — получи стороны и угол второго. Или введи стороны двух треугольников и угол — система проверит подобие.</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(110px,1fr));gap:8px;margin-bottom:10px">
<div><span style="font-size:.8rem;color:var(--muted);display:block">AB</span><input type="number" id="p6-cAB" class="tinp" placeholder="напр. 6" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">AC</span><input type="number" id="p6-cAC" class="tinp" placeholder="напр. 9" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">∠A (°)</span><input type="number" id="p6-cAng" class="tinp" placeholder="напр. 55" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">k</span><input type="number" id="p6-ck" class="tinp" placeholder="напр. 1.5" style="width:100%"></div>
<div style="display:flex;align-items:flex-end"><button class="btn primary" id="p6-ccalc" style="width:100%">Найти</button></div>
</div>
<div id="p6-ccalc-out" style="display:none;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.7"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 4: Тренажёр ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр §6 — Признак по двум сторонам и углу (СУС)</div></div>
<div class="wg-help">5 задач на подобие по признаку по двум сторонам и углу.</div>
<div class="score-display"><span>Задача <b id="p6-tr-i">1</b> / 5</span><span>Очки: <b id="p6-tr-score">0</b></span></div>
<div id="p6-tr-task" style="padding:14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:1.02rem;margin-bottom:10px"></div>
<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
<input type="number" id="p6-tr-ans" class="tinp" placeholder="Ответ" style="width:130px">
<button class="btn primary" id="p6-tr-go">Проверить</button>
<button class="btn" id="p6-tr-start">Начать</button>
</div>
<div class="feedback" id="p6-tr-fb" style="display:none"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 5: DnD-сортер ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Подобны по двум сторонам и углу (СУС) или нет? — Сортировка</div></div>
<div class="wg-help">Перетащи каждую пару в нужную колонку.</div>
<div id="p6-dnd-pool" class="dnd-pool"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:10px">
<div class="drop-box" id="p6-drop-yes"><h5>Подобны (по двум сторонам и углу)</h5><div class="drop-items" id="p6-drop-yes-items"></div></div>
<div class="drop-box" id="p6-drop-no"><h5>Не подобны</h5><div class="drop-items" id="p6-drop-no-items"></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>`;
/* ---- ИНТЕРАКТИВ 6: Босс §6 ---- */
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))">БОСС §6</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">4 задачи — каждая верная даёт +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;
if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
/* == INIT ИНТЕРАКТИВ 1: SVG слайдеры СУС == */
(function(){
const abSl=document.getElementById('p6-ab-sl');
const acSl=document.getElementById('p6-ac-sl');
const angSl=document.getElementById('p6-ang-sl');
const kSl=document.getElementById('p6-k-sl');
const abVal=document.getElementById('p6-ab-val');
const acVal=document.getElementById('p6-ac-val');
const angVal=document.getElementById('p6-ang-val');
const kVal=document.getElementById('p6-k-val');
const svgOut=document.getElementById('p6-svg-out');
const infoEl=document.getElementById('p6-svg-info');
function triFromSAS(ab,ac,angRad,ox,oy){
const Ax=ox, Ay=oy;
const Bx=ox+ab, By=oy;
const Cx=ox+ac*Math.cos(angRad);
const Cy=oy-ac*Math.sin(angRad);
return {Ax,Ay,Bx,By,Cx,Cy};
}
function draw(){
const ab=+abSl.value;
const ac=+acSl.value;
const angDeg=+angSl.value;
const k=+kSl.value/10;
abVal.textContent=ab;
acVal.textContent=ac;
angVal.textContent=angDeg;
kVal.textContent=k.toFixed(1);
const scale=14;
const angRad=angDeg*Math.PI/180;
const W=380, H=180;
const t1=triFromSAS(ab*scale,ac*scale,angRad,24,155);
const ab2=ab/k, ac2=ac/k;
const t2=triFromSAS(ab2*scale,ac2*scale,angRad,220,155);
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
s+=`<polygon points="${t1.Ax},${t1.Ay} ${t1.Bx},${t1.By} ${t1.Cx},${t1.Cy}" fill="rgba(124,58,237,.12)" stroke="#7c3aed" stroke-width="2"/>`;
s+=`<text x="${t1.Ax-3}" y="${t1.Ay+4}" text-anchor="end" font-size="11" font-weight="800" fill="#6d28d9">A</text>`;
s+=`<text x="${t1.Bx+4}" y="${t1.By+4}" font-size="11" font-weight="800" fill="#6d28d9">B</text>`;
s+=`<text x="${t1.Cx}" y="${t1.Cy-5}" text-anchor="middle" font-size="11" font-weight="800" fill="#6d28d9">C</text>`;
const mABx1=(t1.Ax+t1.Bx)/2, mABx1y=(t1.Ay+t1.By)/2;
const mACx1=(t1.Ax+t1.Cx)/2, mACy1=(t1.Ay+t1.Cy)/2;
s+=`<text x="${mABx1}" y="${mABx1y+12}" text-anchor="middle" font-size="9" fill="#7c3aed">${ab}</text>`;
s+=`<text x="${mACx1-8}" y="${mACy1}" text-anchor="end" font-size="9" fill="#7c3aed">${ac}</text>`;
s+=`<path d="M${t1.Ax+14},${t1.Ay} Q${t1.Ax+10+8*Math.cos(angRad/2)},${t1.Ay-8*Math.sin(angRad/2)} ${t1.Ax+8*Math.cos(angRad)},${t1.Ay-8*Math.sin(angRad)}" fill="none" stroke="#f59e0b" stroke-width="1.8"/>`;
s+=`<polygon points="${t2.Ax},${t2.Ay} ${t2.Bx},${t2.By} ${t2.Cx},${t2.Cy}" fill="rgba(99,102,241,.18)" stroke="#6366f1" stroke-width="1.8"/>`;
s+=`<text x="${t2.Ax-3}" y="${t2.Ay+4}" text-anchor="end" font-size="10" font-weight="800" fill="#4f46e5">A'</text>`;
s+=`<text x="${t2.Bx+4}" y="${t2.By+4}" font-size="10" font-weight="800" fill="#4f46e5">B'</text>`;
s+=`<text x="${t2.Cx}" y="${t2.Cy-5}" text-anchor="middle" font-size="10" font-weight="800" fill="#4f46e5">C'</text>`;
const mABx2=(t2.Ax+t2.Bx)/2, mABy2=(t2.Ay+t2.By)/2;
const mACx2=(t2.Ax+t2.Cx)/2, mACy2=(t2.Ay+t2.Cy)/2;
s+=`<text x="${mABx2}" y="${mABy2+12}" text-anchor="middle" font-size="9" fill="#6366f1">${fmt(ab2)}</text>`;
s+=`<text x="${mACx2-6}" y="${mACy2}" text-anchor="end" font-size="9" fill="#6366f1">${fmt(ac2)}</text>`;
s+=`<path d="M${t2.Ax+10},${t2.Ay} Q${t2.Ax+8+6*Math.cos(angRad/2)},${t2.Ay-6*Math.sin(angRad/2)} ${t2.Ax+6*Math.cos(angRad)},${t2.Ay-6*Math.sin(angRad)}" fill="none" stroke="#f59e0b" stroke-width="1.5"/>`;
s+=`<text x="${W/2}" y="12" text-anchor="middle" font-size="10" font-weight="800" fill="#7c3aed">k = ${k.toFixed(1)}</text>`;
s+='</svg>';
svgOut.innerHTML=s;
infoEl.innerHTML=`$AB=${ab}$, $AC=${ac}$, $\\angle A=${angDeg}°$. Второй треугольник: $A'B'=${fmt(ab2)}$, $A'C'=${fmt(ac2)}$, $\\angle A'=${angDeg}°$. Отношение сторон $k=${k.toFixed(1)}$ — треугольники подобны по признаку по двум сторонам и углу (СУС).`;
renderMath(infoEl);
addXp(1,'p6-svg');
}
abSl.addEventListener('input',draw);
acSl.addEventListener('input',draw);
angSl.addEventListener('input',draw);
kSl.addEventListener('input',draw);
draw();
})();
/* == INIT ИНТЕРАКТИВ 2: пошаговое доказательство == */
(function(){
const steps=[
{desc:'<b>Шаг 1.</b> Дано: $\\dfrac{AB}{A\'B\'} = \\dfrac{AC}{A\'C\'} = k$ и $\\angle A = \\angle A\'$. Нужно доказать: $\\triangle ABC \\sim \\triangle A\'B\'C\'$.',
svg:`<svg viewBox="0 0 330 165" style="max-width:330px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="165" y="13" text-anchor="middle" font-size="9" fill="#f59e0b" font-weight="800">AB/A\'B\'=AC/A\'C\'=k=2, ∠A=∠A\'</text><polygon points="88,24 22,142 198,142" fill="rgba(124,58,237,.10)" stroke="#7c3aed" stroke-width="2"/><polygon points="265,68 237,130 312,130" fill="rgba(99,102,241,.18)" stroke="#6366f1" stroke-width="1.8"/><text x="88" y="17" text-anchor="middle" font-size="11" font-weight="800" fill="#6d28d9">A</text><text x="10" y="153" font-size="11" font-weight="800" fill="#6d28d9">B</text><text x="200" y="153" font-size="11" font-weight="800" fill="#6d28d9">C</text><text x="265" y="61" text-anchor="middle" font-size="10" font-weight="800" fill="#4f46e5">A\'</text><text x="225" y="142" font-size="10" font-weight="800" fill="#4f46e5">B\'</text><text x="314" y="142" font-size="10" font-weight="800" fill="#4f46e5">C\'</text></svg>`},
{desc:'<b>Шаг 2.</b> На луче $A\'B\'$ откладываем точку $M$ так, что $A\'M = AB$. Через $M$ проводим прямую $MN \\parallel B\'C\'$, где $N$ на луче $A\'C\'$.',
svg:`<svg viewBox="0 0 280 140" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><polygon points="140,15 30,130 250,130" fill="rgba(99,102,241,.10)" stroke="#6366f1" stroke-width="1.5"/><polygon points="140,15 80,78 200,78" fill="rgba(124,58,237,.22)" stroke="#7c3aed" stroke-width="2"/><line x1="80" y1="78" x2="200" y2="78" stroke="#7c3aed" stroke-width="2" stroke-dasharray="5,3"/><text x="140" y="10" text-anchor="middle" font-size="10" font-weight="800" fill="#4f46e5">A'</text><text x="20" y="138" font-size="10" font-weight="800" fill="#4f46e5">B'</text><text x="252" y="138" font-size="10" font-weight="800" fill="#4f46e5">C'</text><text x="74" y="88" font-size="10" font-weight="800" fill="#7c3aed">M</text><text x="202" y="88" font-size="10" font-weight="800" fill="#7c3aed">N</text><text x="140" y="110" text-anchor="middle" font-size="9" fill="#7c3aed">A'M=AB, MN∥B'C'</text></svg>`},
{desc:'<b>Шаг 3.</b> По теореме о прямой, параллельной стороне (§4): $\\dfrac{A\'M}{A\'B\'} = \\dfrac{A\'N}{A\'C\'}$. Так как $A\'M = AB$ и $\\dfrac{AB}{A\'B\'} = k$, получаем $A\'N = A\'C\' \\cdot \\dfrac{AB}{A\'B\'}= AC$.',
svg:`<svg viewBox="0 0 280 90" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="140" y="22" text-anchor="middle" font-size="11" fill="#4f46e5" font-weight="700">A'M/A'B' = A'N/A'C' (§4, пропорция)</text><text x="140" y="44" text-anchor="middle" font-size="11" fill="#7c3aed" font-weight="700">A'M=AB → A'N=AC</text><text x="140" y="70" text-anchor="middle" font-size="10" fill="#10b981" font-weight="800">Итак: A'M=AB, A'N=AC, ∠A'=∠A</text></svg>`},
{desc:'<b>Шаг 4.</b> Теперь: $A\'M = AB$, $A\'N = AC$, $\\angle A\' = \\angle A$. По признаку равенства треугольников (SAS): $\\triangle A\'MN \\cong \\triangle ABC$. Кроме того, $\\triangle A\'MN \\sim \\triangle A\'B\'C\'$ (§4).',
svg:`<svg viewBox="0 0 280 80" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="140" y="22" text-anchor="middle" font-size="11" fill="#4338ca" font-weight="700">△A'MN ≅ △ABC (SAS)</text><text x="140" y="44" text-anchor="middle" font-size="11" fill="#7c3aed" font-weight="700">△A'MN △A'B'C' (§4)</text><text x="140" y="66" text-anchor="middle" font-size="10" fill="#6366f1">По транзитивности подобия:</text></svg>`},
{desc:'<b>Шаг 5.</b> По транзитивности: $\\triangle ABC \\cong \\triangle A\'MN \\sim \\triangle A\'B\'C\'$ → $\\triangle ABC \\sim \\triangle A\'B\'C\'$. <b>Второй признак подобия (по двум сторонам и углу, СУС) доказан.</b>',
svg:`<svg viewBox="0 0 280 80" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="140" y="26" text-anchor="middle" font-size="12" fill="#7c3aed" font-weight="800">AB/A'B'=AC/A'C'=k, ∠A=∠A' → △ABC∼△A'B'C'</text><text x="140" y="50" text-anchor="middle" font-size="10" fill="#6366f1">Признак по двум сторонам и углу (СУС) — второй признак подобия</text><text x="140" y="70" text-anchor="middle" font-size="12" fill="#10b981" font-weight="900">QED ∎</text></svg>`},
];
let step=0;
const svgEl=document.getElementById('p6-proof-svg');
const descEl=document.getElementById('p6-proof-desc');
function show(){svgEl.innerHTML=steps[step].svg;descEl.innerHTML=steps[step].desc;renderMath(descEl);}
document.getElementById('p6-proof-next').addEventListener('click',()=>{
if(step<steps.length-1){step++;show();addXp(1,'p6-proof-step');}
else{addXp(5,'p6-proof-done');bumpProgress('p6',10);}
});
document.getElementById('p6-proof-reset').addEventListener('click',()=>{step=0;show();});
show();
})();
/* == INIT ИНТЕРАКТИВ 3: калькулятор == */
(function(){
document.getElementById('p6-ccalc').addEventListener('click',()=>{
const AB=parseFloat(document.getElementById('p6-cAB').value);
const AC=parseFloat(document.getElementById('p6-cAC').value);
const ang=parseFloat(document.getElementById('p6-cAng').value);
const k=parseFloat(document.getElementById('p6-ck').value);
const out=document.getElementById('p6-ccalc-out');
if([AB,AC,ang,k].some(v=>!isFinite(v)||v<=0)){
out.style.display='block';out.innerHTML='<span style="color:var(--bad)">Введи все поля (положительные числа).</span>';return;
}
if(ang>=180){out.style.display='block';out.innerHTML='<span style="color:var(--bad)">Угол должен быть меньше 180°.</span>';return;}
const AB2=AB/k, AC2=AC/k;
const angRad=ang*Math.PI/180;
const BC=Math.sqrt(AB*AB+AC*AC-2*AB*AC*Math.cos(angRad));
const BC2=BC/k;
out.style.display='block';
out.innerHTML=`Второй треугольник: $A'B'=\\dfrac{AB}{k}=\\dfrac{${fmt(AB)}}{${fmt(k)}}=${fmt(AB2)}$, $A'C'=\\dfrac{AC}{k}=\\dfrac{${fmt(AC)}}{${fmt(k)}}=${fmt(AC2)}$, $\\angle A'=${fmt(ang)}°$.<br>По теореме косинусов: $BC=${fmt(BC)}$, $B'C'=${fmt(BC2)}$.<br>Треугольники подобны по признаку по двум сторонам и углу (СУС) с $k=${fmt(k)}$.`;
renderMath(out);
addXp(3,'p6-calc');bumpProgress('p6',5);
});
})();
/* == INIT ИНТЕРАКТИВ 4: тренажёр == */
(function(){
const tasks=[
{q:'$AB=12$, $AC=8$, $A\'B\'=6$, $A\'C\'=4$, $\\angle A=\\angle A\'=40°$. Найди коэффициент подобия $k=AB/A\'B\'$.',ans:2,hint:'k=12/6=2. Проверь: AC/A\'C\'=8/4=2. ✓'},
{q:'$PQ=15$, $PR=9$, $P\'Q\'=5$, $P\'R\'=3$, $\\angle P=\\angle P\'$. Подобны ли треугольники? Введи $k$ (или 0 если не подобны).',ans:3,hint:'PQ/P\'Q\'=15/5=3, PR/P\'R\'=9/3=3. Подобны, k=3.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$ по двум сторонам и углу (СУС), $k=2.5$. $A\'B\'=6$. Найди $AB$.',ans:15,hint:'AB=k·A\'B\'=2.5·6=15.'},
{q:'$AB=10$, $AC=6$, $A\'B\'=4$, $A\'C\'=3$, $\\angle A=\\angle A\'$. Проверь подобие: $AB/A\'B\'=?$ (введи значение)',ans:2.5,hint:'AB/A\'B\'=10/4=2.5, AC/A\'C\'=6/3=2. Отношения разные → не подобны по двум сторонам и углу (СУС).'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$ по двум сторонам и углу (СУС), $k=3$. Стороны второго: $A\'B\'=4$, $A\'C\'=5$. Найди $AB+AC$.',ans:27,hint:'AB=3·4=12, AC=3·5=15. AB+AC=27.'},
];
let idx=0,score=0;
function show(){
document.getElementById('p6-tr-i').textContent=idx+1;
const t=document.getElementById('p6-tr-task');
t.innerHTML=tasks[idx].q;
renderMath(t);
document.getElementById('p6-tr-ans').value='';
document.getElementById('p6-tr-fb').style.display='none';
}
document.getElementById('p6-tr-start').addEventListener('click',()=>{idx=0;score=0;document.getElementById('p6-tr-score').textContent=0;show();});
document.getElementById('p6-tr-go').addEventListener('click',()=>{
if(idx>=tasks.length)return;
const ans=+document.getElementById('p6-tr-ans').value;
const fb=document.getElementById('p6-tr-fb');
if(Math.abs(ans-tasks[idx].ans)<0.05){
score++;document.getElementById('p6-tr-score').textContent=score;
addXp(3,'p6-tr-'+idx);bumpProgress('p6',4);
if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}
else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p6-tr-all');bumpProgress('p6',10);confetti();}
} else {
feedback(fb,false,'Неверно. '+tasks[idx].hint);
}
});
document.getElementById('p6-tr-ans').addEventListener('keydown',e=>{if(e.key==='Enter')document.getElementById('p6-tr-go').click();});
show();
})();
/* == INIT ИНТЕРАКТИВ 5: DnD-сортер == */
(function(){
const items=[
{text:'AB=6,AC=9,A\'B\'=4,A\'C\'=6, ∠A=∠A\'',yes:true},
{text:'AB=8,AC=6,A\'B\'=4,A\'C\'=4, ∠A=∠A\'',yes:false},
{text:'PQ=15,PR=10,P\'Q\'=6,P\'R\'=4, ∠P=∠P\'',yes:true},
{text:'AB=10,AC=6,A\'B\'=5,A\'C\'=4, ∠A=∠A\'',yes:false},
{text:'MK=12,ML=8,M\'K\'=9,M\'L\'=6, ∠M=∠M\'',yes:true},
];
const pool=document.getElementById('p6-dnd-pool');
const yesBox=document.getElementById('p6-drop-yes-items');
const noBox=document.getElementById('p6-drop-no-items');
let dragging=null;
function makeChip(it,idx){
const chip=document.createElement('div');
chip.className='dnd-chip';
chip.dataset.idx=idx;
chip.textContent=it.text;
chip.draggable=true;
chip.addEventListener('dragstart',e=>{dragging=chip;chip.classList.add('dragging');e.dataTransfer.effectAllowed='move';});
chip.addEventListener('dragend',()=>{chip.classList.remove('dragging');dragging=null;});
return chip;
}
items.forEach((it,i)=>pool.appendChild(makeChip(it,i)));
[document.getElementById('p6-drop-yes'),document.getElementById('p6-drop-no'),pool].forEach(box=>{
box.addEventListener('dragover',e=>{e.preventDefault();box.classList.add('over');});
box.addEventListener('dragleave',()=>box.classList.remove('over'));
box.addEventListener('drop',e=>{
e.preventDefault();box.classList.remove('over');
if(!dragging)return;
const target=box===document.getElementById('p6-drop-yes')?yesBox:box===document.getElementById('p6-drop-no')?noBox:pool;
target.appendChild(dragging);
});
});
document.getElementById('p6-dnd-check').addEventListener('click',()=>{
const fb=document.getElementById('p6-dnd-fb');
const yChips=[...yesBox.querySelectorAll('.dnd-chip')];
const nChips=[...noBox.querySelectorAll('.dnd-chip')];
if(yChips.length+nChips.length<items.length){feedback(fb,false,'Разложи все карточки.');return;}
let ok=true;
yChips.forEach(c=>{if(!items[+c.dataset.idx].yes)ok=false;});
nChips.forEach(c=>{if(items[+c.dataset.idx].yes)ok=false;});
if(ok){feedback(fb,true,'Верно! +5 XP');addXp(5,'p6-dnd');bumpProgress('p6',8);}
else{feedback(fb,false,'Есть ошибки. Проверь равенство отношений двух пар сторон и равенство углов между ними.');}
});
document.getElementById('p6-dnd-reset').addEventListener('click',()=>{
[...yesBox.children].forEach(c=>pool.appendChild(c));
[...noBox.children].forEach(c=>pool.appendChild(c));
document.getElementById('p6-dnd-fb').style.display='none';
});
})();
/* == INIT: Босс §6 == */
(function(){
const tasks=[
{q:'<svg viewBox="0 0 300 130" style="display:block;max-width:300px;margin:0 auto 8px;background:#ede9fe;border:1px solid #c4b5fd;border-radius:8px"><polygon points="85,15 20,110 160,110" fill="rgba(124,58,237,.12)" stroke="#7c3aed" stroke-width="2"/><polygon points="228,47 185,110 278,110" fill="rgba(99,102,241,.18)" stroke="#6366f1" stroke-width="1.8"/><path d="M73,30 Q85,38 97,32" fill="none" stroke="#f59e0b" stroke-width="1.8"/><path d="M219,60 Q228,67 237,61" fill="none" stroke="#f59e0b" stroke-width="1.5"/><text x="85" y="10" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="800">A</text><text x="12" y="119" font-size="10" fill="#6d28d9" font-weight="800">B</text><text x="163" y="119" font-size="10" fill="#6d28d9" font-weight="800">C</text><text x="228" y="42" text-anchor="middle" font-size="9" fill="#4f46e5" font-weight="800">A\'</text><text x="177" y="119" font-size="9" fill="#4f46e5" font-weight="800">B\'</text><text x="280" y="119" font-size="9" fill="#4f46e5" font-weight="800">C\'</text><text x="150" y="10" text-anchor="middle" font-size="8" fill="#7c3aed">AB=9,AC=12,A\'B\'=6,A\'C\'=8,∠A=∠A\',k=1.5</text></svg>$AB=9$, $AC=12$, $A\'B\'=6$, $A\'C\'=8$, $\\angle A=\\angle A\'$. Найди $k=AB/A\'B\'$.',ans:1.5,hint:'k=9/6=1.5. Проверь: 12/8=1.5. ✓ Признак по двум сторонам и углу (СУС) выполнен.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$ по СУС, $k=4$. Стороны второго: $A\'B\'=3$, $A\'C\'=5$. Чему равно $AB+AC$?',ans:32,hint:'AB=4·3=12, AC=4·5=20. Сумма 32.'},
{q:'$AB=10$, $AC=15$, $\\angle A=60°$. По признаку СУС треугольник подобен другому с $k=2.5$. Найди $A\'B\'$.',ans:4,hint:'A\'B\'=AB/k=10/2.5=4.'},
{q:'Даны два треугольника: $AB=18$, $AC=12$, $A\'B\'=6$, $A\'C\'=4$, $\\angle A=\\angle A\'=75°$. Найди $BC$, если $B\'C\'=5$.',ans:15,hint:'k=18/6=3. BC=k·B\'C\'=3·5=15.'},
];
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="p6b-a${i}" placeholder="Ответ" style="width:110px">
<button class="btn primary small" onclick="(function(){
const v=+document.getElementById('p6b-a${i}').value;
const fb=document.getElementById('p6b-fb${i}');
if(Math.abs(v-${t.ans})<0.05){
feedback(fb,true,'Верно! +5 XP');
if(!window.p6BossSolved.has(${i})){ window.p6BossSolved.add(${i}); addXp(5,'p6-boss${i}'); bumpProgress('p6',8); }
} else feedback(fb,false,'Неверно. ${t.hint}');
})()">Проверить</button>
</div>
<div class="feedback" id="p6b-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p6BossSolved=new Set();
renderMath(bossBox);
})();
}
function buildP7(){
const box=document.getElementById('p7-body');
let html='';
/* ---- Theory cards ---- */
html+=makeCard('theory','Третий признак подобия треугольников — по трём сторонам (ССС)','7.1',`
<p><b>Теорема (3-й признак подобия — по трём сторонам, ССС).</b> Если три стороны одного треугольника пропорциональны трём сторонам другого треугольника, то такие треугольники подобны.</p>
<p style="margin-top:8px">Если $\\dfrac{a}{a'} = \\dfrac{b}{b'} = \\dfrac{c}{c'} = k$, то $\\triangle ABC \\sim \\triangle A'B'C'$.</p>
<p style="margin-top:8px">Здесь $a=BC$, $b=AC$, $c=AB$ и аналогично для второго треугольника. Это самый сильный признак — он <b>не требует</b> проверки углов.</p>
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 310 185" style="max-width:330px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Triangle 1 (large): A=(86,28), B=(14,155), C=(192,155) -->
<!-- BC=178, AB≈141.6, AC≈160.8 — similar proportions -->
<polygon points="86,28 14,155 192,155" fill="rgba(124,58,237,.11)" stroke="#7c3aed" stroke-width="2"/>
<text x="86" y="20" text-anchor="middle" font-size="11" font-weight="800" fill="#6d28d9">A</text>
<text x="2" y="166" font-size="11" font-weight="800" fill="#6d28d9">B</text>
<text x="194" y="166" font-size="11" font-weight="800" fill="#6d28d9">C</text>
<!-- Side labels t1 — OUTSIDE polygon -->
<text x="40" y="100" text-anchor="end" font-size="10" fill="#7c3aed" font-style="italic">c=AB</text>
<text x="103" y="170" text-anchor="middle" font-size="10" fill="#7c3aed" font-style="italic">a=BC</text>
<text x="148" y="97" font-size="10" fill="#7c3aed" font-style="italic">b=AC</text>
<!-- Triangle 2 (small, k=2.5): B'=(232,120), C'=(303,120), A'=(260,77) -->
<!-- B'C'=71≈178/2.5✓ -->
<polygon points="260,77 232,120 303,120" fill="rgba(99,102,241,.18)" stroke="#6366f1" stroke-width="1.8"/>
<text x="260" y="70" text-anchor="middle" font-size="10" font-weight="800" fill="#4f46e5">A'</text>
<text x="220" y="131" font-size="10" font-weight="800" fill="#4f46e5">B'</text>
<text x="305" y="131" font-size="10" font-weight="800" fill="#4f46e5">C'</text>
<!-- Side labels t2 — OUTSIDE polygon -->
<text x="242" y="102" text-anchor="end" font-size="9" fill="#6366f1" font-style="italic">c'</text>
<text x="268" y="130" text-anchor="middle" font-size="9" fill="#6366f1" font-style="italic">a'</text>
<text x="288" y="101" font-size="9" fill="#6366f1" font-style="italic">b'</text>
<!-- Ratio text — at BOTTOM, well below figures -->
<text x="155" y="178" text-anchor="middle" font-size="9" fill="#f59e0b" font-weight="800">a/a'=b/b'=c/c'=k≈2.5 → △ABC∼△A'B'C'</text>
</svg>
</div>`);
html+=makeCard('rule','Доказательство 3-го признака — схема','7.2',`
<p>Доказательство сводится к 2-му признаку (по двум сторонам и углу, СУС).</p>
<p style="margin-top:6px">Пусть $\\dfrac{a}{a'}=\\dfrac{b}{b'}=\\dfrac{c}{c'}=k$. На стороне $A'B'$ откладываем $A'M=AB=kA'B'$. Через $M \\parallel B'C'$ строим точку $N$ на $A'C'$, тогда $\\triangle A'MN \\sim \\triangle A'B'C'$ и все стороны $\\triangle A'MN$ равны сторонам $\\triangle ABC$.</p>
<p style="margin-top:6px">По признаку равенства SSS: $\\triangle A'MN \\cong \\triangle ABC$. Значит $\\triangle ABC \\sim \\triangle A'B'C'$ (транзитивность). <b>QED.</b></p>
<div style="display:flex;justify-content:center;margin-top:10px">
<svg viewBox="0 0 280 130" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<polygon points="140,14 30,122 250,122" fill="rgba(99,102,241,.10)" stroke="#6366f1" stroke-width="1.5"/>
<polygon points="140,14 82,72 198,72" fill="rgba(124,58,237,.22)" stroke="#7c3aed" stroke-width="2"/>
<line x1="82" y1="72" x2="198" y2="72" stroke="#7c3aed" stroke-width="2" stroke-dasharray="5,3"/>
<text x="140" y="9" text-anchor="middle" font-size="10" font-weight="800" fill="#4f46e5">A'</text>
<text x="20" y="130" font-size="10" font-weight="800" fill="#4f46e5">B'</text>
<text x="252" y="130" font-size="10" font-weight="800" fill="#4f46e5">C'</text>
<text x="76" y="82" font-size="10" font-weight="800" fill="#7c3aed">M</text>
<text x="200" y="82" font-size="10" font-weight="800" fill="#7c3aed">N</text>
<text x="140" y="105" text-anchor="middle" font-size="8" fill="#6366f1">△A'MN∼△A'B'C', △A'MN≅△ABC → △ABC∼△A'B'C'</text>
</svg>
</div>`);
html+=makeCard('example','Примеры применения признака по трём сторонам (ССС)','7.3',`
<p><b>Пример 1.</b> $a=6$, $b=8$, $c=10$; $a'=3$, $b'=4$, $c'=5$. Подобны ли треугольники?</p>
<p style="margin-top:4px">$a/a'=6/3=2$, $b/b'=8/4=2$, $c/c'=10/5=2$. Все отношения равны $k=2$ → по трём сторонам (ССС): подобны.</p>
<p style="margin-top:8px"><b>Пример 2.</b> $a=9$, $b=12$, $c=6$; $a'=6$, $b'=8$, $c'=4$. $9/6=1{,}5$, $12/8=1{,}5$, $6/4=1{,}5$. $k=1{,}5$ — подобны.</p>
<p style="margin-top:8px"><b>Пример 3 (не подобны).</b> $a=5$, $b=7$, $c=9$; $a'=5$, $b'=7$, $c'=10$. $5/5=1$, $7/7=1$, $9/10=0{,}9$. Отношения различны → <b>не подобны</b>.</p>`);
/* ---- ИНТЕРАКТИВ 1: SVG три стороны + слайдер k ---- */
html+=`<div class="wg" id="p7-svg-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Два треугольника — признак по трём сторонам (ССС)</div></div>
<div class="wg-help">Задай три стороны первого треугольника и коэффициент $k$. Второй строится автоматически: все стороны уменьшены в $k$ раз. Треугольное неравенство проверяется автоматически.</div>
<div class="sliders">
<label>Сторона $a$: <b id="p7-a-val">6</b>
<input type="range" min="3" max="14" value="6" id="p7-a-sl" step="1">
</label>
<label>Сторона $b$: <b id="p7-b-val">8</b>
<input type="range" min="3" max="14" value="8" id="p7-b-sl" step="1">
</label>
<label>Сторона $c$: <b id="p7-c-val">9</b>
<input type="range" min="3" max="14" value="9" id="p7-c-sl" step="1">
</label>
<label>Коэффициент $k$: <b id="p7-k-val">2.0</b>
<input type="range" min="12" max="35" value="20" id="p7-k-sl" step="1">
</label>
</div>
<div id="p7-svg-out" style="display:flex;justify-content:center"></div>
<div id="p7-svg-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.92rem;line-height:1.8"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 2: Пошаговое доказательство ---- */
html+=`<div class="wg" id="p7-proof-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Доказательство 3-го признака — по шагам</div></div>
<div class="wg-help">Нажимай «Далее» для каждого шага доказательства признака по трём сторонам (ССС).</div>
<div id="p7-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p7-proof-desc" style="padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.6;margin-bottom:10px;min-height:60px"></div>
<div style="display:flex;gap:8px">
<button class="btn primary" id="p7-proof-next">Далее</button>
<button class="btn" id="p7-proof-reset">Сначала</button>
</div>
</div>`;
/* ---- ИНТЕРАКТИВ 3: Калькулятор ССС ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Калькулятор: проверка подобия по трём сторонам</div></div>
<div class="wg-help">Введи стороны двух треугольников — система проверит пропорциональность и найдёт $k$.</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(90px,1fr));gap:8px;margin-bottom:10px">
<div><span style="font-size:.8rem;color:var(--muted);display:block">a (BC)</span><input type="number" id="p7-ca" class="tinp" placeholder="напр. 6" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">b (AC)</span><input type="number" id="p7-cb" class="tinp" placeholder="напр. 8" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">c (AB)</span><input type="number" id="p7-cc" class="tinp" placeholder="напр. 10" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">a' (B'C')</span><input type="number" id="p7-ca2" class="tinp" placeholder="напр. 3" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">b' (A'C')</span><input type="number" id="p7-cb2" class="tinp" placeholder="напр. 4" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">c' (A'B')</span><input type="number" id="p7-cc2" class="tinp" placeholder="напр. 5" style="width:100%"></div>
<div style="display:flex;align-items:flex-end"><button class="btn primary" id="p7-ccalc" style="width:100%">Проверить</button></div>
</div>
<div id="p7-ccalc-out" style="display:none;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.7"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 4: Тренажёр ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр §7 — Признак по трём сторонам (ССС)</div></div>
<div class="wg-help">5 задач на проверку подобия по сторонам и нахождение коэффициента.</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.02rem;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:130px">
<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>`;
/* ---- ИНТЕРАКТИВ 5: DnD-сортер ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Подобны по трём сторонам (ССС) или нет? — Сортировка</div></div>
<div class="wg-help">Перетащи каждую пару троек сторон в нужную колонку.</div>
<div id="p7-dnd-pool" class="dnd-pool"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:10px">
<div class="drop-box" id="p7-drop-yes"><h5>Подобны (по трём сторонам)</h5><div class="drop-items" id="p7-drop-yes-items"></div></div>
<div class="drop-box" id="p7-drop-no"><h5>Не подобны</h5><div class="drop-items" id="p7-drop-no-items"></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>`;
/* ---- ИНТЕРАКТИВ 6: Мини-квиз (три признака) ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 6</span><div class="wg-title">Мини-квиз: все три признака подобия</div></div>
<div class="wg-help">5 вопросов о трёх признаках подобия. Выбери правильный ответ.</div>
<div id="p7-quiz-wrap"></div>
<div class="actions"><button class="btn primary" id="p7-quiz-check">Проверить</button></div>
<div class="feedback" id="p7-quiz-fb" style="display:none"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 7: Босс §7 ---- */
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))">БОСС §7</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">4 задачи — каждая верная даёт +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;
if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
/* == INIT ИНТЕРАКТИВ 1: SVG по трём сторонам == */
(function(){
const aSl=document.getElementById('p7-a-sl');
const bSl=document.getElementById('p7-b-sl');
const cSl=document.getElementById('p7-c-sl');
const kSl=document.getElementById('p7-k-sl');
const aVal=document.getElementById('p7-a-val');
const bVal=document.getElementById('p7-b-val');
const cVal=document.getElementById('p7-c-val');
const kVal=document.getElementById('p7-k-val');
const svgOut=document.getElementById('p7-svg-out');
const infoEl=document.getElementById('p7-svg-info');
function triFromSSS(aa,bb,cc,ox,oy){
// a=BC, b=AC, c=AB. Place B=(ox,oy), C=(ox+a,oy)
// Find A using law of cosines: cosB=(c²+a²-b²)/(2ca)
const cosB=(cc*cc+aa*aa-bb*bb)/(2*cc*aa);
if(cosB<-1||cosB>1) return null;
const Bx=ox, By=oy;
const Cx=ox+aa, Cy=oy;
const Ax=ox+cc*cosB;
const Ay=oy-cc*Math.sqrt(1-cosB*cosB);
return {Ax,Ay,Bx,By,Cx,Cy};
}
function draw(){
const a=+aSl.value, b=+bSl.value, c=+cSl.value;
const k=+kSl.value/10;
aVal.textContent=a; bVal.textContent=b; cVal.textContent=c;
kVal.textContent=k.toFixed(1);
// Triangle inequality check
if(a+b<=c||a+c<=b||b+c<=a){
svgOut.innerHTML='<div style="color:var(--bad);padding:10px">Нарушено треугольное неравенство. Измени стороны.</div>';
infoEl.innerHTML='Треугольное неравенство нарушено.';
return;
}
const scale=13;
const a2=a/k, b2=b/k, c2=c/k;
const W=400, H=185;
const t1=triFromSSS(a*scale,b*scale,c*scale,18,165);
if(!t1){svgOut.innerHTML='';return;}
const t2=triFromSSS(a2*scale,b2*scale,c2*scale,230,165);
if(!t2){svgOut.innerHTML='';return;}
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
s+=`<polygon points="${t1.Ax},${t1.Ay} ${t1.Bx},${t1.By} ${t1.Cx},${t1.Cy}" fill="rgba(124,58,237,.12)" stroke="#7c3aed" stroke-width="2"/>`;
s+=`<text x="${t1.Ax}" y="${t1.Ay-5}" text-anchor="middle" font-size="11" font-weight="800" fill="#6d28d9">A</text>`;
s+=`<text x="${t1.Bx-8}" y="${t1.By+4}" font-size="11" font-weight="800" fill="#6d28d9">B</text>`;
s+=`<text x="${t1.Cx+4}" y="${t1.Cy+4}" font-size="11" font-weight="800" fill="#6d28d9">C</text>`;
const mBCx=(t1.Bx+t1.Cx)/2, mBCy=(t1.By+t1.Cy)/2;
const mACx=(t1.Ax+t1.Cx)/2, mACy=(t1.Ay+t1.Cy)/2;
const mABx=(t1.Ax+t1.Bx)/2, mABy=(t1.Ay+t1.By)/2;
s+=`<text x="${mBCx}" y="${mBCy+11}" text-anchor="middle" font-size="9" fill="#7c3aed">a=${a}</text>`;
s+=`<text x="${mACx+8}" y="${mACy}" font-size="9" fill="#7c3aed">b=${b}</text>`;
s+=`<text x="${mABx-8}" y="${mABy}" text-anchor="end" font-size="9" fill="#7c3aed">c=${c}</text>`;
s+=`<polygon points="${t2.Ax},${t2.Ay} ${t2.Bx},${t2.By} ${t2.Cx},${t2.Cy}" fill="rgba(99,102,241,.18)" stroke="#6366f1" stroke-width="1.8"/>`;
s+=`<text x="${t2.Ax}" y="${t2.Ay-5}" text-anchor="middle" font-size="10" font-weight="800" fill="#4f46e5">A'</text>`;
s+=`<text x="${t2.Bx-10}" y="${t2.By+4}" font-size="10" font-weight="800" fill="#4f46e5">B'</text>`;
s+=`<text x="${t2.Cx+4}" y="${t2.Cy+4}" font-size="10" font-weight="800" fill="#4f46e5">C'</text>`;
const mBCx2=(t2.Bx+t2.Cx)/2, mBCy2=(t2.By+t2.Cy)/2;
const mACx2=(t2.Ax+t2.Cx)/2, mACy2=(t2.Ay+t2.Cy)/2;
const mABx2=(t2.Ax+t2.Bx)/2, mABy2=(t2.Ay+t2.By)/2;
s+=`<text x="${mBCx2}" y="${mBCy2+11}" text-anchor="middle" font-size="9" fill="#6366f1">a'=${fmt(a2)}</text>`;
s+=`<text x="${mACx2+6}" y="${mACy2}" font-size="9" fill="#6366f1">b'=${fmt(b2)}</text>`;
s+=`<text x="${mABx2-6}" y="${mABy2}" text-anchor="end" font-size="9" fill="#6366f1">c'=${fmt(c2)}</text>`;
s+=`<text x="${W/2}" y="12" text-anchor="middle" font-size="10" font-weight="800" fill="#7c3aed">k = ${k.toFixed(1)}</text>`;
s+='</svg>';
svgOut.innerHTML=s;
infoEl.innerHTML=`$a=${a}$, $b=${b}$, $c=${c}$. Второй: $a'=${fmt(a2)}$, $b'=${fmt(b2)}$, $c'=${fmt(c2)}$. $\\dfrac{a}{a'}=\\dfrac{b}{b'}=\\dfrac{c}{c'}=${k.toFixed(1)}$ → по признаку по трём сторонам (ССС) треугольники подобны.`;
renderMath(infoEl);
addXp(1,'p7-svg');
}
aSl.addEventListener('input',draw);
bSl.addEventListener('input',draw);
cSl.addEventListener('input',draw);
kSl.addEventListener('input',draw);
draw();
})();
/* == INIT ИНТЕРАКТИВ 2: пошаговое доказательство == */
(function(){
const steps=[
{desc:'<b>Шаг 1.</b> Дано: $\\dfrac{a}{a\'}=\\dfrac{b}{b\'}=\\dfrac{c}{c\'}=k$. Нужно доказать: $\\triangle ABC \\sim \\triangle A\'B\'C\'$.',
svg:`<svg viewBox="0 0 310 160" style="max-width:310px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="155" y="13" text-anchor="middle" font-size="9" fill="#f59e0b" font-weight="800">a/a\'=b/b\'=c/c\'=k (k≈2) — условие</text><polygon points="88,24 18,145 205,145" fill="rgba(124,58,237,.10)" stroke="#7c3aed" stroke-width="2"/><polygon points="260,72 238,132 302,132" fill="rgba(99,102,241,.18)" stroke="#6366f1" stroke-width="1.8"/><text x="88" y="17" text-anchor="middle" font-size="11" font-weight="800" fill="#6d28d9">A</text><text x="6" y="156" font-size="11" font-weight="800" fill="#6d28d9">B</text><text x="207" y="156" font-size="11" font-weight="800" fill="#6d28d9">C</text><text x="260" y="65" text-anchor="middle" font-size="10" font-weight="800" fill="#4f46e5">A\'</text><text x="226" y="143" font-size="10" font-weight="800" fill="#4f46e5">B\'</text><text x="304" y="143" font-size="10" font-weight="800" fill="#4f46e5">C\'</text></svg>`},
{desc:'<b>Шаг 2.</b> На луче $A\'B\'$ откладываем $A\'M = AB = k \\cdot A\'B\'$. Через $M$ проводим $MN \\parallel B\'C\'$, $N$ на $A\'C\'$. По теореме §4: $\\triangle A\'MN \\sim \\triangle A\'B\'C\'$.',
svg:`<svg viewBox="0 0 280 140" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><polygon points="140,14 30,126 250,126" fill="rgba(99,102,241,.10)" stroke="#6366f1" stroke-width="1.5"/><polygon points="140,14 82,70 198,70" fill="rgba(124,58,237,.22)" stroke="#7c3aed" stroke-width="2"/><line x1="82" y1="70" x2="198" y2="70" stroke="#7c3aed" stroke-width="2" stroke-dasharray="5,3"/><text x="140" y="9" text-anchor="middle" font-size="10" font-weight="800" fill="#4f46e5">A'</text><text x="20" y="134" font-size="10" font-weight="800" fill="#4f46e5">B'</text><text x="252" y="134" font-size="10" font-weight="800" fill="#4f46e5">C'</text><text x="76" y="80" font-size="10" font-weight="800" fill="#7c3aed">M</text><text x="200" y="80" font-size="10" font-weight="800" fill="#7c3aed">N</text><text x="140" y="110" text-anchor="middle" font-size="9" fill="#7c3aed">A'M=AB, MN∥B'C'</text></svg>`},
{desc:'<b>Шаг 3.</b> Поскольку $\\triangle A\'MN \\sim \\triangle A\'B\'C\'$ с коэффициентом $k$, все стороны $\\triangle A\'MN$ равны соответствующим сторонам $\\triangle ABC$: $A\'M=AB$, $MN=a$, $A\'N=b$.',
svg:`<svg viewBox="0 0 280 90" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="140" y="22" text-anchor="middle" font-size="11" fill="#4f46e5" font-weight="700">△A'MN∼△A'B'C' с коэфф. k</text><text x="140" y="44" text-anchor="middle" font-size="11" fill="#7c3aed" font-weight="700">A'M=AB=c, MN=BC=a, A'N=AC=b</text><text x="140" y="68" text-anchor="middle" font-size="10" fill="#10b981" font-weight="800">Все стороны △A'MN = сторонам △ABC</text></svg>`},
{desc:'<b>Шаг 4.</b> По признаку равенства треугольников (SSS): $\\triangle A\'MN \\cong \\triangle ABC$ (у них равны все три пары сторон).',
svg:`<svg viewBox="0 0 280 80" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="140" y="26" text-anchor="middle" font-size="12" fill="#4338ca" font-weight="800">△A'MN ≅ △ABC (SSS)</text><text x="140" y="52" text-anchor="middle" font-size="10" fill="#6366f1">Три стороны равны попарно → конгруэнтны</text><text x="140" y="70" text-anchor="middle" font-size="9" fill="#7c3aed">△A'MN∼△A'B'C' также выполнено</text></svg>`},
{desc:'<b>Шаг 5.</b> По транзитивности: $\\triangle ABC \\cong \\triangle A\'MN \\sim \\triangle A\'B\'C\'$ → $\\triangle ABC \\sim \\triangle A\'B\'C\'$. <b>Третий признак подобия (по трём сторонам, ССС) доказан.</b>',
svg:`<svg viewBox="0 0 280 80" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="140" y="26" text-anchor="middle" font-size="12" fill="#7c3aed" font-weight="800">a/a'=b/b'=c/c'=k → △ABC∼△A'B'C'</text><text x="140" y="50" text-anchor="middle" font-size="10" fill="#6366f1">Признак по трём сторонам (ССС) — третий признак подобия</text><text x="140" y="70" text-anchor="middle" font-size="12" fill="#10b981" font-weight="900">QED ∎</text></svg>`},
];
let step=0;
const svgEl=document.getElementById('p7-proof-svg');
const descEl=document.getElementById('p7-proof-desc');
function show(){svgEl.innerHTML=steps[step].svg;descEl.innerHTML=steps[step].desc;renderMath(descEl);}
document.getElementById('p7-proof-next').addEventListener('click',()=>{
if(step<steps.length-1){step++;show();addXp(1,'p7-proof-step');}
else{addXp(5,'p7-proof-done');bumpProgress('p7',10);}
});
document.getElementById('p7-proof-reset').addEventListener('click',()=>{step=0;show();});
show();
})();
/* == INIT ИНТЕРАКТИВ 3: калькулятор ССС == */
(function(){
document.getElementById('p7-ccalc').addEventListener('click',()=>{
const a=parseFloat(document.getElementById('p7-ca').value);
const b=parseFloat(document.getElementById('p7-cb').value);
const c=parseFloat(document.getElementById('p7-cc').value);
const a2=parseFloat(document.getElementById('p7-ca2').value);
const b2=parseFloat(document.getElementById('p7-cb2').value);
const c2=parseFloat(document.getElementById('p7-cc2').value);
const out=document.getElementById('p7-ccalc-out');
if([a,b,c,a2,b2,c2].some(v=>!isFinite(v)||v<=0)){
out.style.display='block';out.innerHTML='<span style="color:var(--bad)">Введи все шесть сторон.</span>';return;
}
const k1=a/a2, k2=b/b2, k3=c/c2;
const tol=0.001;
const ok=Math.abs(k1-k2)<tol&&Math.abs(k2-k3)<tol;
out.style.display='block';
if(ok){
out.innerHTML=`$\\dfrac{a}{a'}=\\dfrac{${fmt(a)}}{${fmt(a2)}}=${fmt(k1)}$, $\\dfrac{b}{b'}=\\dfrac{${fmt(b)}}{${fmt(b2)}}=${fmt(k2)}$, $\\dfrac{c}{c'}=\\dfrac{${fmt(c)}}{${fmt(c2)}}=${fmt(k3)}$.<br><b>Все отношения равны $k=${fmt(k1)}$</b> → треугольники <b>подобны по признаку по трём сторонам (ССС)</b>.`;
addXp(3,'p7-calc');bumpProgress('p7',5);
} else {
out.innerHTML=`$\\dfrac{a}{a'}=${fmt(k1)}$, $\\dfrac{b}{b'}=${fmt(k2)}$, $\\dfrac{c}{c'}=${fmt(k3)}$.<br>Отношения <b>не равны</b> → треугольники <b>не подобны</b> по признаку по трём сторонам (ССС).`;
}
renderMath(out);
});
})();
/* == INIT ИНТЕРАКТИВ 4: тренажёр == */
(function(){
const tasks=[
{q:'$a=9$, $b=12$, $c=6$; $a\'=3$, $b\'=4$, $c\'=2$. Найди коэффициент подобия $k=a/a\'$.',ans:3,hint:'9/3=12/4=6/2=3. k=3.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$ по трём сторонам (ССС), $k=2.5$. Стороны первого: $a=10$, $b=15$. Найди $a\'$.',ans:4,hint:'a\'=a/k=10/2.5=4.'},
{q:'Стороны: $(6,8,10)$ и $(3,4,5)$. Подобны ли? Введи $k$ (или 0 если нет).',ans:2,hint:'6/3=8/4=10/5=2. Подобны по трём сторонам (ССС), k=2.'},
{q:'$a=5$, $b=7$, $c=9$; $a\'=5$, $b\'=7$, $c\'=10$. Подобны ли? Введи $k$ (или 0 если нет).',ans:0,hint:'5/5=1, 7/7=1, но 9/10≠1. Не подобны. Ответ 0.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$ по трём сторонам (ССС), $k=4$. Периметр $\\triangle A\'B\'C\'=21$. Найди периметр $\\triangle ABC$.',ans:84,hint:'P=k·P\'=4·21=84.'},
];
let idx=0,score=0;
function show(){
document.getElementById('p7-tr-i').textContent=idx+1;
const t=document.getElementById('p7-tr-task');
t.innerHTML=tasks[idx].q;
renderMath(t);
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',()=>{
if(idx>=tasks.length)return;
const ans=+document.getElementById('p7-tr-ans').value;
const fb=document.getElementById('p7-tr-fb');
if(Math.abs(ans-tasks[idx].ans)<0.05){
score++;document.getElementById('p7-tr-score').textContent=score;
addXp(3,'p7-tr-'+idx);bumpProgress('p7',4);
if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}
else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p7-tr-all');bumpProgress('p7',10);confetti();}
} 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();
})();
/* == INIT ИНТЕРАКТИВ 5: DnD-сортер ССС == */
(function(){
const items=[
{text:'(6,8,10) и (3,4,5)',yes:true},
{text:'(5,7,9) и (5,7,10)',yes:false},
{text:'(9,12,6) и (6,8,4)',yes:true},
{text:'(4,6,8) и (3,5,8)',yes:false},
{text:'(15,10,5) и (6,4,2)',yes:true},
];
const pool=document.getElementById('p7-dnd-pool');
const yesBox=document.getElementById('p7-drop-yes-items');
const noBox=document.getElementById('p7-drop-no-items');
let dragging=null;
function makeChip(it,idx){
const chip=document.createElement('div');
chip.className='dnd-chip';
chip.dataset.idx=idx;
chip.textContent=it.text;
chip.draggable=true;
chip.addEventListener('dragstart',e=>{dragging=chip;chip.classList.add('dragging');e.dataTransfer.effectAllowed='move';});
chip.addEventListener('dragend',()=>{chip.classList.remove('dragging');dragging=null;});
return chip;
}
items.forEach((it,i)=>pool.appendChild(makeChip(it,i)));
[document.getElementById('p7-drop-yes'),document.getElementById('p7-drop-no'),pool].forEach(box=>{
box.addEventListener('dragover',e=>{e.preventDefault();box.classList.add('over');});
box.addEventListener('dragleave',()=>box.classList.remove('over'));
box.addEventListener('drop',e=>{
e.preventDefault();box.classList.remove('over');
if(!dragging)return;
const target=box===document.getElementById('p7-drop-yes')?yesBox:box===document.getElementById('p7-drop-no')?noBox:pool;
target.appendChild(dragging);
});
});
document.getElementById('p7-dnd-check').addEventListener('click',()=>{
const fb=document.getElementById('p7-dnd-fb');
const yChips=[...yesBox.querySelectorAll('.dnd-chip')];
const nChips=[...noBox.querySelectorAll('.dnd-chip')];
if(yChips.length+nChips.length<items.length){feedback(fb,false,'Разложи все карточки.');return;}
let ok=true;
yChips.forEach(c=>{if(!items[+c.dataset.idx].yes)ok=false;});
nChips.forEach(c=>{if(items[+c.dataset.idx].yes)ok=false;});
if(ok){feedback(fb,true,'Верно! +5 XP');addXp(5,'p7-dnd');bumpProgress('p7',8);}
else{feedback(fb,false,'Есть ошибки. Проверь: все три отношения a/a\', b/b\', c/c\' должны быть равны.');}
});
document.getElementById('p7-dnd-reset').addEventListener('click',()=>{
[...yesBox.children].forEach(c=>pool.appendChild(c));
[...noBox.children].forEach(c=>pool.appendChild(c));
document.getElementById('p7-dnd-fb').style.display='none';
});
})();
/* == INIT ИНТЕРАКТИВ 6: мини-квиз == */
(function(){
const qs=[
{q:'По какому признаку два угла одного треугольника равны двум углам другого?',opts:['По двум углам (УУ)','По двум сторонам и углу (СУС)','По трём сторонам (ССС)','Нет такого признака'],ans:0},
{q:'Сколько признаков подобия треугольников есть в курсе 8 класса?',opts:['1','2','3','4'],ans:2},
{q:'Признак по двум сторонам и углу (СУС) требует равенства...',opts:['двух углов','двух сторон и угла между ними','трёх сторон','двух сторон и угла напротив'],ans:1},
{q:'Если $a/a\'=2$, $b/b\'=2$, $c/c\'=2$, то треугольники подобны по признаку...',opts:['По двум углам (УУ)','По двум сторонам и углу (СУС)','По трём сторонам (ССС)','Нельзя определить'],ans:2},
{q:'По признаку по трём сторонам (ССС) требуется проверить...',opts:['2 угла','2 стороны и угол','3 стороны','1 угол и 2 стороны'],ans:2},
];
const wrap=document.getElementById('p7-quiz-wrap');
wrap.innerHTML=qs.map((q,qi)=>`
<div style="margin-bottom:14px;padding:12px;background:var(--card);border:1px solid var(--border);border-radius:10px">
<div style="font-weight:700;margin-bottom:8px;font-size:.95rem">${qi+1}. ${q.q}</div>
<div style="display:flex;flex-direction:column;gap:6px">
${q.opts.map((o,oi)=>`<label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:.92rem"><input type="radio" name="p7q${qi}" value="${oi}" style="accent-color:var(--sec-acc,var(--pri))"> ${o}</label>`).join('')}
</div>
</div>`).join('');
document.getElementById('p7-quiz-check').addEventListener('click',()=>{
const fb=document.getElementById('p7-quiz-fb');
let correct=0;
qs.forEach((q,qi)=>{
const sel=document.querySelector(`input[name="p7q${qi}"]:checked`);
if(sel&&+sel.value===q.ans)correct++;
});
if(correct===qs.length){
feedback(fb,true,`Все ${qs.length} ответов верны! +8 XP`);
addXp(8,'p7-quiz');bumpProgress('p7',12);confetti();
} else {
feedback(fb,false,`Верных ответов: ${correct} из ${qs.length}. Повтори параграфы с признаками.`);
}
});
})();
/* == INIT: Босс §7 == */
(function(){
const tasks=[
{q:'<svg viewBox="0 0 240 128" style="display:block;max-width:240px;margin:0 auto 8px;background:#ddd6fe;border:1px solid #c4b5fd;border-radius:8px"><polygon points="100,14 24,118 210,118" fill="rgba(124,58,237,.12)" stroke="#7c3aed" stroke-width="2"/><polygon points="196,40 168,108 252,108" fill="rgba(99,102,241,.18)" stroke="#6366f1" stroke-width="1.8"/><text x="100" y="9" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="800">A</text><text x="14" y="126" font-size="10" fill="#6d28d9" font-weight="800">B</text><text x="213" y="126" font-size="10" fill="#6d28d9" font-weight="800">C</text><text x="196" y="35" text-anchor="middle" font-size="9" fill="#4f46e5" font-weight="800">A\'</text><text x="160" y="116" font-size="9" fill="#4f46e5" font-weight="800">B\'</text><text x="254" y="116" font-size="9" fill="#4f46e5" font-weight="800">C\'</text><text x="120" y="9" text-anchor="middle" font-size="8" fill="#7c3aed">a=12,b=9,c=15; a\'=8,b\'=6,c\'=10</text></svg>$a=12$, $b=9$, $c=15$; $a\'=8$, $b\'=6$, $c\'=10$. Найди $k=a/a\'$.',ans:1.5,hint:'12/8=9/6=15/10=1.5. k=1.5. Признак по трём сторонам (ССС) выполнен.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$ по трём сторонам (ССС), $k=3$. Периметр $\\triangle A\'B\'C\'=28$. Найди периметр $\\triangle ABC$.',ans:84,hint:'P=3·28=84.'},
{q:'Стороны первого: $4, 6, 8$. Стороны второго: $6, 9, 12$. По какому признаку они подобны? Введи $k$ (большего к меньшему).',ans:1.5,hint:'6/4=9/6=12/8=1.5. Признак по трём сторонам (ССС). k=1.5.'},
{q:'$\\triangle DEF$: $DE=10$, $EF=24$, $DF=26$ (прямоугольный). $\\triangle D\'E\'F\'$: $D\'E\'=5$, $E\'F\'=12$, $D\'F\'=13$. Найди $k$.',ans:2,hint:'10/5=24/12=26/13=2. k=2. Признак по трём сторонам (ССС).'},
];
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="p7b-a${i}" placeholder="Ответ" style="width:110px">
<button class="btn primary small" onclick="(function(){
const v=+document.getElementById('p7b-a${i}').value;
const fb=document.getElementById('p7b-fb${i}');
if(Math.abs(v-${t.ans})<0.05){
feedback(fb,true,'Верно! +5 XP');
if(!window.p7BossSolved.has(${i})){ window.p7BossSolved.add(${i}); addXp(5,'p7-boss${i}'); bumpProgress('p7',8); }
} else feedback(fb,false,'Неверно. ${t.hint}');
})()">Проверить</button>
</div>
<div class="feedback" id="p7b-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p7BossSolved=new Set();
renderMath(bossBox);
})();
}
function buildP8(){
const box=document.getElementById('p8-body');
let html='';
/* ---- Theory cards ---- */
html+=makeCard('theory','Свойство биссектрисы треугольника','8.1',`
<p><b>Теорема.</b> Биссектриса угла треугольника делит противоположную сторону на отрезки, пропорциональные двум другим сторонам.</p>
<p style="margin-top:8px">В $\\triangle ABC$, $AD$ — биссектриса $\\angle A$ ($D \\in BC$). Тогда:</p>
$$\\dfrac{BD}{DC} = \\dfrac{AB}{AC} = \\dfrac{c}{b}$$
<p style="margin-top:8px">Здесь $c = AB$, $b = AC$. Биссектриса делит отрезок $BC$ в отношении смежных (прилежащих к нему) сторон.</p>
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 300 180" style="max-width:320px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- Triangle: A=(150,18), B=(30,162), C=(270,162) -->
<!-- Bisector of angle A: splits BC in ratio AB:AC -->
<!-- AB = sqrt((150-30)^2+(18-162)^2) = sqrt(14400+20736) = sqrt(35136) ≈ 187.4 -->
<!-- AC = sqrt((150-270)^2+(18-162)^2) = sqrt(14400+20736) ≈ 187.4 (isoceles here) -->
<!-- Let's use asymmetric: A=(120,18), B=(30,162), C=(260,162) -->
<!-- AB = sqrt(90^2+144^2)=sqrt(8100+20736)=sqrt(28836)≈169.8, AC=sqrt(140^2+144^2)=sqrt(19600+20736)=sqrt(40336)≈200.8 -->
<!-- D divides BC: BD/DC = AB/AC = 169.8/200.8 ≈ 0.846 → BD = 230*0.846/1.846 ≈ 105.5, D=(30+105.5,162)=(135.5,162) -->
<polygon points="120,18 30,162 260,162" fill="rgba(192,38,211,.1)" stroke="#c026d3" stroke-width="2"/>
<!-- bisector AD -->
<line x1="120" y1="18" x2="136" y2="162" stroke="#f59e0b" stroke-width="2.5" stroke-dasharray="6,3"/>
<!-- D point -->
<circle cx="136" cy="162" r="4" fill="#f59e0b"/>
<!-- BD in green, DC in blue -->
<line x1="30" y1="162" x2="136" y2="162" stroke="#10b981" stroke-width="3"/>
<line x1="136" y1="162" x2="260" y2="162" stroke="#6366f1" stroke-width="3"/>
<!-- Labels -->
<text x="120" y="12" text-anchor="middle" font-size="11" font-weight="800" fill="#a21caf">A</text>
<text x="18" y="170" font-size="11" font-weight="800" fill="#a21caf">B</text>
<text x="262" y="170" font-size="11" font-weight="800" fill="#a21caf">C</text>
<text x="136" y="175" text-anchor="middle" font-size="10" font-weight="800" fill="#b45309">D</text>
<!-- Side labels -->
<text x="66" y="90" text-anchor="middle" font-size="10" fill="#c026d3" font-style="italic" font-weight="700">c=AB</text>
<text x="198" y="90" text-anchor="middle" font-size="10" fill="#c026d3" font-style="italic" font-weight="700">b=AC</text>
<!-- BD, DC labels -->
<text x="83" y="155" text-anchor="middle" font-size="10" fill="#10b981" font-weight="800">BD</text>
<text x="198" y="155" text-anchor="middle" font-size="10" fill="#6366f1" font-weight="800">DC</text>
<!-- AD label -->
<text x="135" y="85" text-anchor="start" font-size="9" fill="#b45309" font-weight="700">AD — биссектриса</text>
<text x="150" y="12" text-anchor="middle" font-size="9" fill="#c026d3" font-weight="800">BD/DC = AB/AC = c/b</text>
</svg>
</div>`);
html+=makeCard('rule','Доказательство — через параллельную прямую','8.2',`
<p><b>Метод доказательства:</b> через точку $C$ проводим прямую, параллельную биссектрисе $AD$, до пересечения с продолжением стороны $AB$ в точке $E$.</p>
<ol style="margin-left:18px;margin-top:8px;line-height:2">
<li>$CE \\parallel AD$, $AD$ — биссектриса $\\angle A$ → $\\angle 1 = \\angle 2$ (смежно-внутренние при секущей $AB$).</li>
<li>$\\angle 2 = \\angle 3$ (вертикальные углы при пересечении $CE$ и $AB$ ... скорректировано: соответственные углы при параллельных). Значит $\\angle 1 = \\angle 3$, т.е. $\\triangle ACE$ — равнобедренный: $AE = AC = b$.</li>
<li>По теореме Фалеса (прямая $AD \\parallel CE$, секущие $BA$ и $BC$): $\\dfrac{BD}{DC} = \\dfrac{BA}{AE} = \\dfrac{c}{b}$.</li>
</ol>
<p style="margin-top:8px"><b>Итог:</b> $\\dfrac{BD}{DC} = \\dfrac{c}{b} = \\dfrac{AB}{AC}$. <b>Теорема доказана.</b></p>
<div style="display:flex;justify-content:center;margin-top:12px">
<svg viewBox="0 0 340 250" style="max-width:360px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!--
Layout (all within viewBox 0 0 340 250):
Triangle ABC: A=(150,80), B=(30,210), C=(295,210)
Bisector AD: D on BC at BD/DC = AB/AC ≈ 0.857 → D=(153,210)
E on extension of BA beyond A: place E at (230,30) — visually above-right of A.
CE dashed from C(295,210) to E(230,30).
Parallel tick marks on AD and CE to show CE∥AD.
-->
<!-- Main triangle -->
<polygon points="150,80 30,210 295,210" fill="rgba(192,38,211,.08)" stroke="#c026d3" stroke-width="1.8"/>
<!-- Bisector AD (orange dashed) -->
<line x1="150" y1="80" x2="153" y2="210" stroke="#f59e0b" stroke-width="2.5" stroke-dasharray="6,3"/>
<!-- Parallel tick marks on AD midpoint -->
<line x1="149" y1="143" x2="153" y2="151" stroke="#f59e0b" stroke-width="2.5"/>
<line x1="153" y1="141" x2="157" y2="149" stroke="#f59e0b" stroke-width="2.5"/>
<!-- D point on BC -->
<circle cx="153" cy="210" r="4.5" fill="#f59e0b" stroke="#fff" stroke-width="1.5"/>
<!-- BD green, DC blue on baseline -->
<line x1="30" y1="210" x2="153" y2="210" stroke="#10b981" stroke-width="3.5"/>
<line x1="153" y1="210" x2="295" y2="210" stroke="#6366f1" stroke-width="3.5"/>
<!-- E above A — extension of BA beyond A -->
<circle cx="220" cy="32" r="4.5" fill="#4f46e5" stroke="#fff" stroke-width="1.5"/>
<!-- Extension BA → E (thin dashed blue) -->
<line x1="30" y1="210" x2="222" y2="32" stroke="#6366f1" stroke-width="1.5" stroke-dasharray="4,2"/>
<!-- CE (green dashed, parallel to AD) -->
<line x1="295" y1="210" x2="220" y2="32" stroke="#10b981" stroke-width="2" stroke-dasharray="5,3"/>
<!-- Parallel tick marks on CE midpoint -->
<line x1="254" y1="119" x2="258" y2="127" stroke="#10b981" stroke-width="2.2"/>
<line x1="258" y1="117" x2="262" y2="125" stroke="#10b981" stroke-width="2.2"/>
<!-- Vertex circles -->
<circle cx="150" cy="80" r="4" fill="#c026d3" stroke="#fff" stroke-width="1.5"/>
<circle cx="30" cy="210" r="4" fill="#c026d3" stroke="#fff" stroke-width="1.5"/>
<circle cx="295" cy="210" r="4" fill="#c026d3" stroke="#fff" stroke-width="1.5"/>
<!-- Vertex labels -->
<text x="150" y="73" text-anchor="middle" font-size="11" font-weight="800" fill="#a21caf">A</text>
<text x="18" y="222" font-size="11" font-weight="800" fill="#a21caf">B</text>
<text x="298" y="222" font-size="11" font-weight="800" fill="#a21caf">C</text>
<text x="153" y="224" text-anchor="middle" font-size="10" font-weight="800" fill="#b45309">D</text>
<text x="228" y="27" text-anchor="middle" font-size="11" font-weight="800" fill="#4f46e5">E</text>
<!-- Side labels AB, AC — outside -->
<text x="76" y="140" text-anchor="end" font-size="10" fill="#c026d3" font-style="italic" font-weight="700">c=AB</text>
<text x="234" y="138" font-size="10" fill="#c026d3" font-style="italic" font-weight="700">b=AC</text>
<!-- CE∥AD label -->
<text x="308" y="108" font-size="9" fill="#10b981" font-weight="700">CE∥AD</text>
<!-- BD DC labels -->
<text x="92" y="204" text-anchor="middle" font-size="9" fill="#10b981" font-weight="800">BD</text>
<text x="224" y="204" text-anchor="middle" font-size="9" fill="#6366f1" font-weight="800">DC</text>
<!-- AE=AC=b label near E -->
<text x="218" y="48" text-anchor="end" font-size="9" fill="#4f46e5" font-weight="700">AE=AC=b</text>
<!-- Bottom caption -->
<text x="170" y="242" text-anchor="middle" font-size="9" fill="#c026d3" font-weight="800">BD/DC = BA/AE = c/b (теорема Фалеса)</text>
</svg>
</div>`);
html+=makeCard('example','Примеры применения','8.3',`
<p><b>Пример 1.</b> В $\\triangle ABC$ биссектриса $\\angle A$ делит $BC$. $AB=6$, $AC=9$, $BC=15$. Найти $BD$ и $DC$.</p>
<p style="margin-top:6px">$\\dfrac{BD}{DC} = \\dfrac{AB}{AC} = \\dfrac{6}{9} = \\dfrac{2}{3}$. Сумма $BD+DC=15$. Тогда $BD = \\dfrac{2}{5} \\cdot 15 = 6$, $DC = 9$.</p>
<p style="margin-top:10px"><b>Пример 2.</b> $AB=8$, $AC=12$, $BD=4$. Найти $DC$.</p>
<p style="margin-top:6px">$\\dfrac{4}{DC} = \\dfrac{8}{12}$ → $DC = \\dfrac{4 \\cdot 12}{8} = 6$.</p>
<p style="margin-top:10px"><b>Пример 3.</b> $BD=5$, $DC=7$. Найти $AB$, если $AC=14$.</p>
<p style="margin-top:6px">$\\dfrac{AB}{AC} = \\dfrac{BD}{DC} = \\dfrac{5}{7}$ → $AB = \\dfrac{5 \\cdot 14}{7} = 10$.</p>`);
/* ---- ИНТЕРАКТИВ 1: SVG с биссектрисой (слайдеры AB, AC) ---- */
html+=`<div class="wg" id="p8-svg-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Биссектриса $AD$ — живая пропорция</div></div>
<div class="wg-help">Меняй стороны $AB$ и $AC$ — точка $D$ автоматически делит $BC$ в отношении $AB:AC$. Цвета: BD — зелёный, DC — синий.</div>
<div class="sliders">
<label>$AB = c$: <b id="p8-c-val">6</b>
<input type="range" min="2" max="14" value="6" id="p8-c-sl" step="1">
</label>
<label>$AC = b$: <b id="p8-b-val">9</b>
<input type="range" min="2" max="14" value="9" id="p8-b-sl" step="1">
</label>
<label>$BC$: <b id="p8-a-val">12</b>
<input type="range" min="4" max="20" value="12" id="p8-a-sl" step="1">
</label>
</div>
<div id="p8-svg-out" style="display:flex;justify-content:center"></div>
<div id="p8-svg-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.92rem;line-height:1.8"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 2: Пошаговое доказательство ---- */
html+=`<div class="wg" id="p8-proof-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Доказательство — по шагам</div></div>
<div class="wg-help">Нажимай «Далее» для каждого шага. Доказательство через параллельную прямую.</div>
<div id="p8-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p8-proof-desc" style="padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.6;margin-bottom:10px;min-height:60px"></div>
<div style="display:flex;gap:8px">
<button class="btn primary" id="p8-proof-next">Далее</button>
<button class="btn" id="p8-proof-reset">Сначала</button>
</div>
</div>`;
/* ---- ИНТЕРАКТИВ 3: Калькулятор биссектрисы ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Калькулятор: AB, AC, BC → BD и DC</div></div>
<div class="wg-help">Введи три стороны треугольника и получи отрезки $BD$, $DC$, которые образует биссектриса.</div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(90px,1fr));gap:8px;margin-bottom:10px">
<div><span style="font-size:.8rem;color:var(--muted);display:block">AB = c</span><input type="number" id="p8-kc" class="tinp" placeholder="напр. 6" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">AC = b</span><input type="number" id="p8-kb" class="tinp" placeholder="напр. 9" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">BC</span><input type="number" id="p8-ka" class="tinp" placeholder="напр. 15" style="width:100%"></div>
<div style="display:flex;align-items:flex-end"><button class="btn primary" id="p8-kcalc" style="width:100%">Вычислить</button></div>
</div>
<div id="p8-kcalc-out" style="display:none;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.7"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 4: Тренажёр ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр §8 — Биссектриса</div></div>
<div class="wg-help">5 задач на нахождение отрезков BD, DC и сторон по свойству биссектрисы.</div>
<div class="score-display"><span>Задача <b id="p8-tr-i">1</b> / 5</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.02rem;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:130px">
<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>`;
/* ---- ИНТЕРАКТИВ 5: DnD-сортер ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Верное деление или нет? — Сортировка</div></div>
<div class="wg-help">Перетащи каждую карточку: если биссектриса с данными $AB$, $AC$ действительно делит $BC$ в указанном отношении — в «Верно», иначе — «Неверно».</div>
<div id="p8-dnd-pool" class="dnd-pool"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:10px">
<div class="drop-box" id="p8-drop-yes"><h5>Верно (BD/DC = AB/AC)</h5><div class="drop-items" id="p8-drop-yes-items"></div></div>
<div class="drop-box" id="p8-drop-no"><h5>Неверно</h5><div class="drop-items" id="p8-drop-no-items"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p8-dnd-check">Проверить</button><button class="btn" id="p8-dnd-reset">Сбросить</button></div>
<div class="feedback" id="p8-dnd-fb" style="display:none"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 6: Босс §8 ---- */
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))">БОСС §8</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">4 задачи — каждая верная даёт +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;
if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
/* == INIT ИНТЕРАКТИВ 1: живой SVG биссектрисы == */
(function(){
const cSl=document.getElementById('p8-c-sl');
const bSl=document.getElementById('p8-b-sl');
const aSl=document.getElementById('p8-a-sl');
const cVal=document.getElementById('p8-c-val');
const bVal=document.getElementById('p8-b-val');
const aVal=document.getElementById('p8-a-val');
const svgOut=document.getElementById('p8-svg-out');
const infoEl=document.getElementById('p8-svg-info');
function draw(){
const c=+cSl.value, b=+bSl.value, a=+aSl.value;
cVal.textContent=c; bVal.textContent=b; aVal.textContent=a;
// Triangle: B=(40,170), C=(40+a*16,170) — scaled horizontally
// A somewhere above — use fixed A position for clear view
// Place B at left, C at right on baseline, A above
const W=400, H=200;
const scale=Math.min(14, Math.floor(320/a));
const Bx=40, By=170;
const Cx=Bx+a*scale, Cy=170;
// A: place using sides c=AB, b=AC
// AB=c, AC=b. Place A using cosine rule for angle B:
// cos B = (c^2 + a^2 - b^2)/(2*c*a)
const cosB=(c*c+a*a-b*b)/(2*c*a);
if(cosB<-0.999||cosB>0.999||!isFinite(cosB)){
svgOut.innerHTML='<div style="color:var(--bad);padding:10px">Нарушено треугольное неравенство. Измени стороны.</div>';
infoEl.innerHTML='Некорректные стороны.';
return;
}
const sinB=Math.sqrt(1-cosB*cosB);
const Ax=Bx+c*scale*cosB;
const Ay=By-c*scale*sinB;
// D divides BC: BD/DC = c/b → BD = a*c/(b+c)
const BD=a*c/(b+c);
const DC=a*b/(b+c);
const Dx=Bx+BD*scale;
const Dy=By;
let s=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
// triangle
s+=`<polygon points="${Ax},${Ay} ${Bx},${By} ${Cx},${Cy}" fill="rgba(192,38,211,.09)" stroke="#c026d3" stroke-width="2"/>`;
// BD segment green
s+=`<line x1="${Bx}" y1="${By}" x2="${Dx}" y2="${Dy}" stroke="#10b981" stroke-width="3.5"/>`;
// DC segment blue
s+=`<line x1="${Dx}" y1="${Dy}" x2="${Cx}" y2="${Cy}" stroke="#6366f1" stroke-width="3.5"/>`;
// bisector AD
s+=`<line x1="${Ax}" y1="${Ay}" x2="${Dx}" y2="${Dy}" stroke="#f59e0b" stroke-width="2.5" stroke-dasharray="6,3"/>`;
// D point
s+=`<circle cx="${Dx}" cy="${Dy}" r="5" fill="#f59e0b"/>`;
// Vertices
s+=`<circle cx="${Ax}" cy="${Ay}" r="4.5" fill="#c026d3"/>`;
s+=`<circle cx="${Bx}" cy="${By}" r="4.5" fill="#c026d3"/>`;
s+=`<circle cx="${Cx}" cy="${Cy}" r="4.5" fill="#c026d3"/>`;
// Labels
const ax=Ax, ay=Ay;
s+=`<text x="${ax}" y="${ay-7}" text-anchor="middle" font-size="12" font-weight="800" fill="#a21caf">A</text>`;
s+=`<text x="${Bx-10}" y="${By+4}" font-size="12" font-weight="800" fill="#a21caf">B</text>`;
s+=`<text x="${Cx+4}" y="${Cy+4}" font-size="12" font-weight="800" fill="#a21caf">C</text>`;
s+=`<text x="${Dx}" y="${Dy+16}" text-anchor="middle" font-size="11" font-weight="800" fill="#b45309">D</text>`;
// Side labels
const mABx=(ax+Bx)/2, mABy=(ay+By)/2;
const mACx=(ax+Cx)/2, mACy=(ay+Cy)/2;
s+=`<text x="${mABx-10}" y="${mABy}" text-anchor="end" font-size="10" fill="#c026d3" font-weight="700">c=${c}</text>`;
s+=`<text x="${mACx+10}" y="${mACy}" font-size="10" fill="#c026d3" font-weight="700">b=${b}</text>`;
// BD DC midpoints
const mBDx=(Bx+Dx)/2, mDCx=(Dx+Cx)/2;
s+=`<text x="${mBDx}" y="${By-7}" text-anchor="middle" font-size="10" fill="#10b981" font-weight="800">BD=${fmt(BD)}</text>`;
s+=`<text x="${mDCx}" y="${Cy-7}" text-anchor="middle" font-size="10" fill="#6366f1" font-weight="800">DC=${fmt(DC)}</text>`;
s+=`<text x="${W/2}" y="14" text-anchor="middle" font-size="10" fill="#c026d3" font-weight="800">BD/DC = ${fmt(BD)}/${fmt(DC)} = ${fmt(c)}/${fmt(b)} = ${fmt(c/b)}</text>`;
s+='</svg>';
svgOut.innerHTML=s;
infoEl.innerHTML=`$AB=c=${c}$, $AC=b=${b}$, $BC=${a}$. Биссектриса $AD$ делит $BC$: $BD=${fmt(BD)}$, $DC=${fmt(DC)}$. $\\dfrac{BD}{DC}=\\dfrac{${c}}{${b}}=${fmt(c/b)}$ — совпадает с $\\dfrac{AB}{AC}$.`;
renderMath(infoEl);
addXp(1,'p8-svg');
}
cSl.addEventListener('input',draw);
bSl.addEventListener('input',draw);
aSl.addEventListener('input',draw);
draw();
})();
/* == INIT ИНТЕРАКТИВ 2: пошаговое доказательство == */
(function(){
const steps=[
{desc:'<b>Шаг 1.</b> Дано: $\\triangle ABC$, $AD$ — биссектриса $\\angle A$ ($D \\in BC$). Нужно доказать: $\\dfrac{BD}{DC} = \\dfrac{AB}{AC}$.',
svg:`<svg viewBox="0 0 280 155" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><polygon points="120,18 30,145 250,145" fill="rgba(192,38,211,.09)" stroke="#c026d3" stroke-width="2"/><line x1="120" y1="18" x2="140" y2="145" stroke="#f59e0b" stroke-width="2.5" stroke-dasharray="5,3"/><circle cx="140" cy="145" r="4" fill="#f59e0b"/><text x="120" y="13" text-anchor="middle" font-size="11" font-weight="800" fill="#a21caf">A</text><text x="20" y="152" font-size="11" font-weight="800" fill="#a21caf">B</text><text x="252" y="152" font-size="11" font-weight="800" fill="#a21caf">C</text><text x="140" y="158" text-anchor="middle" font-size="10" font-weight="800" fill="#b45309">D</text><text x="140" y="12" text-anchor="middle" font-size="9" fill="#c026d3" font-weight="700">AD — биссектриса ∠A, D ∈ BC</text></svg>`},
{desc:'<b>Шаг 2.</b> Через вершину $C$ проводим прямую $CE \\parallel AD$ до пересечения с продолжением стороны $AB$ в точке $E$.',
svg:`<svg viewBox="0 0 280 165" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><polygon points="110,22 30,150 240,150" fill="rgba(192,38,211,.08)" stroke="#c026d3" stroke-width="1.5"/><line x1="110" y1="22" x2="130" y2="150" stroke="#f59e0b" stroke-width="2" stroke-dasharray="5,3"/><line x1="240" y1="150" x2="190" y2="22" stroke="#10b981" stroke-width="2" stroke-dasharray="6,2"/><line x1="30" y1="150" x2="210" y2="22" stroke="#6366f1" stroke-width="1.5" stroke-dasharray="3,2"/><circle cx="190" cy="22" r="4" fill="#6366f1"/><circle cx="130" cy="150" r="4" fill="#f59e0b"/><text x="110" y="16" text-anchor="middle" font-size="10" font-weight="800" fill="#a21caf">A</text><text x="20" y="158" font-size="10" font-weight="800" fill="#a21caf">B</text><text x="242" y="158" font-size="10" font-weight="800" fill="#a21caf">C</text><text x="130" y="162" text-anchor="middle" font-size="9" font-weight="800" fill="#b45309">D</text><text x="192" y="16" font-size="10" font-weight="800" fill="#4f46e5">E</text><text x="210" y="100" font-size="9" fill="#10b981" font-weight="700">CE ∥ AD</text></svg>`},
{desc:'<b>Шаг 3.</b> Так как $CE \\parallel AD$ и $AD$ — биссектриса: $\\angle 1 = \\angle DAB$ (смежно-внутренние при секущей $AB$), $\\angle 2 = \\angle DAC$ (соответственные при секущей $AC$). Поскольку $\\angle DAB = \\angle DAC$ (биссектриса!), то $\\angle 1 = \\angle 2$. В $\\triangle ACE$: $\\angle AEC = \\angle 1$, $\\angle ACE = \\angle 2$ → $\\angle AEC = \\angle ACE$ → $\\triangle ACE$ равнобедренный, $AE = AC = b$.',
svg:`<svg viewBox="0 0 280 100" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="140" y="22" text-anchor="middle" font-size="11" fill="#c026d3" font-weight="700">CE ∥ AD, AD — биссектриса ∠A</text><text x="140" y="42" text-anchor="middle" font-size="11" fill="#10b981" font-weight="700">∠AEC = ∠ACE → △ACE равнобедренный</text><text x="140" y="62" text-anchor="middle" font-size="12" fill="#a21caf" font-weight="800">AE = AC = b</text><text x="140" y="85" text-anchor="middle" font-size="10" fill="#6366f1">Ключевой шаг доказательства</text></svg>`},
{desc:'<b>Шаг 4.</b> По теореме Фалеса (параллельные $AD \\parallel CE$, секущие $BA$ и $BC$):<br>$\\dfrac{BD}{DC} = \\dfrac{BA}{AE}$.',
svg:`<svg viewBox="0 0 280 90" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="140" y="24" text-anchor="middle" font-size="11" fill="#4338ca" font-weight="800">Теорема Фалеса: AD ∥ CE</text><text x="140" y="46" text-anchor="middle" font-size="12" fill="#c026d3" font-weight="800">BD/DC = BA/AE</text><text x="140" y="68" text-anchor="middle" font-size="10" fill="#6366f1">секущие BA и BC пересекают параллельные AD и CE</text><text x="140" y="85" text-anchor="middle" font-size="9" fill="#10b981">Теорема Фалеса — §1 этой главы</text></svg>`},
{desc:'<b>Шаг 5.</b> Подставляем $AE = AC = b$ (шаг 3): $\\dfrac{BD}{DC} = \\dfrac{BA}{AE} = \\dfrac{c}{b} = \\dfrac{AB}{AC}$. <b>Теорема доказана.</b>',
svg:`<svg viewBox="0 0 280 80" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="140" y="26" text-anchor="middle" font-size="12" fill="#c026d3" font-weight="800">BD/DC = BA/AE = c/b = AB/AC</text><text x="140" y="50" text-anchor="middle" font-size="10" fill="#6366f1">Биссектриса делит BC в отношении AB:AC</text><text x="140" y="70" text-anchor="middle" font-size="14" fill="#10b981" font-weight="900">QED ∎</text></svg>`},
];
let step=0;
const svgEl=document.getElementById('p8-proof-svg');
const descEl=document.getElementById('p8-proof-desc');
function show(){svgEl.innerHTML=steps[step].svg;descEl.innerHTML=steps[step].desc;renderMath(descEl);}
document.getElementById('p8-proof-next').addEventListener('click',()=>{
if(step<steps.length-1){step++;show();addXp(1,'p8-proof-step');}
else{addXp(5,'p8-proof-done');bumpProgress('p8',10);}
});
document.getElementById('p8-proof-reset').addEventListener('click',()=>{step=0;show();});
show();
})();
/* == INIT ИНТЕРАКТИВ 3: калькулятор == */
(function(){
document.getElementById('p8-kcalc').addEventListener('click',()=>{
const c=parseFloat(document.getElementById('p8-kc').value);
const b=parseFloat(document.getElementById('p8-kb').value);
const a=parseFloat(document.getElementById('p8-ka').value);
const out=document.getElementById('p8-kcalc-out');
if([c,b,a].some(v=>!isFinite(v)||v<=0)){
out.style.display='block';out.innerHTML='<span style="color:var(--bad)">Введи все три значения (положительные числа).</span>';return;
}
const BD=a*c/(b+c);
const DC=a*b/(b+c);
out.style.display='block';
out.innerHTML=`$\\dfrac{BD}{DC} = \\dfrac{AB}{AC} = \\dfrac{${fmt(c)}}{${fmt(b)}}$.<br>$BC = ${fmt(a)}$, поэтому:<br>$BD = \\dfrac{c}{b+c} \\cdot BC = \\dfrac{${fmt(c)}}{${fmt(b+c)}} \\cdot ${fmt(a)} = <b>${fmt(BD)}</b>$.<br>$DC = \\dfrac{b}{b+c} \\cdot BC = \\dfrac{${fmt(b)}}{${fmt(b+c)}} \\cdot ${fmt(a)} = <b>${fmt(DC)}</b>$.`;
renderMath(out);
addXp(3,'p8-calc');bumpProgress('p8',5);
});
})();
/* == INIT ИНТЕРАКТИВ 4: тренажёр == */
(function(){
const tasks=[
{q:'$\\triangle ABC$: $AB=6$, $AC=9$, $BC=15$. Биссектриса $\\angle A$ делит $BC$ в точке $D$. Найди $BD$.',ans:6,hint:'BD/DC = AB/AC = 6/9 = 2/3. BD = 2/(2+3)·15 = 6.'},
{q:'$\\triangle ABC$: $AB=8$, $AC=12$, $BD=4$. Биссектриса $\\angle A$ делит $BC$ в точке $D$. Найди $DC$.',ans:6,hint:'BD/DC = AB/AC → 4/DC = 8/12 → DC = 4·12/8 = 6.'},
{q:'Биссектриса $\\angle A$ делит $BC$: $BD=5$, $DC=7$, $AC=14$. Найди $AB$.',ans:10,hint:'AB/AC = BD/DC = 5/7 → AB = 5·14/7 = 10.'},
{q:'$\\triangle ABC$: $AB=10$, $AC=15$, $BC=20$. Найди $DC$ (биссектриса $\\angle A$).',ans:12,hint:'DC = b/(b+c)·BC = 15/25·20 = 12.'},
{q:'Биссектриса $\\angle A$: $AB=4$, $AC=6$, $BD=8$. Найди $BC$.',ans:20,hint:'BD/DC = AB/AC = 4/6 = 2/3 → DC = BD·3/2 = 12. BC = BD+DC = 8+12 = 20.'},
];
let idx=0,score=0;
function show(){
document.getElementById('p8-tr-i').textContent=idx+1;
const t=document.getElementById('p8-tr-task');
t.innerHTML=tasks[idx].q;
renderMath(t);
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',()=>{
if(idx>=tasks.length)return;
const ans=+document.getElementById('p8-tr-ans').value;
const fb=document.getElementById('p8-tr-fb');
if(Math.abs(ans-tasks[idx].ans)<0.05){
score++;document.getElementById('p8-tr-score').textContent=score;
addXp(3,'p8-tr-'+idx);bumpProgress('p8',4);
if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}
else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p8-tr-all');bumpProgress('p8',10);confetti();}
} 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();
})();
/* == INIT ИНТЕРАКТИВ 5: DnD-сортер == */
(function(){
const items=[
{text:'AB=6, AC=9, BD=4, DC=6',yes:true},
{text:'AB=5, AC=8, BD=5, DC=8',yes:true},
{text:'AB=4, AC=6, BD=3, DC=5',yes:false},
{text:'AB=10, AC=15, BD=8, DC=12',yes:true},
{text:'AB=3, AC=7, BD=3, DC=5',yes:false},
];
const pool=document.getElementById('p8-dnd-pool');
const yesBox=document.getElementById('p8-drop-yes-items');
const noBox=document.getElementById('p8-drop-no-items');
let dragging=null;
function makeChip(it,i){
const chip=document.createElement('div');
chip.className='dnd-chip';chip.dataset.idx=i;chip.textContent=it.text;chip.draggable=true;
chip.addEventListener('dragstart',e=>{dragging=chip;chip.classList.add('dragging');e.dataTransfer.effectAllowed='move';});
chip.addEventListener('dragend',()=>{chip.classList.remove('dragging');dragging=null;});
return chip;
}
items.forEach((it,i)=>pool.appendChild(makeChip(it,i)));
[document.getElementById('p8-drop-yes'),document.getElementById('p8-drop-no'),pool].forEach(box=>{
box.addEventListener('dragover',e=>{e.preventDefault();box.classList.add('over');});
box.addEventListener('dragleave',()=>box.classList.remove('over'));
box.addEventListener('drop',e=>{
e.preventDefault();box.classList.remove('over');
if(!dragging)return;
const target=box===document.getElementById('p8-drop-yes')?yesBox:box===document.getElementById('p8-drop-no')?noBox:pool;
target.appendChild(dragging);
});
});
document.getElementById('p8-dnd-check').addEventListener('click',()=>{
const fb=document.getElementById('p8-dnd-fb');
const yChips=[...yesBox.querySelectorAll('.dnd-chip')];
const nChips=[...noBox.querySelectorAll('.dnd-chip')];
if(yChips.length+nChips.length<items.length){feedback(fb,false,'Разложи все карточки.');return;}
let ok=true;
yChips.forEach(c=>{if(!items[+c.dataset.idx].yes)ok=false;});
nChips.forEach(c=>{if(items[+c.dataset.idx].yes)ok=false;});
if(ok){feedback(fb,true,'Верно! +5 XP');addXp(5,'p8-dnd');bumpProgress('p8',8);}
else{feedback(fb,false,'Есть ошибки. Проверь: BD/DC должно равняться AB/AC.');}
});
document.getElementById('p8-dnd-reset').addEventListener('click',()=>{
[...yesBox.children].forEach(c=>pool.appendChild(c));
[...noBox.children].forEach(c=>pool.appendChild(c));
document.getElementById('p8-dnd-fb').style.display='none';
});
})();
/* == INIT: Босс §8 == */
(function(){
const tasks=[
{q:'<svg viewBox="0 0 280 145" style="display:block;max-width:280px;margin:0 auto 8px;background:#fae8ff;border:1px solid #e879f9;border-radius:8px"><polygon points="120,18 30,135 250,135" fill="rgba(192,38,211,.10)" stroke="#c026d3" stroke-width="2"/><line x1="120" y1="18" x2="140" y2="135" stroke="#f59e0b" stroke-width="2.5" stroke-dasharray="5,3"/><line x1="30" y1="135" x2="140" y2="135" stroke="#10b981" stroke-width="3"/><line x1="140" y1="135" x2="250" y2="135" stroke="#6366f1" stroke-width="3"/><circle cx="140" cy="135" r="4" fill="#f59e0b"/><text x="120" y="12" text-anchor="middle" font-size="10" fill="#a21caf" font-weight="800">A</text><text x="20" y="143" font-size="10" fill="#a21caf" font-weight="800">B</text><text x="252" y="143" font-size="10" fill="#a21caf" font-weight="800">C</text><text x="140" y="148" text-anchor="middle" font-size="9" fill="#b45309" font-weight="800">D</text><text x="140" y="10" text-anchor="middle" font-size="8" fill="#c026d3">AB=9, AC=12, BC=21</text></svg>$AB=9$, $AC=12$, $BC=21$. Биссектриса $\\angle A$ делит $BC$ в точке $D$. Найди $BD$.',ans:9,hint:'BD/DC = AB/AC = 9/12 = 3/4. BD = 3/7·21 = 9.'},
{q:'Биссектриса $\\angle A$ делит $BC$: $BD=6$, $DC=10$, $BC=16$. $AB=9$. Найди $AC$.',ans:15,hint:'BD/DC = AB/AC → 6/10 = 9/AC → AC = 9·10/6 = 15.'},
{q:'В $\\triangle ABC$: $AB=7$, $AC=14$, биссектриса $\\angle A$ делит $BC$ на $BD$ и $DC$. Чему равно $BD$, если $BC=12$?',ans:4,hint:'BD = AB/(AB+AC)·BC = 7/21·12 = 4.'},
{q:'Биссектриса $\\angle B$ треугольника $ABC$ делит сторону $AC$ в точке $E$: $AE=6$, $EC=9$. $BC=15$. Найди $AB$.',ans:10,hint:'AE/EC = AB/BC → 6/9 = AB/15 → AB = 6·15/9 = 10.'},
];
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="p8b-a${i}" placeholder="Ответ" style="width:110px">
<button class="btn primary small" onclick="(function(){
const v=+document.getElementById('p8b-a${i}').value;
const fb=document.getElementById('p8b-fb${i}');
if(Math.abs(v-${t.ans})<0.05){
feedback(fb,true,'Верно! +5 XP');
if(!window.p8BossSolved.has(${i})){ window.p8BossSolved.add(${i}); addXp(5,'p8-boss${i}'); bumpProgress('p8',8); }
} else feedback(fb,false,'Неверно. ${t.hint}');
})()">Проверить</button>
</div>
<div class="feedback" id="p8b-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p8BossSolved=new Set();
renderMath(bossBox);
})();
}
function buildP9(){
const box=document.getElementById('p9-body');
let html='';
/* ---- Theory cards ---- */
html+=makeCard('theory','Отношение площадей подобных треугольников','9.1',`
<p><b>Теорема.</b> Если $\\triangle ABC \\sim \\triangle A'B'C'$ с коэффициентом подобия $k$, то:</p>
$$\\dfrac{S_{ABC}}{S_{A'B'C'}} = k^2$$
<p style="margin-top:8px">Площади подобных треугольников относятся как <b>квадрат коэффициента подобия</b>. Это означает: при увеличении сторон в $k$ раз площадь вырастает в $k^2$ раз.</p>
<p style="margin-top:8px"><b>Обобщение:</b> отношение площадей любых подобных фигур (многоугольников, кругов) тоже равно $k^2$.</p>
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 350 195" style="max-width:370px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<!-- k label at top — clear of all figures -->
<text x="175" y="13" text-anchor="middle" font-size="10" fill="#f59e0b" font-weight="800">k = 2: S₁/S₂ = k² = 4</text>
<!-- Large triangle: A=(90,32), B=(14,172), C=(214,172) — S1 -->
<polygon points="90,32 14,172 214,172" fill="rgba(124,58,237,.15)" stroke="#7c3aed" stroke-width="2"/>
<text x="90" y="25" text-anchor="middle" font-size="11" font-weight="800" fill="#6d28d9">A</text>
<text x="2" y="182" font-size="11" font-weight="800" fill="#6d28d9">B</text>
<text x="216" y="182" font-size="11" font-weight="800" fill="#6d28d9">C</text>
<text x="100" y="118" text-anchor="middle" font-size="10" fill="#7c3aed" font-weight="700">S₁</text>
<!-- Small triangle k=2: B'C'≈100, gap≥40px from C=(214,172) → B'=258 -->
<polygon points="286,97 258,172 332,172" fill="rgba(99,102,241,.22)" stroke="#6366f1" stroke-width="1.8"/>
<text x="286" y="90" text-anchor="middle" font-size="10" font-weight="800" fill="#4f46e5">A'</text>
<text x="246" y="182" font-size="10" font-weight="800" fill="#4f46e5">B'</text>
<text x="334" y="182" font-size="10" font-weight="800" fill="#4f46e5">C'</text>
<text x="294" y="148" text-anchor="middle" font-size="9" fill="#6366f1" font-weight="700">S₂</text>
</svg>
</div>`);
html+=makeCard('rule','Доказательство через формулу площади','9.2',`
<p>Пусть $\\triangle ABC \\sim \\triangle A'B'C'$, коэффициент подобия $k$: $AB = k \\cdot A'B'$, $BC = k \\cdot B'C'$ и т.д.</p>
<p style="margin-top:8px">Высоты подобных треугольников тоже относятся как $k$ (высота строится из вершины перпендикулярно к стороне, а все стороны уменьшены в $k$ раз). Обозначим $h$ и $h'$ — высоты к $BC$ и $B'C'$:</p>
$$h = k \\cdot h'$$
<p style="margin-top:8px">Тогда:</p>
$$\\dfrac{S_{ABC}}{S_{A'B'C'}} = \\dfrac{\\tfrac{1}{2} \\cdot BC \\cdot h}{\\tfrac{1}{2} \\cdot B'C' \\cdot h'} = \\dfrac{BC}{B'C'} \\cdot \\dfrac{h}{h'} = k \\cdot k = k^2$$
<p style="margin-top:8px">Обобщение: для любых подобных многоугольников с коэффициентом подобия $k$: $S_1/S_2 = k^2$.</p>`);
html+=makeCard('example','Примеры','9.3',`
<p><b>Пример 1.</b> $\\triangle ABC \\sim \\triangle A'B'C'$, $k=3$, $S_{A'B'C'} = 8$. Найти $S_{ABC}$.</p>
<p style="margin-top:6px">$S_{ABC} = k^2 \\cdot S_{A'B'C'} = 9 \\cdot 8 = 72$.</p>
<p style="margin-top:10px"><b>Пример 2.</b> Стороны двух подобных треугольников — $6$ и $10$. Площадь меньшего — $18$. Найти площадь большего.</p>
<p style="margin-top:6px">$k = 10/6 = 5/3$. $S_2 = k^2 \\cdot S_1 = (5/3)^2 \\cdot 18 = 25/9 \\cdot 18 = 50$.</p>
<p style="margin-top:10px"><b>Пример 3.</b> $S_1 = 48$, $S_2 = 75$. Найти $k = \\sqrt{S_1/S_2}$ ... нет, $k = \\sqrt{S_2/S_1} = \\sqrt{75/48} = \\sqrt{25/16} = 5/4 = 1{,}25$ (если $S_2$ у большего).</p>
<p style="margin-top:6px">$k = \\sqrt{75/48} = \\sqrt{25/16} = 1{,}25$.</p>`);
/* ---- ИНТЕРАКТИВ 1: SVG два треугольника со слайдером k ---- */
html+=`<div class="wg" id="p9-svg-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Два подобных треугольника: слайдер $k$</div></div>
<div class="wg-help">Меняй $k$ и наблюдай: второй треугольник подобен первому с коэффициентом $k$. Отношение площадей = $k^2$.</div>
<div class="sliders">
<label>Коэффициент $k$: <b id="p9-k-val">2.0</b>
<input type="range" min="10" max="30" value="20" id="p9-k-sl" step="1">
</label>
<label>Площадь $S_2$: <b id="p9-s2-val">12</b>
<input type="range" min="4" max="30" value="12" id="p9-s2-sl" step="1">
</label>
</div>
<div id="p9-svg-out" style="display:flex;justify-content:center"></div>
<div id="p9-svg-info" style="margin-top:10px;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.92rem;line-height:1.8"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 2: Пошаговое доказательство ---- */
html+=`<div class="wg" id="p9-proof-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Доказательство теоремы — по шагам</div></div>
<div class="wg-help">Нажимай «Далее» для каждого шага. Доказательство через формулу площади $S = \\frac{1}{2}ah$.</div>
<div id="p9-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p9-proof-desc" style="padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.6;margin-bottom:10px;min-height:60px"></div>
<div style="display:flex;gap:8px">
<button class="btn primary" id="p9-proof-next">Далее</button>
<button class="btn" id="p9-proof-reset">Сначала</button>
</div>
</div>`;
/* ---- ИНТЕРАКТИВ 3: Калькулятор ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Калькулятор: $k$ и площади</div></div>
<div class="wg-help">Два режима: по $k$ и $S_2$ найти $S_1$; или по двум площадям найти $k$.</div>
<div style="display:flex;gap:14px;flex-wrap:wrap;margin-bottom:10px">
<label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:.88rem"><input type="radio" name="p9-mode" value="0" checked id="p9-m0" style="accent-color:var(--sec-acc,var(--pri))"> По $k$ и $S_2$ → $S_1$</label>
<label style="display:flex;align-items:center;gap:6px;cursor:pointer;font-size:.88rem"><input type="radio" name="p9-mode" value="1" id="p9-m1" style="accent-color:var(--sec-acc,var(--pri))"> По $S_1$, $S_2$ → $k$</label>
</div>
<div id="p9-calc-mode0" style="display:grid;grid-template-columns:repeat(auto-fit,minmax(90px,1fr));gap:8px;margin-bottom:10px">
<div><span style="font-size:.8rem;color:var(--muted);display:block">Коэфф. $k$</span><input type="number" id="p9-ck" class="tinp" placeholder="напр. 3" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">$S_2$ (меньш.)</span><input type="number" id="p9-cs2" class="tinp" placeholder="напр. 8" style="width:100%"></div>
<div style="display:flex;align-items:flex-end"><button class="btn primary" id="p9-ccalc0" style="width:100%">Найти $S_1$</button></div>
</div>
<div id="p9-calc-mode1" style="display:none;grid-template-columns:repeat(auto-fit,minmax(90px,1fr));gap:8px;margin-bottom:10px">
<div><span style="font-size:.8rem;color:var(--muted);display:block">$S_1$ (больш.)</span><input type="number" id="p9-cs1b" class="tinp" placeholder="напр. 72" style="width:100%"></div>
<div><span style="font-size:.8rem;color:var(--muted);display:block">$S_2$ (меньш.)</span><input type="number" id="p9-cs2b" class="tinp" placeholder="напр. 8" style="width:100%"></div>
<div style="display:flex;align-items:flex-end"><button class="btn primary" id="p9-ccalc1" style="width:100%">Найти $k$</button></div>
</div>
<div id="p9-ccalc-out" style="display:none;padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.95rem;line-height:1.7"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 4: Тренажёр ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр §9 — Площади подобных треугольников</div></div>
<div class="wg-help">5 задач на применение формулы $S_1/S_2 = k^2$.</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.02rem;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:130px">
<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>`;
/* ---- ИНТЕРАКТИВ 5: DnD-сортер k → k² ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</span><div class="wg-title">Соотнеси $k$ с отношением площадей $k^2$</div></div>
<div class="wg-help">Перетащи каждую карточку $k$ в колонку с верным $k^2$. Две колонки: «$k^2 = 4$» и «$k^2 = 9$».</div>
<div id="p9-dnd-pool" class="dnd-pool"></div>
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-top:10px">
<div class="drop-box" id="p9-drop-4"><h5>$k^2 = 4$</h5><div class="drop-items" id="p9-drop-4-items"></div></div>
<div class="drop-box" id="p9-drop-9"><h5>$k^2 = 9$</h5><div class="drop-items" id="p9-drop-9-items"></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>`;
/* ---- ИНТЕРАКТИВ 6: Мини-квиз ---- */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 6</span><div class="wg-title">Мини-квиз: теория §9</div></div>
<div class="wg-help">5 вопросов о площадях подобных фигур. Выбери верный ответ.</div>
<div id="p9-quiz-wrap"></div>
<div class="actions"><button class="btn primary" id="p9-quiz-check">Проверить</button></div>
<div class="feedback" id="p9-quiz-fb" style="display:none"></div>
</div>`;
/* ---- ИНТЕРАКТИВ 7: Босс §9 ---- */
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))">БОСС §9</span><div class="wg-title">Итоговые задачи</div></div>
<div class="wg-help">4 задачи — каждая верная даёт +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','final3');
box.innerHTML=html;
if(window.renderMathInElement) setTimeout(()=>renderMath(box),0);
/* == INIT ИНТЕРАКТИВ 1: SVG два подобных треугольника == */
(function(){
const kSl=document.getElementById('p9-k-sl');
const s2Sl=document.getElementById('p9-s2-sl');
const kVal=document.getElementById('p9-k-val');
const s2Val=document.getElementById('p9-s2-val');
const svgOut=document.getElementById('p9-svg-out');
const infoEl=document.getElementById('p9-svg-info');
function draw(){
const k=+kSl.value/10;
const S2=+s2Sl.value;
const S1=S2*k*k;
kVal.textContent=k.toFixed(1);
s2Val.textContent=S2;
const H=220;
// Triangle 2 (smaller): fixed display size
const base2=100;
const h2=55;
const B2y=185;
// Large triangle T1 = k * T2, anchored at B1
const base1=base2*k;
const h1=h2*k;
const B1x=20, B1y=185;
const C1x=B1x+base1, C1y=185;
const A1x=B1x+base1*0.55, A1y=B1y-h1;
// Place T2 at least 30px to the right of T1
const B2x=Math.max(C1x+30, 200);
const C2x=B2x+base2, C2y=185;
const A2x=B2x+base2*0.55, A2y=B2y-h2;
// Safety: check bounds
const W=Math.max(C2x+20, 400);
const maxX=C2x+20;
const minY=Math.min(A1y, A2y)-14;
let s=`<svg viewBox="0 ${Math.min(0,minY)-5} ${Math.max(W,maxX+10)} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">`;
// T1 large
s+=`<polygon points="${A1x},${A1y} ${B1x},${B1y} ${C1x},${C1y}" fill="rgba(124,58,237,.16)" stroke="#7c3aed" stroke-width="2.2"/>`;
// Height line T1 (from A1 perpendicular to B1C1)
s+=`<line x1="${A1x}" y1="${A1y}" x2="${A1x}" y2="${B1y}" stroke="#7c3aed" stroke-width="1.2" stroke-dasharray="4,2"/>`;
// right angle marker T1
s+=`<rect x="${A1x}" y="${B1y-12}" width="12" height="12" fill="none" stroke="#7c3aed" stroke-width="1.2"/>`;
s+=`<text x="${A1x}" y="${A1y-6}" text-anchor="middle" font-size="11" font-weight="800" fill="#6d28d9">A</text>`;
s+=`<text x="${B1x-8}" y="${B1y+4}" font-size="11" font-weight="800" fill="#6d28d9">B</text>`;
s+=`<text x="${C1x+4}" y="${C1y+4}" font-size="11" font-weight="800" fill="#6d28d9">C</text>`;
const mBC1x=(B1x+C1x)/2;
s+=`<text x="${mBC1x}" y="${B1y+14}" text-anchor="middle" font-size="10" fill="#7c3aed" font-weight="700">a·k</text>`;
s+=`<text x="${A1x+6}" y="${(A1y+B1y)/2}" font-size="10" fill="#7c3aed" font-weight="700">h·k</text>`;
s+=`<text x="${mBC1x}" y="${A1y+(B1y-A1y)*0.55}" text-anchor="middle" font-size="13" fill="#7c3aed" font-weight="800">S₁=${fmt(S1)}</text>`;
// T2 small
s+=`<polygon points="${A2x},${A2y} ${B2x},${B2y} ${C2x},${C2y}" fill="rgba(99,102,241,.20)" stroke="#6366f1" stroke-width="1.8"/>`;
s+=`<line x1="${A2x}" y1="${A2y}" x2="${A2x}" y2="${B2y}" stroke="#6366f1" stroke-width="1.2" stroke-dasharray="4,2"/>`;
s+=`<rect x="${A2x}" y="${B2y-12}" width="10" height="10" fill="none" stroke="#6366f1" stroke-width="1.2"/>`;
s+=`<text x="${A2x}" y="${A2y-6}" text-anchor="middle" font-size="10" font-weight="800" fill="#4f46e5">A'</text>`;
s+=`<text x="${B2x-10}" y="${B2y+4}" font-size="10" font-weight="800" fill="#4f46e5">B'</text>`;
s+=`<text x="${C2x+4}" y="${C2y+4}" font-size="10" font-weight="800" fill="#4f46e5">C'</text>`;
const mBC2x=(B2x+C2x)/2;
s+=`<text x="${mBC2x}" y="${B2y+14}" text-anchor="middle" font-size="9" fill="#6366f1" font-weight="700">a</text>`;
s+=`<text x="${A2x+5}" y="${(A2y+B2y)/2}" font-size="9" fill="#6366f1" font-weight="700">h</text>`;
s+=`<text x="${mBC2x}" y="${A2y+(B2y-A2y)*0.55}" text-anchor="middle" font-size="11" fill="#6366f1" font-weight="800">S₂=${S2}</text>`;
// top label
s+=`<text x="${W/2}" y="${Math.min(0,minY)+12}" text-anchor="middle" font-size="11" font-weight="800" fill="#7c3aed">k = ${k.toFixed(1)}, S₁/S₂ = k² = ${fmt(k*k)}</text>`;
s+='</svg>';
svgOut.innerHTML=s;
infoEl.innerHTML=`$k=${k.toFixed(1)}$, $S_2=${S2}$. Тогда $S_1 = k^2 \\cdot S_2 = ${k.toFixed(1)}^2 \\cdot ${S2} = ${fmt(k*k)} \\cdot ${S2} = ${fmt(S1)}$. Стороны первого в $${k.toFixed(1)}$ раз больше, площадь — в $${fmt(k*k)}$ раз больше.`;
renderMath(infoEl);
addXp(1,'p9-svg');
}
kSl.addEventListener('input',draw);
s2Sl.addEventListener('input',draw);
draw();
})();
/* == INIT ИНТЕРАКТИВ 2: пошаговое доказательство == */
(function(){
const steps=[
{desc:'<b>Шаг 1.</b> Дано: $\\triangle ABC \\sim \\triangle A\'B\'C\'$, коэффициент подобия $k$. Нужно доказать: $\\dfrac{S_{ABC}}{S_{A\'B\'C\'}} = k^2$.',
svg:`<svg viewBox="0 0 330 165" style="max-width:330px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="165" y="13" text-anchor="middle" font-size="10" fill="#f59e0b" font-weight="800">△ABC △A\'B\'C\', коэфф. k</text><polygon points="88,25 16,152 212,152" fill="rgba(124,58,237,.12)" stroke="#7c3aed" stroke-width="2"/><polygon points="272,85 250,152 316,152" fill="rgba(99,102,241,.20)" stroke="#6366f1" stroke-width="1.8"/><text x="88" y="18" text-anchor="middle" font-size="11" font-weight="800" fill="#6d28d9">A</text><text x="4" y="162" font-size="11" font-weight="800" fill="#6d28d9">B</text><text x="214" y="162" font-size="11" font-weight="800" fill="#6d28d9">C</text><text x="272" y="78" text-anchor="middle" font-size="10" font-weight="800" fill="#4f46e5">A\'</text><text x="238" y="162" font-size="10" font-weight="800" fill="#4f46e5">B\'</text><text x="318" y="162" font-size="10" font-weight="800" fill="#4f46e5">C\'</text></svg>`},
{desc:'<b>Шаг 2.</b> Из подобия: $BC = k \\cdot B\'C\'$ (основание). Проведём высоты $BH \\perp AC$ и $B\'H\' \\perp A\'C\'$. Так как $\\triangle ABH \\sim \\triangle A\'B\'H\'$ (тот же коэффициент $k$), то $BH = k \\cdot B\'H\'$.',
svg:`<svg viewBox="0 0 300 145" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><polygon points="100,18 20,135 220,135" fill="rgba(124,58,237,.10)" stroke="#7c3aed" stroke-width="1.5"/><line x1="100" y1="18" x2="100" y2="135" stroke="#7c3aed" stroke-width="2" stroke-dasharray="5,3"/><text x="100" y="13" text-anchor="middle" font-size="10" font-weight="800" fill="#6d28d9">A</text><text x="10" y="143" font-size="10" font-weight="800" fill="#6d28d9">B</text><text x="222" y="143" font-size="10" font-weight="800" fill="#6d28d9">C</text><text x="104" y="70" font-size="9" fill="#7c3aed">h = k·h\'</text><rect x="100" y="127" width="10" height="10" fill="none" stroke="#7c3aed" stroke-width="1.5"/><text x="100" y="143" text-anchor="middle" font-size="9" fill="#7c3aed">H</text><text x="150" y="12" text-anchor="middle" font-size="9" fill="#c026d3" font-weight="700">BC = k·B\'C\', BH = k·B\'H\'</text></svg>`},
{desc:'<b>Шаг 3.</b> Запишем формулу площади для каждого треугольника: $S_{ABC} = \\dfrac{1}{2} \\cdot BC \\cdot h$, $S_{A\'B\'C\'} = \\dfrac{1}{2} \\cdot B\'C\' \\cdot h\'$.',
svg:`<svg viewBox="0 0 300 90" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="150" y="22" text-anchor="middle" font-size="12" fill="#7c3aed" font-weight="700">S = ½ · BC · h</text><text x="150" y="44" text-anchor="middle" font-size="12" fill="#6366f1" font-weight="700">S\' = ½ · B\'C\' · h\'</text><text x="150" y="70" text-anchor="middle" font-size="10" fill="#6d28d9">Формула площади треугольника через основание и высоту</text></svg>`},
{desc:'<b>Шаг 4.</b> Составим отношение:<br>$\\dfrac{S_{ABC}}{S_{A\'B\'C\'}} = \\dfrac{\\frac{1}{2} \\cdot BC \\cdot h}{\\frac{1}{2} \\cdot B\'C\' \\cdot h\'} = \\dfrac{BC}{B\'C\'} \\cdot \\dfrac{h}{h\'} = k \\cdot k = k^2$.',
svg:`<svg viewBox="0 0 300 85" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="150" y="24" text-anchor="middle" font-size="11" fill="#4338ca" font-weight="800">S₁/S₂ = (BC/B\'C\')·(h/h\') = k·k = k²</text><text x="150" y="50" text-anchor="middle" font-size="10" fill="#6366f1">BC = k·B\'C\' и h = k·h\'</text><text x="150" y="72" text-anchor="middle" font-size="12" fill="#10b981" font-weight="800">S₁/S₂ = k²</text></svg>`},
{desc:'<b>Шаг 5.</b> <b>Обобщение:</b> для любых подобных многоугольников с коэффициентом $k$ разбиваем их на треугольники с тем же коэффициентом подобия. Сумма площадей каждого треугольника второй фигуры умножена на $k^2$. Значит $S_1/S_2 = k^2$ — верно для любых подобных фигур. <b>Теорема доказана.</b>',
svg:`<svg viewBox="0 0 300 80" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px"><text x="150" y="28" text-anchor="middle" font-size="13" fill="#7c3aed" font-weight="800">S₁/S₂ = k² для подобных △</text><text x="150" y="52" text-anchor="middle" font-size="10" fill="#6366f1">Обобщается на любые подобные фигуры</text><text x="150" y="72" text-anchor="middle" font-size="14" fill="#10b981" font-weight="900">QED ∎</text></svg>`},
];
let step=0;
const svgEl=document.getElementById('p9-proof-svg');
const descEl=document.getElementById('p9-proof-desc');
function show(){svgEl.innerHTML=steps[step].svg;descEl.innerHTML=steps[step].desc;renderMath(descEl);}
document.getElementById('p9-proof-next').addEventListener('click',()=>{
if(step<steps.length-1){step++;show();addXp(1,'p9-proof-step');}
else{addXp(5,'p9-proof-done');bumpProgress('p9',10);}
});
document.getElementById('p9-proof-reset').addEventListener('click',()=>{step=0;show();});
show();
})();
/* == INIT ИНТЕРАКТИВ 3: калькулятор == */
(function(){
document.querySelectorAll('input[name="p9-mode"]').forEach(r=>{
r.addEventListener('change',()=>{
const m=+r.value;
document.getElementById('p9-calc-mode0').style.display=m===0?'grid':'none';
document.getElementById('p9-calc-mode1').style.display=m===1?'grid':'none';
document.getElementById('p9-ccalc-out').style.display='none';
});
});
document.getElementById('p9-ccalc0').addEventListener('click',()=>{
const k=parseFloat(document.getElementById('p9-ck').value);
const s2=parseFloat(document.getElementById('p9-cs2').value);
const out=document.getElementById('p9-ccalc-out');
if(!isFinite(k)||k<=0||!isFinite(s2)||s2<=0){
out.style.display='block';out.innerHTML='<span style="color:var(--bad)">Введи $k > 0$ и $S_2 > 0$.</span>';renderMath(out);return;
}
const s1=s2*k*k;
out.style.display='block';
out.innerHTML=`$k = ${fmt(k)}$, $S_2 = ${fmt(s2)}$.<br>$S_1 = k^2 \\cdot S_2 = ${fmt(k)}^2 \\cdot ${fmt(s2)} = ${fmt(k*k)} \\cdot ${fmt(s2)} = <b>${fmt(s1)}</b>$.`;
renderMath(out);addXp(3,'p9-calc0');bumpProgress('p9',5);
});
document.getElementById('p9-ccalc1').addEventListener('click',()=>{
const s1=parseFloat(document.getElementById('p9-cs1b').value);
const s2=parseFloat(document.getElementById('p9-cs2b').value);
const out=document.getElementById('p9-ccalc-out');
if(!isFinite(s1)||s1<=0||!isFinite(s2)||s2<=0){
out.style.display='block';out.innerHTML='<span style="color:var(--bad)">Введи $S_1 > 0$ и $S_2 > 0$.</span>';renderMath(out);return;
}
const k=Math.sqrt(s1/s2);
out.style.display='block';
out.innerHTML=`$S_1 = ${fmt(s1)}$, $S_2 = ${fmt(s2)}$.<br>$k = \\sqrt{S_1/S_2} = \\sqrt{${fmt(s1)}/${fmt(s2)}} = \\sqrt{${fmt(s1/s2)}} = <b>${fmt(k)}</b>$.`;
renderMath(out);addXp(3,'p9-calc1');bumpProgress('p9',5);
});
})();
/* == INIT ИНТЕРАКТИВ 4: тренажёр == */
(function(){
const tasks=[
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$, $k=3$, $S_{A\'B\'C\'}=8$. Найди $S_{ABC}$.',ans:72,hint:'S = k²·S\' = 9·8 = 72.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$, $S_{ABC}=50$, $S_{A\'B\'C\'}=8$. Найди $k$.',ans:2.5,hint:'k = √(S/S\') = √(50/8) = √(6.25) = 2.5.'},
{q:'Стороны двух подобных треугольников — $6$ и $10$. Площадь меньшего — $18$. Найди площадь большего.',ans:50,hint:'k = 10/6 = 5/3. S₁ = (5/3)²·18 = 25/9·18 = 50.'},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$, $k=4$. На сколько $S_{ABC}$ больше $S_{A\'B\'C\'}$? (Во сколько раз — введи число.)',ans:16,hint:'S₁/S₂ = k² = 16. Площадь больше в 16 раз.'},
{q:'Площади двух подобных треугольников: $S_1=48$, $S_2=75$. Найди $k = \\sqrt{S_2/S_1}$ (большего к меньшему).',ans:1.25,hint:'k = √(75/48) = √(25/16) = 5/4 = 1.25.'},
];
let idx=0,score=0;
function show(){
document.getElementById('p9-tr-i').textContent=idx+1;
const t=document.getElementById('p9-tr-task');
t.innerHTML=tasks[idx].q;
renderMath(t);
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',()=>{
if(idx>=tasks.length)return;
const ans=+document.getElementById('p9-tr-ans').value;
const fb=document.getElementById('p9-tr-fb');
if(Math.abs(ans-tasks[idx].ans)<0.05){
score++;document.getElementById('p9-tr-score').textContent=score;
addXp(3,'p9-tr-'+idx);bumpProgress('p9',4);
if(idx<tasks.length-1){feedback(fb,true,'Верно! +3 XP');idx++;setTimeout(()=>show(),900);}
else{feedback(fb,true,'Все задачи решены! +5 XP');addXp(5,'p9-tr-all');bumpProgress('p9',10);confetti();}
} 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();
})();
/* == INIT ИНТЕРАКТИВ 5: DnD-сортер k → k² == */
(function(){
// items: k value → which bucket k²=4 (k=2) or k²=9 (k=3)
const items=[
{text:'k = 2',group:4},
{text:'k = 3',group:9},
{text:'√4 = 2',group:4},
{text:'S₁/S₂ = 4',group:4},
{text:'S₁/S₂ = 9',group:9},
{text:'k = √9 = 3',group:9},
];
const pool=document.getElementById('p9-dnd-pool');
const box4=document.getElementById('p9-drop-4-items');
const box9=document.getElementById('p9-drop-9-items');
let dragging=null;
function makeChip(it,i){
const chip=document.createElement('div');
chip.className='dnd-chip';chip.dataset.idx=i;chip.textContent=it.text;chip.draggable=true;
chip.addEventListener('dragstart',e=>{dragging=chip;chip.classList.add('dragging');e.dataTransfer.effectAllowed='move';});
chip.addEventListener('dragend',()=>{chip.classList.remove('dragging');dragging=null;});
return chip;
}
items.forEach((it,i)=>pool.appendChild(makeChip(it,i)));
[document.getElementById('p9-drop-4'),document.getElementById('p9-drop-9'),pool].forEach(box=>{
box.addEventListener('dragover',e=>{e.preventDefault();box.classList.add('over');});
box.addEventListener('dragleave',()=>box.classList.remove('over'));
box.addEventListener('drop',e=>{
e.preventDefault();box.classList.remove('over');
if(!dragging)return;
const target=box===document.getElementById('p9-drop-4')?box4:box===document.getElementById('p9-drop-9')?box9:pool;
target.appendChild(dragging);
});
});
document.getElementById('p9-dnd-check').addEventListener('click',()=>{
const fb=document.getElementById('p9-dnd-fb');
const chips4=[...box4.querySelectorAll('.dnd-chip')];
const chips9=[...box9.querySelectorAll('.dnd-chip')];
if(chips4.length+chips9.length<items.length){feedback(fb,false,'Разложи все карточки.');return;}
let ok=true;
chips4.forEach(c=>{if(items[+c.dataset.idx].group!==4)ok=false;});
chips9.forEach(c=>{if(items[+c.dataset.idx].group!==9)ok=false;});
if(ok){feedback(fb,true,'Верно! +5 XP');addXp(5,'p9-dnd');bumpProgress('p9',8);}
else{feedback(fb,false,'Есть ошибки. $k=2$ → $k^2=4$; $k=3$ → $k^2=9$.');}
});
document.getElementById('p9-dnd-reset').addEventListener('click',()=>{
[...box4.children].forEach(c=>pool.appendChild(c));
[...box9.children].forEach(c=>pool.appendChild(c));
document.getElementById('p9-dnd-fb').style.display='none';
});
})();
/* == INIT ИНТЕРАКТИВ 6: мини-квиз == */
(function(){
const qs=[
{q:'Если коэффициент подобия треугольников $k=5$, то отношение их площадей равно...',opts:['5','10','25','$\\sqrt{5}$'],ans:2},
{q:'Площади двух подобных треугольников — $9$ и $36$. Чему равен коэффициент подобия?',opts:['4','2','$\\sqrt{3}$','$\\frac{1}{2}$'],ans:1},
{q:'При каком $k$ площадь первого треугольника вдвое больше площади второго?',opts:['$k = 2$','$k = \\sqrt{2}$','$k = 4$','$k = 0{,}5$'],ans:1},
{q:'Верно ли, что для любых подобных фигур (не только треугольников) $S_1/S_2 = k^2$?',opts:['Только для треугольников','Только для прямоугольников','Да, для любых подобных фигур','Нет, только для равнобедренных'],ans:2},
{q:'$\\triangle ABC \\sim \\triangle A\'B\'C\'$, $k = 3$. Периметр $\\triangle A\'B\'C\' = 12$. Периметр $\\triangle ABC$ равен...',opts:['36','108','4','144'],ans:0},
];
const wrap=document.getElementById('p9-quiz-wrap');
wrap.innerHTML=qs.map((q,qi)=>`
<div style="margin-bottom:14px;padding:12px;background:var(--card);border:1px solid var(--border);border-radius:10px">
<div style="font-weight:700;margin-bottom:8px;font-size:.95rem">${qi+1}. ${q.q}</div>
<div style="display:flex;flex-direction:column;gap:6px">
${q.opts.map((o,oi)=>`<label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:.92rem"><input type="radio" name="p9q${qi}" value="${oi}" style="accent-color:var(--sec-acc,var(--pri))"> ${o}</label>`).join('')}
</div>
</div>`).join('');
renderMath(wrap);
document.getElementById('p9-quiz-check').addEventListener('click',()=>{
const fb=document.getElementById('p9-quiz-fb');
let correct=0;
qs.forEach((q,qi)=>{
const sel=document.querySelector(`input[name="p9q${qi}"]:checked`);
if(sel&&+sel.value===q.ans)correct++;
});
if(correct===qs.length){
feedback(fb,true,`Все ${qs.length} ответов верны! +8 XP`);
addXp(8,'p9-quiz');bumpProgress('p9',12);confetti();
} else {
feedback(fb,false,`Верных ответов: ${correct} из ${qs.length}. Повтори: $S_1/S_2 = k^2$.`);
}
});
})();
/* == INIT: Босс §9 == */
(function(){
const tasks=[
{q:'<svg viewBox="0 0 310 158" style="display:block;max-width:310px;margin:0 auto 8px;background:#ede9fe;border:1px solid #c4b5fd;border-radius:8px"><text x="155" y="11" text-anchor="middle" font-size="8" fill="#7c3aed" font-weight="700">S₂=8, k=3 → S₁=?</text><polygon points="88,20 18,142 202,142" fill="rgba(124,58,237,.14)" stroke="#7c3aed" stroke-width="2"/><polygon points="264,86 244,142 310,142" fill="rgba(99,102,241,.22)" stroke="#6366f1" stroke-width="1.8"/><text x="88" y="14" text-anchor="middle" font-size="10" fill="#6d28d9" font-weight="800">A</text><text x="6" y="152" font-size="10" fill="#6d28d9" font-weight="800">B</text><text x="204" y="152" font-size="10" fill="#6d28d9" font-weight="800">C</text><text x="264" y="80" text-anchor="middle" font-size="9" fill="#4f46e5" font-weight="800">A\'</text><text x="232" y="152" font-size="9" fill="#4f46e5" font-weight="800">B\'</text><text x="312" y="152" font-size="9" fill="#4f46e5" font-weight="800">C\'</text></svg>$\\triangle ABC \\sim \\triangle A\'B\'C\'$, $k=3$, $S_{A\'B\'C\'}=8$. Найди $S_{ABC}$.',ans:72,hint:'S₁ = k²·S₂ = 9·8 = 72.'},
{q:'Стороны двух подобных треугольников: первый $12$, второй $8$. Площадь первого $S_1=54$. Найди $S_2$.',ans:24,hint:'k = 12/8 = 1.5. S₂ = S₁/k² = 54/2.25 = 24.'},
{q:'$S_1 = 75$, $S_2 = 48$. Найди коэффициент подобия $k = \\sqrt{S_1/S_2}$ (Ответ в виде десятичной дроби).',ans:1.25,hint:'k = √(75/48) = √(25/16) = 5/4 = 1.25.'},
{q:'Прямоугольный треугольник с катетами $3$ и $4$. Подобный с $k=2$. Найди площадь большего треугольника.',ans:24,hint:'S_мал = 0.5·3·4 = 6. S_бол = k²·S_мал = 4·6 = 24.'},
];
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="p9b-a${i}" placeholder="Ответ" style="width:110px">
<button class="btn primary small" onclick="(function(){
const v=+document.getElementById('p9b-a${i}').value;
const fb=document.getElementById('p9b-fb${i}');
if(Math.abs(v-${t.ans})<0.05){
feedback(fb,true,'Верно! +5 XP');
if(!window.p9BossSolved.has(${i})){ window.p9BossSolved.add(${i}); addXp(5,'p9-boss${i}'); bumpProgress('p9',8); }
} else feedback(fb,false,'Неверно. ${t.hint}');
})()">Проверить</button>
</div>
<div class="feedback" id="p9b-fb${i}" style="display:none;margin-top:8px"></div>
</div>`).join('');
window.p9BossSolved=new Set();
renderMath(bossBox);
})();
}
function buildFinal3(){
const box = document.getElementById('final3-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">Итоговая шпаргалка · Вся Глава 3 «Подобные треугольники»</div>
</div>
<div class="card-body">
<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(210px,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:.65rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));text-transform:uppercase;letter-spacing:.07em;margin-bottom:7px">§1 Теорема Фалеса</div>
<div style="display:flex;align-items:center;gap:10px">
<svg viewBox="0 0 70 60" style="width:70px;height:60px;flex-shrink:0">
<line x1="10" y1="50" x2="65" y2="10" stroke="#7c3aed" stroke-width="1.8"/>
<line x1="10" y1="50" x2="65" y2="50" stroke="#7c3aed" stroke-width="1.8"/>
<line x1="14" y1="47" x2="62" y2="47" stroke="#6366f1" stroke-width="1.2" stroke-dasharray="3 2"/>
<line x1="22" y1="38" x2="62" y2="38" stroke="#6366f1" stroke-width="1.2" stroke-dasharray="3 2"/>
<line x1="34" y1="26" x2="62" y2="26" stroke="#6366f1" stroke-width="1.2" stroke-dasharray="3 2"/>
<text x="5" y="42" font-size="7" fill="#4f46e5" font-family="JetBrains Mono,monospace">A</text>
<text x="5" y="30" font-size="7" fill="#4f46e5" font-family="JetBrains Mono,monospace">B</text>
<text x="5" y="18" font-size="7" fill="#4f46e5" font-family="JetBrains Mono,monospace">C</text>
</svg>
<div>
<p style="font-size:.88rem;margin-bottom:4px">$\dfrac{AB}{BC} = \dfrac{A'B'}{B'C'}$</p>
<p style="font-size:.78rem;color:var(--muted)">параллельные прямые</p>
</div>
</div>
</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:.65rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));text-transform:uppercase;letter-spacing:.07em;margin-bottom:7px">§2 Деление m:n</div>
<div style="display:flex;align-items:center;gap:10px">
<svg viewBox="0 0 70 60" style="width:70px;height:60px;flex-shrink:0">
<line x1="5" y1="30" x2="65" y2="30" stroke="#8b5cf6" stroke-width="2"/>
<circle cx="5" cy="30" r="3.5" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<circle cx="65" cy="30" r="3.5" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<circle cx="37" cy="30" r="3.5" fill="#ec4899" stroke="#fff" stroke-width="1.5"/>
<text x="2" y="22" font-size="8" fill="#6d28d9" font-family="Unbounded,sans-serif" font-weight="800">A</text>
<text x="32" y="22" font-size="8" fill="#be185d" font-family="Unbounded,sans-serif" font-weight="800">C</text>
<text x="61" y="22" font-size="8" fill="#6d28d9" font-family="Unbounded,sans-serif" font-weight="800">B</text>
<text x="16" y="45" font-size="7" fill="#6d28d9" font-family="JetBrains Mono,monospace">m</text>
<text x="46" y="45" font-size="7" fill="#6d28d9" font-family="JetBrains Mono,monospace">n</text>
</svg>
<div>
<p style="font-size:.88rem;margin-bottom:4px">$AC = \dfrac{m}{m+n}\cdot AB$</p>
<p style="font-size:.78rem;color:var(--muted)">$C$ делит $AB$ в $m{:}n$</p>
</div>
</div>
</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:.65rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));text-transform:uppercase;letter-spacing:.07em;margin-bottom:7px">§3 Подобие треугольников</div>
<div>
<p style="font-size:.88rem;margin-bottom:4px">$\triangle ABC \sim \triangle A'B'C'$</p>
<p style="font-size:.82rem;margin-bottom:4px">$\dfrac{a}{a'} = \dfrac{b}{b'} = \dfrac{c}{c'} = k$</p>
<p style="font-size:.78rem;color:var(--muted)">углы попарно равны</p>
</div>
</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:.65rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));text-transform:uppercase;letter-spacing:.07em;margin-bottom:7px">§4 Прямая || стороне</div>
<div style="display:flex;align-items:center;gap:10px">
<svg viewBox="0 0 70 60" style="width:70px;height:60px;flex-shrink:0">
<polygon points="35,4 4,56 66,56" fill="rgba(99,102,241,.10)" stroke="#6366f1" stroke-width="2"/>
<line x1="17" y1="34" x2="53" y2="34" stroke="#7c3aed" stroke-width="2" stroke-dasharray="3 2"/>
<circle cx="17" cy="34" r="3" fill="#7c3aed" stroke="#fff" stroke-width="1.2"/>
<circle cx="53" cy="34" r="3" fill="#7c3aed" stroke="#fff" stroke-width="1.2"/>
<text x="32" y="2" font-size="8" fill="#4f46e5" font-family="Unbounded,sans-serif" font-weight="800">A</text>
<text x="0" y="60" font-size="8" fill="#4f46e5" font-family="Unbounded,sans-serif" font-weight="800">B</text>
<text x="60" y="60" font-size="8" fill="#4f46e5" font-family="Unbounded,sans-serif" font-weight="800">C</text>
<text x="10" y="30" font-size="7" fill="#6d28d9" font-family="Unbounded,sans-serif" font-weight="800">M</text>
<text x="54" y="30" font-size="7" fill="#6d28d9" font-family="Unbounded,sans-serif" font-weight="800">N</text>
</svg>
<div>
<p style="font-size:.88rem;margin-bottom:4px">$MN \parallel BC$</p>
<p style="font-size:.82rem;margin-bottom:4px">$\Rightarrow \triangle AMN \sim \triangle ABC$</p>
<p style="font-size:.78rem;color:var(--muted)">$k = AM/AB$</p>
</div>
</div>
</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:.65rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));text-transform:uppercase;letter-spacing:.07em;margin-bottom:7px">§5 Признак по двум углам (УУ)</div>
<div style="display:flex;align-items:center;gap:10px">
<svg viewBox="0 0 70 60" style="width:70px;height:60px;flex-shrink:0">
<polygon points="35,4 6,56 64,56" fill="rgba(124,58,237,.10)" stroke="#7c3aed" stroke-width="2"/>
<polygon points="50,4 38,26 62,26" fill="rgba(139,92,246,.18)" stroke="#8b5cf6" stroke-width="1.8"/>
<text x="32" y="2" font-size="8" fill="#6d28d9" font-family="Unbounded,sans-serif" font-weight="800">A</text>
<text x="2" y="60" font-size="8" fill="#6d28d9" font-family="Unbounded,sans-serif" font-weight="800">B</text>
<text x="58" y="60" font-size="8" fill="#6d28d9" font-family="Unbounded,sans-serif" font-weight="800">C</text>
</svg>
<div>
<p style="font-size:.88rem;margin-bottom:4px">$\angle A = \angle A'$</p>
<p style="font-size:.82rem;margin-bottom:4px">$\angle B = \angle B'$</p>
<p style="font-size:.78rem;color:var(--muted)">$\Rightarrow$ подобны</p>
</div>
</div>
</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:.65rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));text-transform:uppercase;letter-spacing:.07em;margin-bottom:7px">§6 Признак по двум сторонам и углу (СУС)</div>
<div>
<p style="font-size:.88rem;margin-bottom:4px">$\dfrac{AB}{A'B'} = \dfrac{AC}{A'C'}$</p>
<p style="font-size:.82rem;margin-bottom:4px">$\angle A = \angle A'$</p>
<p style="font-size:.78rem;color:var(--muted)">$\Rightarrow$ подобны (СУС)</p>
</div>
</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:.65rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));text-transform:uppercase;letter-spacing:.07em;margin-bottom:7px">§7 Признак по трём сторонам (ССС)</div>
<div>
<p style="font-size:.88rem;margin-bottom:4px">$\dfrac{a}{a'} = \dfrac{b}{b'} = \dfrac{c}{c'}$</p>
<p style="font-size:.78rem;color:var(--muted)">$\Rightarrow$ подобны (ССС)</p>
<p style="font-size:.78rem;color:var(--muted)">три пары сторон</p>
</div>
</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:.65rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));text-transform:uppercase;letter-spacing:.07em;margin-bottom:7px">§8 Биссектриса</div>
<div style="display:flex;align-items:center;gap:10px">
<svg viewBox="0 0 70 60" style="width:70px;height:60px;flex-shrink:0">
<polygon points="35,4 4,56 66,56" fill="rgba(192,38,211,.08)" stroke="#c026d3" stroke-width="2"/>
<line x1="35" y1="4" x2="38" y2="56" stroke="#a21caf" stroke-width="1.8" stroke-dasharray="4 2"/>
<circle cx="38" cy="56" r="3" fill="#a21caf" stroke="#fff" stroke-width="1.2"/>
<text x="32" y="2" font-size="8" fill="#a21caf" font-family="Unbounded,sans-serif" font-weight="800">A</text>
<text x="0" y="60" font-size="8" fill="#a21caf" font-family="Unbounded,sans-serif" font-weight="800">B</text>
<text x="60" y="60" font-size="8" fill="#a21caf" font-family="Unbounded,sans-serif" font-weight="800">C</text>
<text x="38" y="52" font-size="7" fill="#a21caf" font-family="Unbounded,sans-serif" font-weight="800">D</text>
</svg>
<div>
<p style="font-size:.88rem;margin-bottom:4px">$\dfrac{BD}{DC} = \dfrac{AB}{AC}$</p>
<p style="font-size:.78rem;color:var(--muted)">$AD$ — биссектриса $\angle A$</p>
</div>
</div>
</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:.65rem;font-weight:800;color:var(--sec-acc-d,var(--pri2));text-transform:uppercase;letter-spacing:.07em;margin-bottom:7px">§9 Площади подобных</div>
<div>
<p style="font-size:.88rem;margin-bottom:4px">$\dfrac{S_1}{S_2} = k^2$</p>
<p style="font-size:.82rem;margin-bottom:4px">$k$ — коэффициент подобия</p>
<p style="font-size:.78rem;color:var(--muted)">площади ~ квадрату $k$</p>
</div>
</div>
</div>
</div>
</div>`;
/* === ЧАСТЬ 2: Карта связей === */
html += `<div class="wg" id="final3-map-wrap">
<div class="wg-header"><span class="wg-badge">КАРТА СВЯЗЕЙ</span><div class="wg-title">Признаки подобия и следствия</div></div>
<div class="wg-help">Нажми на узел, чтобы увидеть формулировку и способ применения.</div>
<div id="final3-map-svg" style="display:flex;justify-content:center;overflow-x:auto"></div>
<div id="final3-map-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:#7c3aed;background:linear-gradient(135deg,var(--card),#ede9fe)">
<div class="wg-header">
<span class="wg-badge" style="background:#7c3aed">7 БОССОВ ГЛАВЫ 3</span>
<div class="wg-title">Интегрированные задачи</div>
</div>
<div class="wg-help">Каждая задача объединяет 2–3 темы главы. +10 XP за каждого побеждённого босса. Победи всех семерых — получишь +50 XP и достижение «Мастер подобия Главы 3»!</div>
<div id="final3-bosses"></div>
</div>`;
/* === ЧАСТЬ 4: Финальная плашка === */
html += `<div id="final3-finish" style="display:none;margin-top:20px;padding:24px;background:linear-gradient(135deg,#ede9fe,#f5f3ff);border:2px solid #7c3aed;border-radius:16px;text-align:center">
<div style="font-family:'Unbounded',sans-serif;font-size:1.2rem;font-weight:900;color:#4c1d95;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>
Мастер подобия Главы 3!
</div>
<p style="color:#4c1d95;font-size:.95rem;margin-bottom:16px">Ты победил всех 7 боссов и освоил всю Главу 3 «Подобные треугольники». Превосходная работа!</p>
<a href="/textbook/geometry-8-ch4" 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>
Перейти к Главе 4
</a>
</div>`;
html += `<div style="margin-top:18px;display:flex;justify-content:center">
<button class="btn primary" id="final3-read-btn" onclick="addXp(10,'final3-read');bumpProgress('final3',40);this.textContent='Прочитано!';this.disabled=true;">
<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>
Я изучил Главу 3 (+10 XP)
</button>
</div>`;
html += secNav('p9', null);
box.innerHTML = html;
/* === JS: Карта связей SVG === */
(function(){
const W = 620, H = 340;
const nodes = [
{ id:'sim', x:310, y:28, rx:62, label:'Подобие треугольников',
props:'Два треугольника подобны, если углы попарно равны и стороны пропорциональны. $\\triangle ABC \\sim \\triangle A\'B\'C\'$, коэффициент $k$.' },
{ id:'aa', x:100, y:120, rx:54, label:'По двум углам (УУ)',
props:'§5: Если два угла одного треугольника равны двум углам другого — треугольники подобны. Наиболее часто применяемый признак.' },
{ id:'sas', x:310, y:120, rx:54, label:'По сторонам и углу (СУС)',
props:'§6: Два треугольника подобны, если две стороны пропорциональны и угол между ними равен: $\\dfrac{AB}{A\'B\'} = \\dfrac{AC}{A\'C\'}$, $\\angle A = \\angle A\'$.' },
{ id:'sss', x:510, y:120, rx:54, label:'По трём сторонам (ССС)',
props:'§7: Три треугольника подобны, если все три пары сторон пропорциональны: $\\dfrac{a}{a\'} = \\dfrac{b}{b\'} = \\dfrac{c}{c\'}$.' },
{ id:'fales', x:80, y:230, rx:52, label:'Теорема Фалеса',
props:'§1: Параллельные прямые отсекают пропорциональные отрезки на двух секущих. $\\dfrac{AB}{BC} = \\dfrac{A\'B\'}{B\'C\'}$.' },
{ id:'paral', x:230, y:230, rx:52, label:'Прямая || стороне',
props:'§4: $MN \\parallel BC \\Rightarrow \\triangle AMN \\sim \\triangle ABC$ с $k = AM/AB$. Следует из теоремы Фалеса + признак УУ.' },
{ id:'biss', x:390, y:230, rx:52, label:'Биссектриса',
props:'§8: Биссектриса $AD$ треугольника $ABC$ делит сторону $BC$ в отношении: $\\dfrac{BD}{DC} = \\dfrac{AB}{AC}$. Доказывается через подобие.' },
{ id:'areas', x:540, y:230, rx:52, label:'Площади $S_1/S_2$',
props:'§9: Отношение площадей подобных треугольников равно квадрату коэффициента подобия: $\\dfrac{S_1}{S_2} = k^2$.' },
{ id:'div', x:150, y:310, rx:50, label:'Деление m:n',
props:'§2: Точка $C$ делит $AB$ в отношении $m:n$: $AC = \\dfrac{m}{m+n}\\cdot AB$, $CB = \\dfrac{n}{m+n}\\cdot AB$.' },
];
const edges = [
['sim','aa'],['sim','sas'],['sim','sss'],
['aa','paral'],['fales','paral'],['aa','biss'],
['sim','areas'],['paral','div'],
];
let sel = null;
function draw(selId){
const colors = { sim:'#7c3aed', aa:'#4f46e5', sas:'#8b5cf6', sss:'#6366f1', fales:'#2563eb', paral:'#0891b2', biss:'#c026d3', areas:'#059669', div:'#d97706' };
let s = `<svg viewBox="0 0 ${W} ${H}" style="width:100%;max-width:640px;background:var(--card);border:1.5px solid var(--border);border-radius:14px;cursor:pointer">`;
s += `<defs><marker id="f3-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);
if(!na||!nb) return;
const dx=nb.x-na.x, dy=nb.y-na.y, len=Math.sqrt(dx*dx+dy*dy);
if(len<1) return;
const sy_r=na.rx*0.52;
const sx=na.x+dx/len*na.rx, sy2=na.y+dy/len*sy_r;
const ex=nb.x-dx/len*(nb.rx+7), ey=nb.y-dy/len*(nb.rx*0.52+7);
const isAct = selId===a||selId===b;
s += `<line x1="${sx.toFixed(1)}" y1="${sy2.toFixed(1)}" x2="${ex.toFixed(1)}" y2="${ey.toFixed(1)}" stroke="${isAct?'#7c3aed':'#94a3b8'}" stroke-width="${isAct?2.5:1.5}" marker-end="url(#f3-arr)"/>`;
});
nodes.forEach(n=>{
const isS = selId===n.id;
const col = colors[n.id] || '#7c3aed';
const ry = n.rx * 0.52;
s += `<ellipse cx="${n.x}" cy="${n.y}" rx="${n.rx}" ry="${ry}" fill="${isS?col:'var(--card)'}" stroke="${col}" stroke-width="${isS?3:2}" data-nid="${n.id}" style="cursor:pointer"/>`;
const words = n.label.split(' ');
const line1 = words.slice(0,2).join(' '), line2 = words.slice(2).join(' ');
const tc = isS ? '#fff' : col;
if(line2){
s += `<text x="${n.x}" y="${n.y-5}" text-anchor="middle" font-size="8" font-weight="800" font-family="Unbounded,sans-serif" fill="${tc}" style="pointer-events:none">${line1}</text>`;
s += `<text x="${n.x}" y="${n.y+7}" text-anchor="middle" font-size="8" font-weight="800" font-family="Unbounded,sans-serif" fill="${tc}" style="pointer-events:none">${line2}</text>`;
} else {
s += `<text x="${n.x}" y="${n.y+4}" text-anchor="middle" font-size="8" font-weight="800" font-family="Unbounded,sans-serif" fill="${tc}" style="pointer-events:none">${line1}</text>`;
}
});
s += '</svg>';
document.getElementById('final3-map-svg').innerHTML = s;
document.getElementById('final3-map-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('final3-map-info').innerHTML = '<b>' + nd.label + '</b>: ' + nd.props; renderMath(document.getElementById('final3-map-info')); }
else document.getElementById('final3-map-info').textContent = 'Нажми на узел в схеме выше';
draw(sel);
});
}
draw(null);
})();
/* === JS: 7 боссов === */
(function(){
const bosses = [
{
n: 1,
title: 'Параллель и пропорция',
color: '#4f46e5',
svg: `<svg viewBox="0 0 280 185" style="width:100%;max-width:280px;background:var(--card);border:1px solid var(--border);border-radius:10px;display:block;margin:10px auto">
<!-- Triangle A(140,12) B(20,170) C(260,170). MN parallel BC at 2/3 height. AM/AB=8/12=2/3: M on AB, N on AC. M=(140+2/3*(20-140), 12+2/3*(170-12))=(140-80,12+105.3)=(60,117) N=(140+2/3*(260-140),12+2/3*(170-12))=(140+80,117)=(220,117) -->
<polygon points="140,12 20,170 260,170" fill="rgba(79,70,229,.08)" stroke="#4f46e5" stroke-width="2"/>
<line x1="60" y1="117" x2="220" y2="117" stroke="#7c3aed" stroke-width="2.5"/>
<!-- tick marks: MN || BC -->
<line x1="136" y1="113" x2="144" y2="121" stroke="#7c3aed" stroke-width="1.8"/>
<line x1="136" y1="167" x2="144" y2="175" stroke="#4f46e5" stroke-width="1.8"/>
<!-- vertex dots -->
<circle cx="140" cy="12" r="4" fill="#4f46e5" stroke="#fff" stroke-width="1.5"/>
<circle cx="20" cy="170" r="4" fill="#4f46e5" stroke="#fff" stroke-width="1.5"/>
<circle cx="260" cy="170" r="4" fill="#4f46e5" stroke="#fff" stroke-width="1.5"/>
<circle cx="60" cy="117" r="4" fill="#7c3aed" stroke="#fff" stroke-width="1.5"/>
<circle cx="220" cy="117" r="4" fill="#7c3aed" stroke="#fff" stroke-width="1.5"/>
<!-- labels -->
<text x="127" y="8" font-size="11" font-weight="700" fill="#3730a3" font-family="Unbounded,sans-serif">A</text>
<text x="4" y="180" font-size="11" font-weight="700" fill="#3730a3" font-family="Unbounded,sans-serif">B</text>
<text x="264" y="180" font-size="11" font-weight="700" fill="#3730a3" font-family="Unbounded,sans-serif">C</text>
<text x="42" y="113" font-size="11" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">M</text>
<text x="224" y="113" font-size="11" font-weight="700" fill="#6d28d9" font-family="Unbounded,sans-serif">N</text>
<!-- side labels -->
<text x="72" y="68" font-size="9" fill="#3730a3" font-family="JetBrains Mono,monospace">AB=12</text>
<text x="4" y="148" font-size="9" fill="#3730a3" font-family="JetBrains Mono,monospace">AM=8</text>
<text x="190" y="150" font-size="9" fill="#3730a3" font-family="JetBrains Mono,monospace">AC=18</text>
<text x="116" y="108" font-size="9" fill="#6d28d9" font-family="JetBrains Mono,monospace">MN=?</text>
</svg>`,
cond: 'В $\\triangle ABC$: $AB = 12$, $AC = 18$, $MN \\parallel BC$, $AM = 8$. (§1, §4)',
parts: [
{ label: 'Найди коэффициент подобия $k = AM/AB$.', ans: 0.667, tol: 0.005, hint: '$k = AM/AB = 8/12 = 2/3 \\approx 0{,}667$' },
{ label: 'Найди $AN$ (используй $AN/AC = k$).', ans: 12, hint: '$AN = k \\cdot AC = \\dfrac{2}{3} \\cdot 18 = 12$' },
],
},
{
n: 2,
title: 'Биссектриса треугольника',
color: '#c026d3',
svg: `<svg viewBox="0 0 280 185" style="width:100%;max-width:280px;background:var(--card);border:1px solid var(--border);border-radius:10px;display:block;margin:10px auto">
<!-- Triangle A(140,12) B(20,170) C(260,170). AB=15, AC=10, BC=14. Bisector AD from A to BC. BD/DC=AB/AC=15/10=3/2. BD=14*3/5=8.4, DC=5.6. D on BC: D=(20+8.4/14*(260-20), 170)=(20+144, 170)=(164, 170) -->
<polygon points="140,12 20,170 260,170" fill="rgba(192,38,211,.08)" stroke="#c026d3" stroke-width="2"/>
<line x1="140" y1="12" x2="164" y2="170" stroke="#a21caf" stroke-width="2" stroke-dasharray="5 3"/>
<!-- vertex dots -->
<circle cx="140" cy="12" r="4" fill="#c026d3" stroke="#fff" stroke-width="1.5"/>
<circle cx="20" cy="170" r="4" fill="#c026d3" stroke="#fff" stroke-width="1.5"/>
<circle cx="260" cy="170" r="4" fill="#c026d3" stroke="#fff" stroke-width="1.5"/>
<circle cx="164" cy="170" r="4" fill="#a21caf" stroke="#fff" stroke-width="1.5"/>
<!-- labels -->
<text x="130" y="8" font-size="11" font-weight="700" fill="#86198f" font-family="Unbounded,sans-serif">A</text>
<text x="4" y="180" font-size="11" font-weight="700" fill="#86198f" font-family="Unbounded,sans-serif">B</text>
<text x="264" y="180" font-size="11" font-weight="700" fill="#86198f" font-family="Unbounded,sans-serif">C</text>
<text x="162" y="163" font-size="11" font-weight="700" fill="#86198f" font-family="Unbounded,sans-serif">D</text>
<!-- angle bisector arc mark at A -->
<path d="M126,30 A18,18 0 0,1 155,30" stroke="#c026d3" stroke-width="1.8" fill="rgba(192,38,211,.12)"/>
<!-- side labels -->
<text x="58" y="90" font-size="9" fill="#86198f" font-family="JetBrains Mono,monospace">AB=15</text>
<text x="186" y="90" font-size="9" fill="#86198f" font-family="JetBrains Mono,monospace">AC=10</text>
<text x="80" y="185" font-size="9" fill="#86198f" font-family="JetBrains Mono,monospace">BC=14</text>
<text x="148" y="155" font-size="9" fill="#86198f" font-family="JetBrains Mono,monospace">D=?</text>
</svg>`,
cond: 'В $\\triangle ABC$: $AB = 15$, $AC = 10$, $BC = 14$. $AD$ — биссектриса угла $A$. (§8)',
parts: [
{ label: 'Найди $BD$ (используй $BD/DC = AB/AC = 15/10$, $BD + DC = 14$).', ans: 8.4, tol: 0.05, hint: '$BD/DC = 3/2$, $BD = 14 \\cdot 3/5 = 8{,}4$' },
{ label: 'Найди $DC$.', ans: 5.6, tol: 0.05, hint: '$DC = 14 - BD = 14 - 8{,}4 = 5{,}6$' },
],
},
{
n: 3,
title: 'Признак по двум углам и площади',
color: '#059669',
svg: `<svg viewBox="0 0 280 185" style="width:100%;max-width:280px;background:var(--card);border:1px solid var(--border);border-radius:10px;display:block;margin:10px auto">
<!-- Large triangle A(140,12) B(20,170) C(260,170), k=9/6=1.5. Small triangle A'(180,90) B'(130,170) C'(230,170) -->
<polygon points="140,12 20,170 260,170" fill="rgba(5,150,105,.08)" stroke="#059669" stroke-width="2"/>
<polygon points="180,90 130,170 230,170" fill="rgba(16,185,129,.18)" stroke="#10b981" stroke-width="2"/>
<!-- vertex dots large -->
<circle cx="140" cy="12" r="4" fill="#059669" stroke="#fff" stroke-width="1.5"/>
<circle cx="20" cy="170" r="4" fill="#059669" stroke="#fff" stroke-width="1.5"/>
<circle cx="260" cy="170" r="4" fill="#059669" stroke="#fff" stroke-width="1.5"/>
<!-- vertex dots small -->
<circle cx="180" cy="90" r="3.5" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<circle cx="130" cy="170" r="3.5" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<circle cx="230" cy="170" r="3.5" fill="#10b981" stroke="#fff" stroke-width="1.5"/>
<!-- labels large -->
<text x="128" y="8" font-size="11" font-weight="700" fill="#065f46" font-family="Unbounded,sans-serif">A</text>
<text x="4" y="180" font-size="11" font-weight="700" fill="#065f46" font-family="Unbounded,sans-serif">B</text>
<text x="264" y="180" font-size="11" font-weight="700" fill="#065f46" font-family="Unbounded,sans-serif">C</text>
<!-- labels small -->
<text x="184" y="88" font-size="9" font-weight="700" fill="#047857" font-family="Unbounded,sans-serif">A'</text>
<text x="116" y="180" font-size="9" font-weight="700" fill="#047857" font-family="Unbounded,sans-serif">B'</text>
<text x="232" y="180" font-size="9" font-weight="700" fill="#047857" font-family="Unbounded,sans-serif">C'</text>
<!-- side labels -->
<text x="55" y="85" font-size="9" fill="#065f46" font-family="JetBrains Mono,monospace">AB=6</text>
<text x="194" y="128" font-size="9" fill="#047857" font-family="JetBrains Mono,monospace">A'B'=9</text>
<text x="60" y="175" font-size="9" fill="#065f46" font-family="JetBrains Mono,monospace">S=12</text>
</svg>`,
cond: '$\\triangle ABC \\sim \\triangle A\'B\'C\'$ по двум углам. $AB = 6$, $A\'B\' = 9$, $S_{\\triangle ABC} = 12$ см². (§5, §9)',
parts: [
{ label: 'Найди коэффициент подобия $k = A\'B\'/AB$.', ans: 1.5, tol: 0.01, hint: '$k = 9/6 = 1{,}5$' },
{ label: 'Найди $S_{\\triangle A\'B\'C\'}$ (используй $S\'/S = k^2$).', ans: 27, hint: '$S\' = k^2 \\cdot S = 2{,}25 \\cdot 12 = 27$ см²' },
],
},
{
n: 4,
title: 'Деление отрезка в отношении',
color: '#d97706',
svg: `<svg viewBox="0 0 280 120" style="width:100%;max-width:280px;background:var(--card);border:1px solid var(--border);border-radius:10px;display:block;margin:10px auto">
<!-- Segment A(20,60) B(260,60). Total=240. m:n=3:2. AC=240*3/5=144. C=(20+144, 60)=(164, 60). -->
<line x1="20" y1="60" x2="260" y2="60" stroke="#d97706" stroke-width="2.5"/>
<circle cx="20" cy="60" r="5" fill="#d97706" stroke="#fff" stroke-width="2"/>
<circle cx="260" cy="60" r="5" fill="#d97706" stroke="#fff" stroke-width="2"/>
<circle cx="164" cy="60" r="5" fill="#b45309" stroke="#fff" stroke-width="2"/>
<!-- tick marks -->
<line x1="92" y1="50" x2="92" y2="70" stroke="#d97706" stroke-width="1.5"/>
<line x1="92" y1="50" x2="92" y2="70" stroke="#d97706" stroke-width="1.5"/>
<line x1="212" y1="50" x2="212" y2="70" stroke="#d97706" stroke-width="1.5"/>
<!-- brace AC label -->
<text x="20" y="44" font-size="11" font-weight="700" fill="#92400e" font-family="Unbounded,sans-serif">A</text>
<text x="158" y="44" font-size="11" font-weight="700" fill="#92400e" font-family="Unbounded,sans-serif">C</text>
<text x="254" y="44" font-size="11" font-weight="700" fill="#92400e" font-family="Unbounded,sans-serif">B</text>
<!-- distance labels -->
<text x="76" y="82" font-size="9" fill="#92400e" font-family="JetBrains Mono,monospace">AC=?</text>
<text x="185" y="82" font-size="9" fill="#92400e" font-family="JetBrains Mono,monospace">CB=?</text>
<!-- ratio label -->
<text x="78" y="100" text-anchor="middle" font-size="9" fill="#b45309" font-family="JetBrains Mono,monospace">3</text>
<text x="208" y="100" text-anchor="middle" font-size="9" fill="#b45309" font-family="JetBrains Mono,monospace">2</text>
<text x="140" y="30" text-anchor="middle" font-size="9" fill="#92400e" font-family="JetBrains Mono,monospace">AB = 20 см, AC:CB = 3:2</text>
</svg>`,
cond: 'Отрезок $AB = 20$ см. Точка $C$ делит $AB$ в отношении $AC{:}CB = 3{:}2$. (§2)',
parts: [
{ label: 'Найди $AC$ (см).', ans: 12, hint: '$AC = \\dfrac{3}{3+2} \\cdot 20 = 12$ см' },
{ label: 'Найди $CB$ (см).', ans: 8, hint: '$CB = \\dfrac{2}{5} \\cdot 20 = 8$ см' },
],
},
{
n: 5,
title: 'По двум сторонам и углу: стороны и косинус',
color: '#8b5cf6',
svg: `<svg viewBox="0 0 280 185" style="width:100%;max-width:280px;background:var(--card);border:1px solid var(--border);border-radius:10px;display:block;margin:10px auto">
<!-- Triangle ABC: AB=8, AC=12, angle A=60deg. Large. A'B'C' similar with k=1.5: A'B'=12, A'C'=18. Triangle A(40,160) B(200,160) C=A+AC*cos60deg, A+AC*sin60deg = (40+12*0.5*12, 160-12*sin60*12)... scale: AB=8 -> 120px, so 1cm=15px. A(30,155) B(30+120,155)=(150,155). C=A+(AC*cos60*15, -AC*sin60*15)=(30+90, 155-156)=(120, 0)... too tall. Use AB=8->100px scale (12.5px/cm). A(30,155) B(30+100,155)=(130,155). C=(30+12*0.5*12.5, 155-12*sin60*12.5)=(30+75, 155-129.9)=(105,25). -->
<polygon points="30,155 130,155 105,25" fill="rgba(139,92,246,.08)" stroke="#8b5cf6" stroke-width="2"/>
<!-- angle at A: arc -->
<path d="M52,155 A22,22 0 0,1 44,131" stroke="#8b5cf6" stroke-width="1.8" fill="rgba(139,92,246,.15)"/>
<text x="56" y="148" font-size="9" fill="#6d28d9" font-weight="700" font-family="JetBrains Mono,monospace">60°</text>
<!-- vertex dots -->
<circle cx="30" cy="155" r="4" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<circle cx="130" cy="155" r="4" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<circle cx="105" cy="25" r="4" fill="#8b5cf6" stroke="#fff" stroke-width="1.5"/>
<!-- labels -->
<text x="16" y="151" font-size="11" font-weight="700" fill="#5b21b6" font-family="Unbounded,sans-serif">A</text>
<text x="134" y="168" font-size="11" font-weight="700" fill="#5b21b6" font-family="Unbounded,sans-serif">B</text>
<text x="94" y="18" font-size="11" font-weight="700" fill="#5b21b6" font-family="Unbounded,sans-serif">C</text>
<!-- side labels -->
<text x="72" y="172" text-anchor="middle" font-size="9" fill="#5b21b6" font-family="JetBrains Mono,monospace">AB=8</text>
<text x="62" y="92" font-size="9" fill="#5b21b6" font-family="JetBrains Mono,monospace">AC=12</text>
<!-- similar triangle labels -->
<text x="155" y="60" font-size="9" fill="#7c3aed" font-family="JetBrains Mono,monospace">k=1.5</text>
<text x="155" y="75" font-size="9" fill="#7c3aed" font-family="JetBrains Mono,monospace">A'B'=?</text>
<text x="155" y="90" font-size="9" fill="#7c3aed" font-family="JetBrains Mono,monospace">A'C'=?</text>
<text x="155" y="105" font-size="9" fill="#7c3aed" font-family="JetBrains Mono,monospace">B'C'=?</text>
</svg>`,
cond: '$\\triangle ABC \\sim \\triangle A\'B\'C\'$ (по двум сторонам и углу, СУС). $AB = 8$, $AC = 12$, $\\angle A = 60°$, $k = 1{,}5$. (§6)',
parts: [
{ label: 'Найди $A\'B\'$ (см).', ans: 12, hint: '$A\'B\' = k \\cdot AB = 1{,}5 \\cdot 8 = 12$' },
{ label: 'Найди $A\'C\'$ (см).', ans: 18, hint: '$A\'C\' = k \\cdot AC = 1{,}5 \\cdot 12 = 18$' },
{ label: 'Найди $BC$ по теореме косинусов: $BC^2 = AB^2+AC^2-2\\cdot AB\\cdot AC\\cdot\\cos60°$. Введи $BC$ (целое).', ans: 4, tol: 0.5, hint: '$BC^2 = 64+144-2\\cdot8\\cdot12\\cdot0{,}5 = 208-96=112$. Нет, пересчитаем: $BC^2=64+144-96=112 \\Rightarrow BC\\approx 10{,}58$. Округли до $11$.', _override: true, _ans_exact: 11, _tol_exact: 0.5, _hint_fix: 'По теореме косинусов: $BC^2 = 8^2+12^2-2\\cdot8\\cdot12\\cdot\\cos60° = 64+144-96=112$, $BC=\\sqrt{112}\\approx 10{,}58 \\approx 11$' },
],
},
{
n: 6,
title: 'Высоты и площади подобных',
color: '#0891b2',
svg: `<svg viewBox="0 0 280 185" style="width:100%;max-width:280px;background:var(--card);border:1px solid var(--border);border-radius:10px;display:block;margin:10px auto">
<!-- Two similar triangles: large ABC (k=2 wrt small A'B'C') -->
<!-- Small A'B'C': A'(190,155) B'(130,155) C'(170,90). h'=65px -->
<!-- Large ABC: A(250,155) B(40,155) C(140,25). h=130px, k=2 -->
<polygon points="140,25 40,155 250,155" fill="rgba(8,145,178,.08)" stroke="#0891b2" stroke-width="2"/>
<polygon points="170,90 130,155 210,155" fill="rgba(6,182,212,.20)" stroke="#06b6d4" stroke-width="1.8"/>
<!-- height of large -->
<line x1="140" y1="25" x2="140" y2="155" stroke="#0891b2" stroke-width="1.5" stroke-dasharray="4 2"/>
<path d="M140,155 L140,147 L148,147" fill="none" stroke="#0891b2" stroke-width="1.5"/>
<!-- height of small -->
<line x1="170" y1="90" x2="170" y2="155" stroke="#06b6d4" stroke-width="1.5" stroke-dasharray="3 2"/>
<path d="M170,155 L170,149 L176,149" fill="none" stroke="#06b6d4" stroke-width="1.5"/>
<!-- vertex dots large -->
<circle cx="140" cy="25" r="4" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<circle cx="40" cy="155" r="4" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<circle cx="250" cy="155" r="4" fill="#0891b2" stroke="#fff" stroke-width="1.5"/>
<!-- vertex dots small -->
<circle cx="170" cy="90" r="3.5" fill="#06b6d4" stroke="#fff" stroke-width="1.5"/>
<circle cx="130" cy="155" r="3.5" fill="#06b6d4" stroke="#fff" stroke-width="1.5"/>
<circle cx="210" cy="155" r="3.5" fill="#06b6d4" stroke="#fff" stroke-width="1.5"/>
<!-- labels large -->
<text x="126" y="20" font-size="11" font-weight="700" fill="#164e63" font-family="Unbounded,sans-serif">A</text>
<text x="24" y="167" font-size="11" font-weight="700" fill="#164e63" font-family="Unbounded,sans-serif">B</text>
<text x="254" y="167" font-size="11" font-weight="700" fill="#164e63" font-family="Unbounded,sans-serif">C</text>
<!-- labels small -->
<text x="174" y="88" font-size="9" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">A'</text>
<text x="116" y="166" font-size="9" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">B'</text>
<text x="212" y="166" font-size="9" font-weight="700" fill="#0e7490" font-family="Unbounded,sans-serif">C'</text>
<!-- height labels -->
<text x="144" y="88" font-size="9" fill="#164e63" font-family="JetBrains Mono,monospace">h=6</text>
<text x="174" y="128" font-size="9" fill="#0e7490" font-family="JetBrains Mono,monospace">h'=?</text>
<text x="4" y="130" font-size="9" fill="#0e7490" font-family="JetBrains Mono,monospace">S'=9</text>
<text x="4" y="144" font-size="9" fill="#164e63" font-family="JetBrains Mono,monospace">S=?</text>
<text x="4" y="158" font-size="9" fill="#164e63" font-family="JetBrains Mono,monospace">k=2</text>
</svg>`,
cond: '$\\triangle ABC \\sim \\triangle A\'B\'C\'$ с коэффициентом $k = 2$ (большой к малому). Высота $h_{ABC} = 6$ см, $S_{A\'B\'C\'} = 9$ см². (§9)',
parts: [
{ label: 'Найди $h\'$ (высоту малого треугольника).', ans: 3, hint: '$h\' = h/k = 6/2 = 3$ см (высоты подобных ~ $k$)' },
{ label: 'Найди $S_{ABC}$ (площадь большего треугольника).', ans: 36, hint: '$S/S\' = k^2 = 4 \\Rightarrow S = 4 \\cdot 9 = 36$ см²' },
],
},
{
n: 7,
title: 'Средняя линия и площади',
color: '#16a34a',
svg: `<svg viewBox="0 0 280 185" style="width:100%;max-width:280px;background:var(--card);border:1px solid var(--border);border-radius:10px;display:block;margin:10px auto">
<!-- Triangle ABC: AB=14, BC=21. M midpoint AB. MN||BC, N on AC. k=AM/AB=1/2. MN=BC/2=10.5. A(140,12) B(20,170) C(260,170). M=midpoint(A,B)=(80,91). N=midpoint(A,C)=(200,91). -->
<polygon points="140,12 20,170 260,170" fill="rgba(22,163,74,.08)" stroke="#16a34a" stroke-width="2"/>
<line x1="80" y1="91" x2="200" y2="91" stroke="#15803d" stroke-width="2.5"/>
<!-- vertex dots -->
<circle cx="140" cy="12" r="4" fill="#16a34a" stroke="#fff" stroke-width="1.5"/>
<circle cx="20" cy="170" r="4" fill="#16a34a" stroke="#fff" stroke-width="1.5"/>
<circle cx="260" cy="170" r="4" fill="#16a34a" stroke="#fff" stroke-width="1.5"/>
<circle cx="80" cy="91" r="4" fill="#15803d" stroke="#fff" stroke-width="1.5"/>
<circle cx="200" cy="91" r="4" fill="#15803d" stroke="#fff" stroke-width="1.5"/>
<!-- tick marks M midpoint -->
<line x1="108" y1="48" x2="116" y2="56" stroke="#16a34a" stroke-width="1.8"/>
<line x1="37" y1="126" x2="45" y2="134" stroke="#16a34a" stroke-width="1.8"/>
<!-- labels -->
<text x="128" y="8" font-size="11" font-weight="700" fill="#14532d" font-family="Unbounded,sans-serif">A</text>
<text x="4" y="180" font-size="11" font-weight="700" fill="#14532d" font-family="Unbounded,sans-serif">B</text>
<text x="264" y="180" font-size="11" font-weight="700" fill="#14532d" font-family="Unbounded,sans-serif">C</text>
<text x="60" y="88" font-size="11" font-weight="700" fill="#14532d" font-family="Unbounded,sans-serif">M</text>
<text x="204" y="88" font-size="11" font-weight="700" fill="#14532d" font-family="Unbounded,sans-serif">N</text>
<!-- side labels -->
<text x="48" y="126" font-size="9" fill="#14532d" font-family="JetBrains Mono,monospace">AB=14</text>
<text x="108" y="185" font-size="9" fill="#14532d" font-family="JetBrains Mono,monospace">BC=21</text>
<text x="110" y="82" font-size="9" fill="#14532d" font-family="JetBrains Mono,monospace">MN=?</text>
</svg>`,
cond: 'В $\\triangle ABC$: $AB = 14$, $BC = 21$. $M$ — середина $AB$, $N$ на $AC$ так, что $MN \\parallel BC$. (§4, §9)',
parts: [
{ label: 'Найди $MN$ (используй $k = AM/AB = 1/2$, $MN = k \\cdot BC$).', ans: 10.5, tol: 0.05, hint: '$k = 7/14 = 1/2$, $MN = 1/2 \\cdot 21 = 10{,}5$ см' },
{ label: 'Найди $AN/NC$ (какое оно?).', ans: 1, hint: '$M$ — середина $AB \\Rightarrow k = 1/2 \\Rightarrow AN/NC = AM/MB = 1/1$, т.е. $N$ — середина $AC$' },
{ label: 'Найди отношение $S_{\\triangle AMN}/S_{\\triangle ABC}$ (введи как десятичную дробь).', ans: 0.25, tol: 0.01, hint: '$S_{AMN}/S_{ABC} = k^2 = (1/2)^2 = 0{,}25$' },
],
},
];
window.final3BossSolved = new Set();
const bossBox = document.getElementById('final3-bosses');
bossBox.innerHTML = bosses.map(b => {
const partsHtml = b.parts.map((p,pi) => {
return `<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" step="any" class="tinp final3-boss-inp" data-b="${b.n-1}" data-p="${pi}" placeholder="Ответ" style="width:120px">
<button class="btn primary final3-boss-check" data-b="${b.n-1}" data-p="${pi}" style="background:${b.color};border-color:${b.color}">Проверить</button>
<span class="final3-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 final3-boss-fb" data-b="${b.n-1}" data-p="${pi}" style="display:none;margin-top:6px"></div>
</div>`;
}).join('');
const svgHtml = b.svg ? `<div style="display:flex;justify-content:center;margin:10px 0">${b.svg}</div>` : '';
return `<div style="padding:16px;background:var(--card);border-radius:12px;border:2px solid ${b.color};margin-bottom:14px" id="final3-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="final3-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,#ede9fe,#fff);border-left:4px solid ${b.color};border-radius:9px;font-size:.92rem;line-height:1.6;margin-bottom:4px">${b.cond}</div>
${svgHtml}
${partsHtml}
</div>`;
}).join('');
function checkPart(bi, pi){
const boss = bosses[bi];
const part = boss.parts[pi];
const inp = bossBox.querySelector(`.final3-boss-inp[data-b="${bi}"][data-p="${pi}"]`);
const fb = bossBox.querySelector(`.final3-boss-fb[data-b="${bi}"][data-p="${pi}"]`);
const ok = bossBox.querySelector(`.final3-boss-ok[data-b="${bi}"][data-p="${pi}"]`);
if(!inp) return;
const val = parseFloat(inp.value);
const useExact = part._override && part._ans_exact !== undefined;
const ansCheck = useExact ? part._ans_exact : part.ans;
const tolCheck = useExact ? part._tol_exact : (part.tol !== undefined ? part.tol : 0);
const hintText = useExact ? part._hint_fix : part.hint;
if(Math.abs(val - ansCheck) <= tolCheck){
feedback(fb, true, 'Верно! ' + (hintText ? '<br><span style="font-size:.82rem;opacity:.85">' + hintText + '</span>' : ''));
inp.disabled = true;
const btn = bossBox.querySelector(`.final3-boss-check[data-b="${bi}"][data-p="${pi}"]`);
if(btn) btn.disabled = true;
if(ok) ok.style.display = 'inline';
const allDone = boss.parts.every((_,pj) => {
const el = bossBox.querySelector(`.final3-boss-inp[data-b="${bi}"][data-p="${pj}"]`);
return el && el.disabled;
});
if(allDone && !window.final3BossSolved.has(bi)){
window.final3BossSolved.add(bi);
addXp(10, 'final3-boss-' + (bi+1));
const xpBadge = document.getElementById('final3-boss-xp-' + bi);
if(xpBadge) xpBadge.style.display = 'inline';
bumpProgress('final3', 8);
if(window.final3BossSolved.size === bosses.length){
setTimeout(()=>{
confetti();
if(!STATE.achievements.has('final3-master')){
STATE.achievements.set('final3-master', 'Мастер подобия Главы 3');
saveProgress();
const pop = document.getElementById('ach-popup');
if(pop){ document.getElementById('ach-text').textContent = 'Мастер подобия Главы 3!'; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'), 4000); }
}
addXp(50, 'final3-all-bosses');
bumpProgress('final3', 20);
const fin = document.getElementById('final3-finish');
if(fin) fin.style.display = 'block';
}, 500);
}
}
} else {
feedback(fb, false, 'Неверно. Подсказка: ' + (hintText || part.hint));
}
}
bossBox.querySelectorAll('.final3-boss-check').forEach(btn=>{
btn.addEventListener('click', ()=>{ checkPart(+btn.dataset.b, +btn.dataset.p); });
});
bossBox.querySelectorAll('.final3-boss-inp').forEach(inp=>{
inp.addEventListener('keydown', e=>{ if(e.key==='Enter'){ const btn=bossBox.querySelector(`.final3-boss-check[data-b="${inp.dataset.b}"][data-p="${inp.dataset.p}"]`); if(btn)btn.click(); } });
});
})();
renderMath(box);
}
</script>
</body>
</html>