Files
Learn_System/frontend/textbooks/geometry_8_ch4.html
T
Maxim Dolgolyov 17e42990ad feat(geom8): Wave 1 Главы 4 — §1-§3 (касательная)
§1 Касательная. Признак: слайдер d от 0 до 2R — секущая/касательная/не
пересекает с цветовым индикатором; 5-шаговое доказательство через
прямоугольный △OTM; калькулятор вида прямой; DnD по 3 корзинам;
тренажёр; босс.

§2 Свойство касательной: слайдер угла T — касательная ⊥ радиус OT всегда,
маркер 90° следует за T; 5-шаговое доказательство от противного;
калькулятор AT=√(|OA|²−R²); тренажёр; DnD утверждения; босс.

§3 Касательные из одной точки: слайдер |OA| — две касательные из A,
AT₁=AT₂ с тиками равенства; 5-шаговое доказательство через равенство
прямоугольных △OAT₁ и △OAT₂; калькулятор |AT|; тренажёр; DnD; босс.

GLOSSARY: +точка касания, +радиус.
File: 470 → 1549 LOC.

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

1639 lines
146 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 · Глава 4 · Окружности</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"
onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false})"></script>
<script src="/js/api.js" defer></script>
<script src="/js/xp.js" defer></script>
<link 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:#ecfeff; --text:#0f172a; --ink:#0f172a; --muted:#64748b;
--border:#e2e8f0; --sh:0 1px 3px rgba(0,0,0,.06); --sh2:0 4px 14px rgba(0,0,0,.08);
--pri:#0891b2; --pri2:#0e7490; --pri-soft:#cffafe;
--acc:#06b6d4; --acc2:#0891b2; --acc-soft:#a5f3fc;
--ok:#10b981; --ok-bg:#d1fae5; --warn:#f59e0b; --warn-bg:#fef3c7;
--bad:#ef4444; --fail:#dc2626; --fail-bg:#fee2e2;
}
.dark{--bg:#03080f; --card:#040d17; --card-soft:#06121f; --text:#ecfeff; --ink:#ecfeff; --muted:#5ea4b8; --border:#0d2535}
*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}
html,body{font-family:'Inter',system-ui,sans-serif;background:var(--bg);color:var(--text);line-height:1.55;font-size:15px}
button,input,select,textarea{font-family:inherit;font-size:inherit}
button{cursor:pointer;border:0;background:transparent;color:inherit}
a{color:inherit;text-decoration:none}
.ic{width:16px;height:16px;display:inline-block;flex-shrink:0;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round;vertical-align:middle}
.hdr{position:relative;background:linear-gradient(110deg,#164e63 0%,#0891b2 55%,#22d3ee 100%);color:#fff;padding:46px 22px 30px;overflow:hidden;border-bottom:2px solid rgba(34,211,238,.2);min-height:130px}
.hdr::before{content:'ГЛАВА 4';position:absolute;right:-12px;top:50%;transform:translateY(-50%);font-family:'Unbounded',sans-serif;font-size:clamp(5rem,15vw,11rem);font-weight:900;letter-spacing:-.04em;color:transparent;-webkit-text-stroke:1.5px rgba(165,243,252,.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(8,145,178,.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(8,145,178,.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(8,145,178,.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(8,145,178,.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,#ecfeff,#cffafe)}
.psel-card.final .psel-num{color:var(--warn)}
/* SECTION COLORS — cyan/sky/blue spectrum */
.sec[id="sec-p1"] { --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
.sec[id="sec-p2"] { --sec-acc:#06b6d4; --sec-acc-d:#0891b2; --sec-acc-soft:#a5f3fc; }
.sec[id="sec-p3"] { --sec-acc:#0284c7; --sec-acc-d:#0369a1; --sec-acc-soft:#e0f2fe; }
.sec[id="sec-p4"] { --sec-acc:#2563eb; --sec-acc-d:#1d4ed8; --sec-acc-soft:#dbeafe; }
.sec[id="sec-p5"] { --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
.sec[id="sec-p6"] { --sec-acc:#06b6d4; --sec-acc-d:#0891b2; --sec-acc-soft:#a5f3fc; }
.sec[id="sec-p7"] { --sec-acc:#0d9488; --sec-acc-d:#0f766e; --sec-acc-soft:#ccfbf1; }
.sec[id="sec-p8"] { --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
.sec[id="sec-p9"] { --sec-acc:#06b6d4; --sec-acc-d:#0891b2; --sec-acc-soft:#a5f3fc; }
.sec[id="sec-p10"] { --sec-acc:#0284c7; --sec-acc-d:#0369a1; --sec-acc-soft:#e0f2fe; }
.sec[id="sec-p11"] { --sec-acc:#2563eb; --sec-acc-d:#1d4ed8; --sec-acc-soft:#dbeafe; }
.sec[id="sec-p12"] { --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
.sec[id="sec-p13"] { --sec-acc:#06b6d4; --sec-acc-d:#0891b2; --sec-acc-soft:#a5f3fc; }
.sec[id="sec-p14"] { --sec-acc:#0d9488; --sec-acc-d:#0f766e; --sec-acc-soft:#ccfbf1; }
.sec[id="sec-p15"] { --sec-acc:#0284c7; --sec-acc-d:#0369a1; --sec-acc-soft:#e0f2fe; }
.sec[id="sec-p16"] { --sec-acc:#2563eb; --sec-acc-d:#1d4ed8; --sec-acc-soft:#dbeafe; }
.sec[id="sec-final4"] { --sec-acc:#0891b2; --sec-acc-d:#0e7490; --sec-acc-soft:#cffafe; }
.sec{display:none;position:relative;animation:fadeIn .35s ease}
.sec.active{display:block}
@keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.sec::before{content:attr(data-watermark);position:absolute;right:-20px;top:10%;font-family:'Unbounded',sans-serif;font-size:clamp(6rem,18vw,14rem);font-weight:900;color:transparent;-webkit-text-stroke:1.5px var(--sec-acc-soft,var(--pri-soft));line-height:1;pointer-events:none;user-select:none;z-index:0;opacity:.35}
.sec-header{margin-bottom:22px;padding-bottom:14px;border-bottom:2px solid var(--sec-acc-soft,var(--pri-soft));position:relative;z-index:1}
.sec-num{display:inline-block;padding:4px 10px;background:linear-gradient(135deg,var(--sec-acc,var(--pri)),var(--sec-acc-d,var(--pri2)));color:#fff;border-radius:7px;font-family:'Unbounded',sans-serif;font-size:.78rem;font-weight:800;letter-spacing:.04em;margin-bottom:8px}
.sec-h{font-family:'Unbounded',sans-serif;font-size:1.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(8,145,178,.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(8,145,178,.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(6,182,212,.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,#0891b2,#06b6d4);color:#fff;padding:12px 18px;border-radius:11px;font-weight:700;font-size:.9rem;box-shadow:0 8px 28px rgba(8,145,178,.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(8,145,178,.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 · Глава 4</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> = половина центрального, хорды, секущие, взаимное расположение двух окружностей. 16 параграфов богатой классической геометрии.</p>
<div class="hero-row">
<button class="btn-primary" onclick="goTo('p1')">
<svg class="ic" viewBox="0 0 24 24"><polygon points="6 4 20 12 6 20 6 4" fill="currentColor" stroke="none"/></svg>
Начать § 1
</button>
<div class="hero-progress">
<span class="hp-label">Прогресс по главе</span>
<div class="hp-bar"><div id="hero-hp-fill" class="hp-fill"></div></div>
<span id="hero-hp-text" class="hp-text">0%</span>
</div>
<div id="hero-xp-badge" class="hero-xp-badge" title="Опыт"></div>
</div>
</section>
<section class="psel">
<div class="psel-title">Параграфы главы</div>
<div id="psel-grid" class="psel-grid"></div>
</section>
<section id="sec-p1" class="sec" data-watermark="&#8869;"><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="&#8857;"><div class="sec-header"><span class="sec-num">§ 2</span><h2 class="sec-h">Свойство касательной</h2></div><div id="p2-body"></div></section>
<section id="sec-p3" class="sec" data-watermark="&#8857;&#8857;"><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="&#9998;"><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;"><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="&#8857;&#8857;"><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="d"><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="&#8736;2"><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="&#189;&#8736;"><div class="sec-header"><span class="sec-num">§ 9</span><h2 class="sec-h">Свойство вписанного угла</h2></div><div id="p9-body"></div></section>
<section id="sec-p10" class="sec" data-watermark="="><div class="sec-header"><span class="sec-num">§ 10</span><h2 class="sec-h">Вписанные углы, опирающиеся на одну дугу</h2></div><div id="p10-body"></div></section>
<section id="sec-p11" class="sec" data-watermark="90&#176;"><div class="sec-header"><span class="sec-num">§ 11</span><h2 class="sec-h">Вписанный угол, опирающийся на диаметр</h2></div><div id="p11-body"></div></section>
<section id="sec-p12" class="sec" data-watermark="&#8756;"><div class="sec-header"><span class="sec-num">§ 12</span><h2 class="sec-h">Угол между касательной и хордой</h2></div><div id="p12-body"></div></section>
<section id="sec-p13" class="sec" data-watermark="&#10005;"><div class="sec-header"><span class="sec-num">§ 13</span><h2 class="sec-h">Угол между хордами</h2></div><div id="p13-body"></div></section>
<section id="sec-p14" class="sec" data-watermark="))(("><div class="sec-header"><span class="sec-num">§ 14</span><h2 class="sec-h">Угол между секущими</h2></div><div id="p14-body"></div></section>
<section id="sec-p15" class="sec" data-watermark="&#8901;"><div class="sec-header"><span class="sec-num">§ 15</span><h2 class="sec-h">Свойство пересекающихся хорд</h2></div><div id="p15-body"></div></section>
<section id="sec-p16" class="sec" data-watermark="&#215;"><div class="sec-header"><span class="sec-num">§ 16</span><h2 class="sec-h">Свойство касательной и секущей</h2></div><div id="p16-body"></div></section>
<section id="sec-final4" class="sec" data-watermark="&#9733;"><div class="sec-header"><span class="sec-num" style="background:linear-gradient(135deg,#0891b2,#06b6d4)">Финал главы</span><h2 class="sec-h">Итоги. Боссы главы 4</h2></div><div id="final4-body"></div></section>
</div>
<aside class="col-side" id="col-side"><div id="sidebar-content"></div></aside>
<div class="col-side-backdrop" id="col-side-backdrop"></div>
</main>
<footer class="foot">Интерактивный учебник «Геометрия 8» · Глава 4 · Окружности · LearnSpace</footer>
<div id="ach-popup" class="ach-popup"><svg class="ic" viewBox="0 0 24 24" style="width:22px;height:22px"><circle cx="12" cy="12" r="9"/></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,p10:0,p11:0,p12:0,p13:0,p14:0,p15:0,p16:0,final4: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:'Начало главы 4!', ch4_done:'Окружности изучены!' };
function loadProgress(){ try{ const s=localStorage.getItem('geometry8_ch4_progress');if(s)Object.assign(STATE.progress,JSON.parse(s)); const a=localStorage.getItem('geometry8_ch4_achievements');if(a){const p=JSON.parse(a);if(Array.isArray(p))p.forEach(id=>STATE.achievements.set(id,ACH_LABELS[id]||id));else if(p&&typeof p==='object')for(const[id,t]of Object.entries(p))STATE.achievements.set(id,(t&&t!==id)?t:(ACH_LABELS[id]||id));} STATE.xp=+(localStorage.getItem('geometry8_xp')||0);STATE.level=calcLevel(STATE.xp); }catch(e){} }
function saveProgress(){ try{localStorage.setItem('geometry8_ch4_progress',JSON.stringify(STATE.progress));localStorage.setItem('geometry8_ch4_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-ch4';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-ch4-'+(src||'misc'));if(STATE.level>prev){const pop=document.getElementById('ach-popup');if(pop){document.getElementById('ach-text').textContent='Уровень '+STATE.level+'!';pop.classList.add('show');setTimeout(()=>pop.classList.remove('show'),2600);}if(window.confetti)try{confetti();}catch(e){}}}
const TOTAL_PARAS=17;
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"><circle cx="12" cy="12" r="9"/></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:'Свойство касательной',sub:'Перпендикулярность'},
{id:'p3',num:'§ 3',name:'Касательные из одной точки',sub:'Равные отрезки'},
{id:'p4',num:'§ 4',name:'Построение касательной',sub:'Циркуль и линейка'},
{id:'p5',num:'§ 5',name:'Окружности в угле',sub:'Вписанные в угол'},
{id:'p6',num:'§ 6',name:'Две окружности',sub:'Взаимное расположение'},
{id:'p7',num:'§ 7',name:'Общая внешняя касательная',sub:'Формула длины'},
{id:'p8',num:'§ 8',name:'Центральный и вписанный угол',sub:'Определения'},
{id:'p9',num:'§ 9',name:'Свойство вписанного угла',sub:'Половина центрального'},
{id:'p10',num:'§ 10',name:'Углы на одной дуге',sub:'Равные вписанные углы'},
{id:'p11',num:'§ 11',name:'Угол на диаметре',sub:'Прямой угол'},
{id:'p12',num:'§ 12',name:'Касательная и хорда',sub:'Угол между ними'},
{id:'p13',num:'§ 13',name:'Угол между хордами',sub:'Полусумма дуг'},
{id:'p14',num:'§ 14',name:'Угол между секущими',sub:'Полуразность дуг'},
{id:'p15',num:'§ 15',name:'Пересекающиеся хорды',sub:'Произведение отрезков'},
{id:'p16',num:'§ 16',name:'Касательная и секущая',sub:'Квадрат касательной'},
{id:'final4',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:()=>buildP4stub(),p5:()=>buildP5stub(),p6:()=>buildP6stub(),p7:()=>buildP7stub(),p8:()=>buildP8stub(),p9:()=>buildP9stub(),p10:()=>buildP10stub(),p11:()=>buildP11stub(),p12:()=>buildP12stub(),p13:()=>buildP13stub(),p14:()=>buildP14stub(),p15:()=>buildP15stub(),p16:()=>buildP16stub(),final4:()=>buildFinal4stub()};
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:[['Свойство','касательная ⊥ радиусу в точке касания']]},
p3:{title:'Шпаргалка § 3',rows:[['Из одной точки','две касательные равны'],['|PA| = |PB|','']]},
p4:{title:'Шпаргалка § 4',rows:[['Построение','окружность на диаметре OP находит точки касания']]},
p5:{title:'Шпаргалка § 5',rows:[['Вписанные в угол','касаются обеих сторон угла'],['Центры','лежат на биссектрисе угла']]},
p6:{title:'Шпаргалка § 6',rows:[['Внешнее касание','$d = r_1 + r_2$'],['Внутреннее касание','$d = |r_1 - r_2|$'],['Пересечение','$|r_1-r_2| < d < r_1+r_2$']]},
p7:{title:'Шпаргалка § 7',rows:[['Внешняя касательная','$l = \\sqrt{d^2 - (r_1 - r_2)^2}$']]},
p8:{title:'Шпаргалка § 8',rows:[['Центральный угол','вершина в центре'],['Вписанный угол','вершина на окружности'],['Градусная мера дуги','= центральный угол']]},
p9:{title:'Шпаргалка § 9',rows:[['Вписанный = ½ центрального','$\\angle = \\dfrac{1}{2}\\cdot$дуга']]},
p10:{title:'Шпаргалка § 10',rows:[['Одна дуга','все вписанные углы равны']]},
p11:{title:'Шпаргалка § 11',rows:[['На диаметре','вписанный угол = 90°'],['Следствие','диаметр — хорда, стягивающая прямой угол']]},
p12:{title:'Шпаргалка § 12',rows:[['Касательная и хорда','угол = половине дуги, отсекаемой хордой']]},
p13:{title:'Шпаргалка § 13',rows:[['Хорды пересекаются','$\\angle = \\dfrac{1}{2}(\\text{дуга}_1 + \\text{дуга}_2)$']]},
p14:{title:'Шпаргалка § 14',rows:[['Секущие из точки вне','$\\angle = \\dfrac{1}{2}|\\text{дуга}_1 - \\text{дуга}_2|$']]},
p15:{title:'Шпаргалка § 15',rows:[['Пересекающиеся хорды','$PA\\cdot PB = PC\\cdot PD$']]},
p16:{title:'Шпаргалка § 16',rows:[['Касательная и секущая','$t^2 = PA\\cdot PB$'],['$t$','длина касательной']]},
final4:{title:'Финал главы',rows:[['16 параграфов','окружности изучены'],['Главное','вписанный угол = ½ дуги']]},
};
const TIPS=[
{sec:'p1',html:'Касательная ⊥ радиусу в точке касания — это и признак, и свойство.'},
{sec:'p2',html:'Из любой внешней точки можно провести <b>две</b> касательных к окружности.'},
{sec:'p3',html:'Отрезки двух касательных из одной внешней точки равны.'},
{sec:'p4',html:'Точки касания находятся на пересечении вспомогательной окружности и данной.'},
{sec:'p5',html:'Окружности, вписанные в угол, имеют центры на биссектрисе этого угла.'},
{sec:'p6',html:'$d > r_1 + r_2$ — нет общих точек. $d = r_1 + r_2$ — внешнее касание.'},
{sec:'p7',html:'Длина общей внешней касательной: $l=\\sqrt{d^2-(r_1-r_2)^2}$.'},
{sec:'p8',html:'Градусная мера дуги = величина центрального угла, опирающегося на эту дугу.'},
{sec:'p9',html:'Вписанный угол <b>вдвое меньше</b> центрального, опирающегося на ту же дугу.'},
{sec:'p10',html:'Все вписанные углы, опирающиеся на одну дугу, равны между собой.'},
{sec:'p11',html:'Вписанный угол, опирающийся на диаметр, всегда равен $90°$.'},
{sec:'p12',html:'Угол между касательной и хордой = половине дуги, отсекаемой этой хордой.'},
{sec:'p13',html:'Угол между хордами = полусумма двух дуг, образованных хордами.'},
{sec:'p14',html:'Угол между двумя секущими из внешней точки = полуразность дуг.'},
{sec:'p15',html:'Если хорды $AB$ и $CD$ пересекаются в точке $P$: $PA\\cdot PB = PC\\cdot PD$.'},
{sec:'p16',html:'Если из точки $P$ проведены касательная $t$ и секущая $PA\\cdot PB$: $t^2 = PA\\cdot PB$.'},
{sec:'final4',html:'Ключевая теорема: вписанный угол = ½ дуги. Из неё следуют большинство остальных.'},
];
function buildSidebar(id){const box=document.getElementById('sidebar-content');const sb=SIDEBARS[id]||SIDEBARS.p1;let html='';const xpForLv=_xpForLevel(STATE.level),xpNext=_xpForLevel(STATE.level+1);const xpInLv=STATE.xp-xpForLv,xpRange=xpNext-xpForLv,xpPct=xpRange>0?Math.round(xpInLv/xpRange*100):100;html+=`<div class="xp-card"><div class="xp-card-title"><span>XP-прогресс</span><span class="xp-level">Ур. ${STATE.level}</span></div><div class="xp-bar"><div class="xp-fill" style="width:${xpPct}%"></div></div><div class="xp-nums"><span>${STATE.xp} XP</span><span>${xpNext} XP</span></div></div>`;html+=`<div class="sidecard"><h4>${sb.title}</h4>`;sb.rows.forEach(([k,v])=>{html+=`<div class="sidecard-row"><b>${k}</b>${v?' — '+v:''}</div>`;});html+='</div>';const tip=TIPS.find(t=>t.sec===id)||TIPS[0];html+=`<div class="sidecard" style="background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--pri-soft));border-color:var(--warn,#f59e0b)"><h4 style="color:#0e4f6b;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"><circle cx="12" cy="12" r="9"/></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_ch4_theme')||'light';if(t==='dark')document.documentElement.classList.add('dark');document.getElementById('theme-lab').textContent=t==='dark'?'Светлая':'Тёмная';document.getElementById('theme-btn').addEventListener('click',()=>{document.documentElement.classList.toggle('dark');const dark=document.documentElement.classList.contains('dark');localStorage.setItem('geometry8_ch4_theme',dark?'dark':'light');document.getElementById('theme-lab').textContent=dark?'Светлая':'Тёмная';});}
function renderMath(root){if(window.renderMathInElement){try{renderMathInElement(root,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false});}catch(e){}}}
function feedback(elm,ok,text){elm.className='feedback '+(ok?'ok':'fail');elm.innerHTML=text||(ok?'&#10003; Верно!':'&#10007; Неверно');}
function fmt(n){if(!isFinite(n))return '?';if(Number.isInteger(n))return String(n);return Math.abs(n-Math.round(n))<1e-9?String(Math.round(n)):(+n.toFixed(4)).toString();}
const ICONS={repeat:'<svg class="ic" viewBox="0 0 24 24"><polyline points="9 11 12 14 22 4"/><path d="M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11"/></svg>',theory:'<svg class="ic" viewBox="0 0 24 24"><path d="M4 19.5A2.5 2.5 0 0 1 6.5 17H20"/><path d="M6.5 2H20v20H6.5A2.5 2.5 0 0 1 4 19.5v-15A2.5 2.5 0 0 1 6.5 2z"/></svg>',algo:'<svg class="ic" viewBox="0 0 24 24"><polyline points="17 11 21 7 17 3"/><line x1="21" y1="7" x2="9" y2="7"/><polyline points="7 13 3 17 7 21"/><line x1="3" y1="17" x2="15" y2="17"/></svg>',rule:'<svg class="ic" viewBox="0 0 24 24"><path d="M6 8a6 6 0 0 1 12 0c0 7 3 9 3 9H3s3-2 3-9"/><path d="M10.3 21a1.94 1.94 0 0 0 3.4 0"/></svg>',example:'<svg class="ic" viewBox="0 0 24 24"><path d="M9 18h6"/><path d="M10 22h4"/><path d="M12 2a7 7 0 0 0-4 13c1 1 2 2 2 4h4c0-2 1-3 2-4a7 7 0 0 0-4-13z"/></svg>',oral:'<svg class="ic" viewBox="0 0 24 24"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>',class:'<svg class="ic" viewBox="0 0 24 24"><rect x="3" y="3" width="18" height="14" rx="1"/><line x1="3" y1="21" x2="21" y2="21"/><polyline points="7 14 10 11 13 14 17 10"/></svg>',home:'<svg class="ic" viewBox="0 0 24 24"><path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/><polyline points="9 22 9 12 15 12 15 22"/></svg>'};
function makeCard(kind,title,num,body){const labels={repeat:'Повторение',theory:'Теория',algo:'Алгоритм',rule:'Правило',example:'Пример',oral:'Устно',class:'Класс',home:'Домашка'};return `<div class="card"><div class="card-header"><div class="card-icon ${kind}">${ICONS[kind]}</div><div class="card-title">${labels[kind]||''}${title&&title!==labels[kind]?' \xb7 '+title:''}</div>${num?`<div class="card-num">${num}</div>`:''}</div><div class="card-body">${body}</div></div>`;}
function secNav(prev,next){const NAMES={p1:'§1',p2:'§2',p3:'§3',p4:'§4',p5:'§5',p6:'§6',p7:'§7',p8:'§8',p9:'§9',p10:'§10',p11:'§11',p12:'§12',p13:'§13',p14:'§14',p15:'§15',p16:'§16',final4:'Финал'};let h='<div class="sec-nav">';h+=prev?`<button class="btn" onclick="goTo('${prev}')"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> ${NAMES[prev]}</button>`:'<span></span>';h+=next?`<button class="btn primary" onclick="goTo('${next}')">${NAMES[next]} <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></button>`:'<span></span>';h+='</div>';return h;}
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=['#0891b2','#06b6d4','#22d3ee','#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:'Прямая, имеющая с окружностью ровно одну общую точку (точку касания). Признак: прямая перпендикулярна радиусу в точке касания.',sec:'p1',aliases:['касательная','касательной','касательную','касательных','касательными']},
{term:'точка касания',def:'Единственная общая точка касательной и окружности.',sec:'p1',aliases:['точка касания','точки касания','точку касания','точек касания']},
{term:'секущая',def:'Прямая, пересекающая окружность в двух точках.',sec:'p1',aliases:['секущая','секущей','секущую','секущих','секущими']},
{term:'радиус',def:'Отрезок от центра окружности до любой её точки; также длина этого отрезка.',sec:'p1',aliases:['радиус','радиуса','радиусу','радиусом','радиусы','радиусов']},
{term:'хорда',def:'Отрезок, соединяющий две точки окружности.',sec:'p13',aliases:['хорда','хорды','хорде','хорду','хорд','хордами']},
{term:'центральный угол',def:'Угол с вершиной в центре окружности, стороны которого — радиусы.',sec:'p8',aliases:['центральный угол','центрального угла','центральному углу','центральные углы']},
{term:'вписанный угол',def:'Угол с вершиной на окружности, стороны которого — хорды.',sec:'p8',aliases:['вписанный угол','вписанного угла','вписанному углу','вписанные углы','вписанных углов']},
{term:'дуга',def:'Часть окружности, ограниченная двумя точками.',sec:'p8',aliases:['дуга','дуги','дуге','дугу','дуг','дугами']},
{term:'диаметр',def:'Хорда, проходящая через центр окружности; наибольшая хорда.',sec:'p11',aliases:['диаметр','диаметра','диаметре','диаметром']},
{term:'многоугольник',def:'Замкнутая ломаная фигура.',sec:'p1',aliases:['многоугольник','многоугольника']},
{term:'подобные треугольники',def:'Треугольники с равными углами и пропорциональными сторонами.',sec:'p9',aliases:['подобные треугольники','подобных треугольников']},
{term:'коэффициент подобия',def:'Отношение соответственных сторон подобных фигур.',sec:'p9',aliases:['коэффициент подобия','коэффициента подобия']},
{term:'биссектриса',def:'Луч, делящий угол пополам.',sec:'p5',aliases:['биссектриса','биссектрисы','биссектрису']},
{term:'теорема Пифагора',def:'$a^2+b^2=c^2$ в прямоугольном треугольнике.',sec:'p7',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:['теорема Фалеса','теореме Фалеса','теоремы Фалеса']},
];
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}));[['Формула','Вписанный угол = ½ центрального','§9','p9'],['Формула','На диаметре: вписанный угол = 90°','§11','p11'],['Формула','Хорды: PA·PB = PC·PD','§15','p15'],['Формула','Касательная: t² = PA·PB','§16','p16']].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','Начало главы 4!'),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> Прямая называется <b>касательной</b> к окружности, если она имеет с окружностью ровно одну общую точку. Эта общая точка называется <b>точкой касания</b>.</p>
<p style="margin-top:8px">Прямая может быть расположена относительно окружности тремя способами:</p>
<ul style="margin-left:18px;margin-top:6px;line-height:1.9">
<li>Расстояние от центра до прямой <b>$d &lt; R$</b> — прямая <b>секущая</b> (2 общие точки).</li>
<li>Расстояние от центра до прямой <b>$d = R$</b> — прямая <b>касательная</b> (1 общая точка).</li>
<li>Расстояние от центра до прямой <b>$d &gt; R$</b> — прямая <b>не пересекает</b> окружность (0 общих точек).</li>
</ul>
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 280 130" style="max-width:300px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<defs><marker id="arr-p1" markerWidth="6" markerHeight="6" refX="3" refY="3" orient="auto"><path d="M0,0 L6,3 L0,6 Z" fill="#0891b2"/></marker></defs>
<!-- секущая -->
<circle cx="52" cy="65" r="30" fill="rgba(8,145,178,.08)" stroke="#0891b2" stroke-width="1.8"/>
<line x1="14" y1="50" x2="90" y2="50" stroke="#0891b2" stroke-width="2"/>
<circle cx="27" cy="50" r="3" fill="#0891b2"/>
<circle cx="77" cy="50" r="3" fill="#0891b2"/>
<text x="52" y="112" text-anchor="middle" font-size="10" fill="#0e7490" font-weight="700">секущая</text>
<text x="52" y="124" text-anchor="middle" font-size="9" fill="#64748b" font-style="italic">d &lt; R</text>
<!-- касательная -->
<circle cx="140" cy="65" r="30" fill="rgba(8,145,178,.08)" stroke="#0891b2" stroke-width="1.8"/>
<line x1="102" y1="35" x2="178" y2="35" stroke="#10b981" stroke-width="2.2"/>
<circle cx="140" cy="35" r="3.5" fill="#10b981"/>
<text x="140" y="112" text-anchor="middle" font-size="10" fill="#065f46" font-weight="700">касательная</text>
<text x="140" y="124" text-anchor="middle" font-size="9" fill="#64748b" font-style="italic">d = R</text>
<!-- не пересекает -->
<circle cx="228" cy="65" r="30" fill="rgba(8,145,178,.08)" stroke="#0891b2" stroke-width="1.8"/>
<line x1="190" y1="20" x2="266" y2="20" stroke="#ef4444" stroke-width="2"/>
<text x="228" y="112" text-anchor="middle" font-size="10" fill="#7f1d1d" font-weight="700">не пересекает</text>
<text x="228" y="124" text-anchor="middle" font-size="9" fill="#64748b" font-style="italic">d &gt; R</text>
</svg>
</div>`);
html+=makeCard('rule','Признак касательной','1.2',`
<p><b>Теорема (признак касательной).</b> Если прямая проходит через точку окружности и перпендикулярна радиусу, проведённому в эту точку, то эта прямая является касательной к окружности.</p>
$$OT \\perp \\ell \\implies \\ell \\text{ — касательная в точке } T$$
<p style="margin-top:8px"><b>Расстояние от центра</b> до касательной равно радиусу: $d(O,\\ell) = R$.</p>
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 200 160" style="max-width:220px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<circle cx="100" cy="90" r="55" fill="rgba(8,145,178,.08)" stroke="#0891b2" stroke-width="2"/>
<line x1="100" y1="90" x2="100" y2="35" stroke="#0891b2" stroke-width="2"/>
<circle cx="100" cy="90" r="3" fill="#0e7490"/>
<circle cx="100" cy="35" r="3.5" fill="#10b981"/>
<line x1="40" y1="35" x2="160" y2="35" stroke="#10b981" stroke-width="2.5"/>
<polyline points="100,35 109,35 109,44 100,44" fill="none" stroke="#0e7490" stroke-width="1.8"/>
<text x="104" y="70" font-size="11" font-weight="700" fill="#0e7490">R</text>
<text x="108" y="31" font-size="11" font-weight="700" fill="#065f46">T</text>
<text x="86" y="106" font-size="11" font-weight="700" fill="#0e7490">O</text>
<text x="38" y="28" font-size="10" fill="#065f46" font-style="italic">&#8467;</text>
</svg>
</div>`);
html+=makeCard('example','Пример','1.3',`
<p><b>Задача.</b> Радиус окружности $R = 5$. Расстояние от центра $O$ до прямой $\ell$ равно $d$. Является ли прямая касательной, секущей или не пересекает окружность при:</p>
<table class="tbl" style="margin-top:8px">
<thead><tr><th>$d$</th><th>Вид прямой</th></tr></thead>
<tbody>
<tr><td>$3$</td><td style="color:#0e7490;font-weight:700">секущая ($3 &lt; 5$)</td></tr>
<tr><td>$5$</td><td style="color:#065f46;font-weight:700">касательная ($5 = 5$)</td></tr>
<tr><td>$7$</td><td style="color:#7f1d1d;font-weight:700">не пересекает ($7 &gt; 5$)</td></tr>
</tbody>
</table>`);
/* ИНТЕРАКТИВ 1 — слайдер d */
html+=`<div class="wg" id="p1-dist-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Расположение прямой относительно окружности</div></div>
<div class="wg-help">Двигай слайдер — наблюдай, как меняется расположение прямой. При $d = R$ прямая становится касательной!</div>
<div class="sliders">
<label>Расстояние $d$ = <b id="p1-d-val">60</b> (R = 60)
<input type="range" min="0" max="130" value="60" id="p1-d-sl">
</label>
</div>
<div id="p1-dist-svg" style="display:flex;justify-content:center"></div>
<div id="p1-dist-info" style="margin-top:10px;padding:10px 14px;border-radius:10px;font-size:.94rem;font-weight:700;text-align:center"></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-text" style="padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.94rem;line-height:1.65;margin-bottom:10px;min-height:64px"></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">Введи радиус $R$ и расстояние $d$ от центра до прямой — узнай её вид.</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
<label style="font-size:.9rem">R = <input type="number" id="p1-cr" class="tinp" value="5" min="0.1" style="width:80px"></label>
<label style="font-size:.9rem">d = <input type="number" id="p1-cd" class="tinp" value="3" min="0" style="width:80px"></label>
<button class="btn primary" id="p1-calc-btn">Определить</button>
</div>
<div id="p1-calc-out" style="display:none;padding:10px 14px;border-radius:10px;font-size:.95rem;font-weight:700"></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">Перетащи каждую карточку в нужную корзину. Дано R и d.</div>
<div id="p1-dnd-pool" class="dnd-pool"></div>
<div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:10px;margin-top:10px">
<div class="drop-box" id="p1-drop-sec"><h5>Секущая</h5><div class="drop-items" id="p1-drop-sec-items"></div></div>
<div class="drop-box" id="p1-drop-tan"><h5>Касательная</h5><div class="drop-items" id="p1-drop-tan-items"></div></div>
<div class="drop-box" id="p1-drop-none"><h5>Не пересекает</h5><div class="drop-items" id="p1-drop-none-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>`;
/* ИНТЕРАКТИВ 5 — Тренажёр */
html+=`<div class="wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 5</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:1rem;margin-bottom:10px;line-height:1.6"></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:130px">
<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>`;
/* ИНТЕРАКТИВ 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: слайдер d === */
(function(){
const sl=document.getElementById('p1-d-sl');
const dVal=document.getElementById('p1-d-val');
const svgWrap=document.getElementById('p1-dist-svg');
const info=document.getElementById('p1-dist-info');
const R=60, cx=140, cy=100, W=280, H=200;
function draw(){
const d=+sl.value;
dVal.textContent=d;
const lineY=cy-d;
const hasIntersect=d<R;
const isTan=Math.abs(d-R)<1;
let ix1=0,ix2=0,iy1=0;
if(hasIntersect){
const dx=Math.sqrt(R*R-d*d);
ix1=cx-dx; ix2=cx+dx; iy1=lineY;
}
let pts='', tanMark='';
if(isTan){
pts=`<circle cx="${cx}" cy="${lineY}" r="5" fill="#10b981"/>`;
} else if(hasIntersect){
pts=`<circle cx="${ix1}" cy="${iy1}" r="4" fill="#0891b2"/><circle cx="${ix2}" cy="${iy1}" r="4" fill="#0891b2"/>`;
}
const lineColor=isTan?'#10b981':hasIntersect?'#0891b2':'#ef4444';
const dLineX1=cx, dLineY1=cy, dLineX2=cx, dLineY2=lineY;
const svgStr=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<circle cx="${cx}" cy="${cy}" r="${R}" fill="rgba(8,145,178,.09)" stroke="#0891b2" stroke-width="2"/>
<circle cx="${cx}" cy="${cy}" r="3.5" fill="#0e7490"/>
<text x="${cx+5}" y="${cy+5}" font-size="11" font-weight="700" fill="#0e7490">O</text>
<line x1="20" y1="${lineY}" x2="${W-20}" y2="${lineY}" stroke="${lineColor}" stroke-width="2.5"/>
<line x1="${dLineX1}" y1="${dLineY1}" x2="${dLineX2}" y2="${dLineY2}" stroke="#64748b" stroke-width="1.5" stroke-dasharray="4,3"/>
<text x="${cx+4}" y="${(cy+lineY)/2+4}" font-size="10" fill="#64748b" font-style="italic">d=${d}</text>
${pts}
${tanMark}
</svg>`;
svgWrap.innerHTML=svgStr;
if(isTan){
info.style.background='#d1fae5';info.style.color='#065f46';
info.textContent='d = R = '+R+' — прямая КАСАТЕЛЬНАЯ (1 общая точка)';
} else if(hasIntersect){
info.style.background='var(--sec-acc-soft,#cffafe)';info.style.color='#0e7490';
info.textContent='d = '+d+' < R = '+R+' — прямая СЕКУЩАЯ (2 общие точки)';
} else {
info.style.background='#fee2e2';info.style.color='#7f1d1d';
info.textContent='d = '+d+' > R = '+R+' — прямая НЕ ПЕРЕСЕКАЕТ окружность';
}
}
sl.addEventListener('input',draw);
draw();
})();
/* === INIT ИНТЕРАКТИВ 2: пошаговое доказательство === */
(function(){
const steps=[
{text:'<b>Дано:</b> окружность с центром $O$, точка $T$ на окружности, прямая $\\ell$ проходит через $T$ и $OT \\perp \\ell$. Нужно доказать, что $\\ell$ — касательная.',
highlight:'none'},
{text:'<b>Шаг 1.</b> Возьмём произвольную точку $M \\neq T$ на прямой $\\ell$. Нам нужно показать, что $M$ лежит вне окружности, то есть $OM > R$.',
highlight:'M'},
{text:'<b>Шаг 2.</b> В прямоугольном треугольнике $OTM$ гипотенуза $OM$ больше любого катета. Поскольку $OT = R$ является катетом, то $OM > OT = R$.',
highlight:'triangle'},
{text:'<b>Шаг 3.</b> Значит, любая точка $M \\neq T$ на прямой $\\ell$ лежит вне окружности. Следовательно, прямая $\\ell$ имеет с окружностью только одну общую точку $T$.',
highlight:'conclude'},
{text:'По определению, прямая с единственной общей точкой с окружностью является касательной. <b>ч.т.д.</b>',
highlight:'done'},
];
let step=0;
const svgEl=document.getElementById('p1-proof-svg');
const txtEl=document.getElementById('p1-proof-text');
const nextBtn=document.getElementById('p1-proof-next');
const resetBtn=document.getElementById('p1-proof-reset');
const cx=130, cy=110, R=70;
function drawProof(s){
const h=s.highlight;
const Tx=cx, Ty=cy-R;
const Mx=cx+80, My=Ty;
let extras='';
if(h==='M'||h==='triangle'||h==='conclude'||h==='done'){
extras+=`<circle cx="${Mx}" cy="${My}" r="4" fill="#f59e0b"/>`;
extras+=`<text x="${Mx+5}" y="${My-4}" font-size="11" font-weight="700" fill="#b45309">M</text>`;
extras+=`<line x1="${cx}" y1="${cy}" x2="${Mx}" y2="${My}" stroke="#f59e0b" stroke-width="1.8" stroke-dasharray="4,3"/>`;
}
if(h==='triangle'||h==='conclude'||h==='done'){
extras+=`<polygon points="${cx},${cy} ${Tx},${Ty} ${Mx},${My}" fill="rgba(245,158,11,.12)" stroke="#f59e0b" stroke-width="1.2"/>`;
extras+=`<polyline points="${Tx},${Ty+9} ${Tx+9},${Ty+9} ${Tx+9},${Ty}" fill="none" stroke="#0e7490" stroke-width="1.8"/>`;
}
const lineColor=h==='done'?'#10b981':'#0891b2';
const svg=`<svg viewBox="0 0 260 200" style="max-width:280px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<circle cx="${cx}" cy="${cy}" r="${R}" fill="rgba(8,145,178,.09)" stroke="${lineColor}" stroke-width="2"/>
<line x1="${cx}" y1="${cy}" x2="${Tx}" y2="${Ty}" stroke="#0891b2" stroke-width="2"/>
<circle cx="${cx}" cy="${cy}" r="3.5" fill="#0e7490"/>
<circle cx="${Tx}" cy="${Ty}" r="4" fill="#10b981"/>
<line x1="20" y1="${Ty}" x2="240" y2="${Ty}" stroke="${lineColor}" stroke-width="2.2"/>
<text x="${cx+4}" y="${cy+5}" font-size="11" font-weight="700" fill="#0e7490">O</text>
<text x="${Tx+4}" y="${Ty-6}" font-size="11" font-weight="700" fill="#065f46">T</text>
<text x="22" y="${Ty-5}" font-size="10" fill="#065f46" font-style="italic">&#8467;</text>
<text x="${(cx+Tx)/2+4}" y="${(cy+Ty)/2+4}" font-size="10" fill="#0e7490" font-weight="700">R</text>
${extras}
</svg>`;
svgEl.innerHTML=svg;
txtEl.innerHTML=steps[s.idx].text;
if(window.renderMathInElement) try{renderMath(txtEl);}catch(e){}
nextBtn.textContent=step>=steps.length-1?'Готово':'Далее';
}
steps.forEach((s,i)=>s.idx=i);
function go(){
drawProof(steps[step]);
if(step<steps.length-1) step++;
}
nextBtn.addEventListener('click',go);
resetBtn.addEventListener('click',()=>{step=0;go();});
go();
})();
/* === INIT ИНТЕРАКТИВ 3: калькулятор === */
(function(){
const btn=document.getElementById('p1-calc-btn');
const out=document.getElementById('p1-calc-out');
btn.addEventListener('click',()=>{
const R=parseFloat(document.getElementById('p1-cr').value);
const d=parseFloat(document.getElementById('p1-cd').value);
if(isNaN(R)||isNaN(d)||R<=0||d<0){out.style.display='block';out.style.background='#fee2e2';out.style.color='#7f1d1d';out.textContent='Введите корректные значения (R > 0, d ≥ 0)';return;}
out.style.display='block';
if(Math.abs(d-R)<1e-9){out.style.background='#d1fae5';out.style.color='#065f46';out.textContent='d = R = '+fmt(R)+' → прямая КАСАТЕЛЬНАЯ';}
else if(d<R){out.style.background='var(--sec-acc-soft,#cffafe)';out.style.color='#0e7490';out.textContent='d = '+fmt(d)+' < R = '+fmt(R)+' → прямая СЕКУЩАЯ';}
else{out.style.background='#fee2e2';out.style.color='#7f1d1d';out.textContent='d = '+fmt(d)+' > R = '+fmt(R)+' → прямая НЕ ПЕРЕСЕКАЕТ окружность';}
});
})();
/* === INIT ИНТЕРАКТИВ 4: DnD === */
(function(){
const items=[
{label:'R=5, d=3',cat:'sec'},{label:'R=8, d=8',cat:'tan'},{label:'R=6, d=9',cat:'none'},
{label:'R=10, d=7',cat:'sec'},{label:'R=4, d=4',cat:'tan'},{label:'R=3, d=5',cat:'none'},
];
const pool=document.getElementById('p1-dnd-pool');
const boxes={sec:document.getElementById('p1-drop-sec-items'),tan:document.getElementById('p1-drop-tan-items'),none:document.getElementById('p1-drop-none-items')};
const fb=document.getElementById('p1-dnd-fb');
let placed={};
function reset(){
pool.innerHTML='';placed={};
Object.values(boxes).forEach(b=>b.innerHTML='');
fb.style.display='none';
items.forEach((it,i)=>{
const chip=document.createElement('div');
chip.className='dnd-chip';chip.dataset.i=i;chip.textContent=it.label;
setupChip(chip,i);
pool.appendChild(chip);
});
}
function setupChip(chip,i){
chip.addEventListener('click',()=>{
if(placed[i]!==undefined){
boxes[placed[i]].removeChild(chip);
delete placed[i];
chip.querySelector('.dnd-x')&&chip.querySelector('.dnd-x').remove();
pool.appendChild(chip);
return;
}
chip.classList.add('armed');
});
Object.entries(boxes).forEach(([cat,box])=>{
box.parentElement.addEventListener('click',()=>{
if(!chip.classList.contains('armed'))return;
chip.classList.remove('armed');
if(placed[i]!==undefined) boxes[placed[i]].removeChild(chip);
placed[i]=cat;
const x=document.createElement('span');x.className='dnd-x';x.textContent='×';
x.addEventListener('click',e=>{e.stopPropagation();boxes[cat].removeChild(chip);delete placed[i];x.remove();pool.appendChild(chip);});
chip.appendChild(x);
box.appendChild(chip);
});
});
}
document.getElementById('p1-dnd-check').addEventListener('click',()=>{
let ok=0;
items.forEach((it,i)=>{if(placed[i]===it.cat)ok++;});
feedback(fb,ok===items.length,'Верно: '+ok+'/'+items.length+'. '+(ok===items.length?'Все правильно!':'Попробуй ещё.'));
if(ok===items.length){addXp(8,'p1-dnd');bumpProgress('p1',15);}
});
document.getElementById('p1-dnd-reset').addEventListener('click',reset);
reset();
})();
/* === INIT ИНТЕРАКТИВ 5: Тренажёр === */
(function(){
const tasks=[
{q:'Радиус окружности $R = 13$. Расстояние от центра до прямой $d = 5$. Сколько общих точек у прямой с окружностью?',a:'2',hint:'d < R → секущая → 2 точки'},
{q:'Радиус $R = 7$. Прямая касается окружности. Расстояние от центра до прямой равно?',a:'7',hint:'При касании d = R = 7'},
{q:'От центра до прямой $d = 10$, радиус $R = 6$. Сколько общих точек?',a:'0',hint:'d > R → 0 точек'},
{q:'Прямая перпендикулярна радиусу $OT = 4$ в точке $T$ на окружности. Расстояние от центра до прямой равно?',a:'4',hint:'Признак касательной: расстояние = R = 4'},
{q:'Прямая касается окружности радиуса $R = 9$. Каково расстояние от центра до точки касания?',a:'9',hint:'Расстояние от центра до точки касания = R'},
];
let cur=0,score=0;
const iEl=document.getElementById('p1-tr-i');
const scEl=document.getElementById('p1-tr-score');
const taskEl=document.getElementById('p1-tr-task');
const ansEl=document.getElementById('p1-tr-ans');
const goBtn=document.getElementById('p1-tr-go');
const startBtn=document.getElementById('p1-tr-start');
const fb=document.getElementById('p1-tr-fb');
function showTask(){
if(cur>=tasks.length){taskEl.innerHTML='<b>Тренажёр завершён! Очки: '+score+'/'+tasks.length+'</b>';ansEl.style.display='none';goBtn.style.display='none';addXp(score*4,'p1-trainer');bumpProgress('p1',20);return;}
taskEl.innerHTML=tasks[cur].q;iEl.textContent=cur+1;ansEl.value='';fb.style.display='none';
if(window.renderMathInElement) try{renderMath(taskEl);}catch(e){}
}
goBtn.addEventListener('click',()=>{
const ans=ansEl.value.trim().replace(',','.');
const ok=Math.abs(parseFloat(ans)-parseFloat(tasks[cur].a))<0.01;
feedback(fb,ok,ok?'Верно!':'Неверно. Подсказка: '+tasks[cur].hint);
if(ok){score++;scEl.textContent=score;cur++;setTimeout(showTask,900);}
});
startBtn.addEventListener('click',()=>{cur=0;score=0;scEl.textContent=0;ansEl.style.display='';goBtn.style.display='';showTask();});
showTask();
})();
/* === INIT ИНТЕРАКТИВ 6: Босс §1 === */
(function(){
const tasks=[
{q:'Прямая проходит через точку $A(6, 0)$ и параллельна оси $Oy$. Центр окружности $O(0,0)$, радиус $R = 6$. Является ли прямая касательной?',
opts:['Да, касательная','Нет, секущая','Нет, не пересекает'],cor:0,
exp:'Расстояние от центра $(0,0)$ до прямой $x=6$ равно $6 = R$. Прямая является касательной.'},
{q:'Из точки $M$ вне окружности расстояние до центра $|OM| = 10$, радиус $R = 6$. Прямая $MN$ перпендикулярна $OM$. Является ли $MN$ касательной?',
opts:['Да','Нет, секущая','Нет, не пересекает'],cor:2,
exp:'$MN \\perp OM$, значит расстояние от $O$ до $MN$ равно $|OM| = 10 > R = 6$. Прямая не пересекает окружность.'},
{q:'Прямая $\\ell$ перпендикулярна радиусу $OR$ в точке $R$ на окружности. Выбери верное утверждение:',
opts:['$\\ell$ — касательная в точке $R$','$\\ell$ — секущая','$\\ell$ не пересекает окружность'],cor:0,
exp:'По признаку касательной: прямая, проходящая через точку окружности и перпендикулярная радиусу в этой точке, является касательной.'},
{q:'Расстояние от центра окружности до прямой равно диаметру. Прямая является:',
opts:['касательной','секущей','не пересекает окружность'],cor:2,
exp:'Диаметр $= 2R > R$, значит $d > R$ — прямая не пересекает окружность.'},
];
const cont=document.getElementById('p1-boss-tasks');
let html='';
tasks.forEach((t,i)=>{
html+=`<div style="margin-bottom:14px;padding:14px;background:var(--card);border:1px solid var(--border);border-radius:10px">
<div style="font-weight:600;margin-bottom:8px;font-size:.94rem">${i+1}. ${t.q}</div>
<div style="display:flex;flex-direction:column;gap:6px">
${t.opts.map((o,j)=>`<button class="btn" data-boss="p1-${i}" data-j="${j}" onclick="(function(btn){
const btns=document.querySelectorAll('[data-boss=\\'p1-${i}\\']');
btns.forEach(b=>b.disabled=true);
const ok=${j}===${t.cor};
btn.style.background=ok?'#d1fae5':'#fee2e2';
btn.style.borderColor=ok?'#10b981':'#ef4444';
btn.style.color=ok?'#065f46':'#7f1d1d';
const exp=document.getElementById('p1-boss-exp-${i}');
exp.style.display='block';exp.innerHTML='<b>'+(ok?'Верно!':'Неверно.')+'</b> ${t.exp.replace(/'/g,"\\'")}';
if(window.renderMathInElement) try{renderMath(exp);}catch(e){}
if(ok){addXp(5,'p1-boss-${i}');bumpProgress('p1',5);}
})(this)" style="text-align:left">${o}</button>`).join('')}
</div>
<div id="p1-boss-exp-${i}" style="display:none;margin-top:8px;padding:8px 12px;border-radius:8px;font-size:.88rem;background:var(--sec-acc-soft,var(--pri-soft));color:var(--sec-acc-d,var(--pri2));line-height:1.55"></div>
</div>`;
});
cont.innerHTML=html;
if(window.renderMathInElement) try{renderMath(cont);}catch(e){}
})();
}
function buildP2(){
const box=document.getElementById('p2-body');
let html='';
html+=makeCard('theory','Свойство касательной','2.1',`
<p><b>Теорема (свойство касательной).</b> Касательная к окружности перпендикулярна радиусу, проведённому в точку касания.</p>
$$OT \\perp \\ell$$
<p style="margin-top:8px">где $O$ — центр, $T$ — точка касания, $\\ell$ — касательная.</p>
<p style="margin-top:8px"><b>Доказательство — от противного.</b> Предположим, что $OT$ не перпендикулярно $\\ell$. Тогда из $O$ можно опустить перпендикуляр на $\\ell$ в точку $H \\neq T$, и $OH &lt; OT = R$. Значит, $H$ лежит внутри окружности. Но тогда прямая $\\ell$ пересекает окружность в двух точках — противоречие с тем, что $\\ell$ касательная. Следовательно, $OT \\perp \\ell$. <b>ч.т.д.</b></p>
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 200 170" style="max-width:220px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<circle cx="100" cy="95" r="58" fill="rgba(6,182,212,.08)" stroke="#0891b2" stroke-width="2"/>
<line cx="100" cy="95" x1="100" y1="95" x2="100" y2="37" stroke="#0891b2" stroke-width="2"/>
<circle cx="100" cy="95" r="3.5" fill="#0e7490"/>
<circle cx="100" cy="37" r="4" fill="#06b6d4"/>
<line x1="30" y1="37" x2="170" y2="37" stroke="#06b6d4" stroke-width="2.5"/>
<polyline points="100,37 109,37 109,46 100,46" fill="none" stroke="#0e7490" stroke-width="1.8"/>
<text x="104" y="70" font-size="11" font-weight="700" fill="#0e7490">R</text>
<text x="105" y="33" font-size="11" font-weight="700" fill="#0e7490">T</text>
<text x="86" y="111" font-size="11" font-weight="700" fill="#0e7490">O</text>
<text x="28" y="30" font-size="10" fill="#0891b2" font-style="italic">&#8467;</text>
<text x="100" y="158" text-anchor="middle" font-size="10" fill="#0e7490" font-weight="700">&#8736;OT&#8467; = 90°</text>
</svg>
</div>`);
html+=makeCard('rule','Следствие: длина касательной из внешней точки','2.2',`
<p>Если из внешней точки $A$ проведена касательная до точки касания $T$, то:</p>
$$AT = \\sqrt{|OA|^2 - R^2}$$
<p style="margin-top:8px">Это следует из теоремы Пифагора для прямоугольного треугольника $OAT$: $|OA|^2 = R^2 + AT^2$.</p>
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 240 160" style="max-width:260px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<circle cx="80" cy="90" r="50" fill="rgba(6,182,212,.08)" stroke="#0891b2" stroke-width="2"/>
<circle cx="80" cy="90" r="3.5" fill="#0e7490"/>
<line x1="80" y1="90" x2="80" y2="40" stroke="#0891b2" stroke-width="2"/>
<circle cx="80" cy="40" r="4" fill="#06b6d4"/>
<line x1="80" y1="40" x2="200" y2="90" stroke="#10b981" stroke-width="2.2"/>
<line x1="80" y1="90" x2="200" y2="90" stroke="#64748b" stroke-width="1.8" stroke-dasharray="5,3"/>
<polyline points="80,40 89,40 89,49 80,49" fill="none" stroke="#0e7490" stroke-width="1.8"/>
<text x="84" y="68" font-size="10" font-weight="700" fill="#0e7490">R</text>
<text x="83" y="37" font-size="11" font-weight="700" fill="#0e7490">T</text>
<text x="64" y="106" font-size="11" font-weight="700" fill="#0e7490">O</text>
<text x="204" y="94" font-size="11" font-weight="700" fill="#065f46">A</text>
<text x="130" y="36" font-size="10" fill="#065f46" font-weight="700">AT</text>
<text x="130" y="106" font-size="10" fill="#64748b" font-style="italic">|OA|</text>
</svg>
</div>`);
html+=makeCard('example','Пример','2.3',`
<p><b>Задача.</b> Радиус окружности $R = 5$, расстояние от внешней точки $A$ до центра $|OA| = 13$. Найти длину касательной $AT$.</p>
<p style="margin-top:8px"><b>Решение:</b> $AT = \\sqrt{|OA|^2 - R^2} = \\sqrt{169 - 25} = \\sqrt{144} = 12$.</p>
<p style="margin-top:8px"><b>Ответ:</b> $AT = 12$.</p>`);
/* ИНТЕРАКТИВ 1 — SVG с угловым маркером перпендикулярности */
html+=`<div class="wg" id="p2-perp-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Касательная всегда перпендикулярна радиусу</div></div>
<div class="wg-help">Перемещай точку касания $T$ по окружности — касательная и радиус $OT$ всегда перпендикулярны. Угловой маркер 90° следует за $T$.</div>
<div class="sliders">
<label>Угол точки $T$: <b id="p2-t-val">90</b>°
<input type="range" min="0" max="359" value="90" id="p2-t-sl">
</label>
</div>
<div id="p2-perp-svg" style="display:flex;justify-content:center"></div>
<div id="p2-perp-info" style="margin-top:8px;padding:8px 14px;background:#d1fae5;border-radius:10px;color:#065f46;font-weight:700;font-size:.94rem;text-align:center">&#8736;OT&#8467; = 90° всегда</div>
</div>`;
/* ИНТЕРАКТИВ 2 — Пошаговое доказательство от противного */
html+=`<div class="wg" id="p2-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="p2-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p2-proof-text" style="padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.94rem;line-height:1.65;margin-bottom:10px;min-height:64px"></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>`;
/* ИНТЕРАКТИВ 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">Дано $R$ и $|OA|$ — найди $AT = \\sqrt{|OA|^2 - R^2}$.</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
<label style="font-size:.9rem">R = <input type="number" id="p2-cr" class="tinp" value="5" min="0.1" style="width:80px"></label>
<label style="font-size:.9rem">|OA| = <input type="number" id="p2-coa" class="tinp" value="13" min="0.1" style="width:80px"></label>
<button class="btn primary" id="p2-calc-btn">Вычислить AT</button>
</div>
<div id="p2-calc-out" style="display:none;padding:10px 14px;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">Тренажёр §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:1rem;margin-bottom:10px;line-height:1.6"></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:130px">
<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 — 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="p2-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="p2-drop-true"><h5>Верно</h5><div class="drop-items" id="p2-drop-true-items"></div></div>
<div class="drop-box" id="p2-drop-false"><h5>Неверно</h5><div class="drop-items" id="p2-drop-false-items"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p2-dnd-check">Проверить</button><button class="btn" id="p2-dnd-reset">Сбросить</button></div>
<div class="feedback" id="p2-dnd-fb" style="display:none"></div>
</div>`;
/* ИНТЕРАКТИВ 6 — Босс §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: вращение T по окружности === */
(function(){
const sl=document.getElementById('p2-t-sl');
const tVal=document.getElementById('p2-t-val');
const svgWrap=document.getElementById('p2-perp-svg');
const cx=140, cy=110, R=70, W=280, H=210;
function draw(){
const angDeg=+sl.value;
tVal.textContent=angDeg;
const angRad=(angDeg-90)*Math.PI/180;
const Tx=cx+R*Math.cos(angRad);
const Ty=cy+R*Math.sin(angRad);
// unit vectors
const urx=(Tx-cx)/R, ury=(Ty-cy)/R;
const utx=-ury, uty=urx; // tangent unit vector (perp to radius)
const tLen=80;
const lx1=Tx-utx*tLen, ly1=Ty-uty*tLen;
const lx2=Tx+utx*tLen, ly2=Ty+uty*tLen;
// right angle marker at T: 9px square
const s=9;
const mx1=Tx+urx*s, my1=Ty+ury*s;
const mx2=Tx+urx*s+utx*s, my2=Ty+ury*s+uty*s;
const mx3=Tx+utx*s, my3=Ty+uty*s;
const svg=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<circle cx="${cx}" cy="${cy}" r="${R}" fill="rgba(6,182,212,.09)" stroke="#06b6d4" stroke-width="2"/>
<circle cx="${cx}" cy="${cy}" r="3.5" fill="#0e7490"/>
<line x1="${cx}" y1="${cy}" x2="${Tx.toFixed(1)}" y2="${Ty.toFixed(1)}" stroke="#0891b2" stroke-width="2.2"/>
<circle cx="${Tx.toFixed(1)}" cy="${Ty.toFixed(1)}" r="5" fill="#06b6d4"/>
<line x1="${lx1.toFixed(1)}" y1="${ly1.toFixed(1)}" x2="${lx2.toFixed(1)}" y2="${ly2.toFixed(1)}" stroke="#10b981" stroke-width="2.5"/>
<polyline points="${mx1.toFixed(1)},${my1.toFixed(1)} ${mx2.toFixed(1)},${my2.toFixed(1)} ${mx3.toFixed(1)},${my3.toFixed(1)}" fill="none" stroke="#0e7490" stroke-width="1.8"/>
<text x="${cx+4}" y="${cy+5}" font-size="11" font-weight="700" fill="#0e7490">O</text>
<text x="${(Tx+4).toFixed(1)}" y="${(Ty-6).toFixed(1)}" font-size="11" font-weight="700" fill="#0e7490">T</text>
<text x="10" y="${H-8}" font-size="10" fill="#065f46" font-weight="700">&#8467; (касательная)</text>
<text x="${cx}" y="${H-8}" font-size="10" fill="#065f46" font-weight="700" text-anchor="middle">&#8736;OT&#8467; = 90°</text>
</svg>`;
svgWrap.innerHTML=svg;
}
sl.addEventListener('input',draw);
draw();
})();
/* === INIT ИНТЕРАКТИВ 2: доказательство от противного === */
(function(){
const steps=[
{text:'<b>Дано:</b> окружность с центром $O$ и радиусом $R$; прямая $\\ell$ касается окружности в точке $T$ ($\\ell$ имеет с окружностью ровно одну общую точку). Доказать: $OT \\perp \\ell$.',
phase:'given'},
{text:'<b>Допущение (от противного).</b> Предположим, что $OT$ не перпендикулярно $\\ell$. Тогда из $O$ можно опустить перпендикуляр $OH$ на $\\ell$, где $H \\neq T$.',
phase:'contra'},
{text:'<b>Шаг 2.</b> В прямоугольном треугольнике $OHT$ гипотенуза $OT = R$, катет $OH &lt; OT$. Значит, $OH &lt; R$ — точка $H$ лежит <em>внутри</em> окружности.',
phase:'inside'},
{text:'<b>Шаг 3.</b> Прямая $\\ell$, проходящая через точку $H$ внутри окружности, обязана пересекать окружность в двух точках. Но $\\ell$ — касательная (одна общая точка). Противоречие!',
phase:'contradiction'},
{text:'<b>Вывод.</b> Допущение неверно. Следовательно, $OT \\perp \\ell$. <b>ч.т.д.</b>',
phase:'done'},
];
let step=0;
const svgEl=document.getElementById('p2-proof-svg');
const txtEl=document.getElementById('p2-proof-text');
const nextBtn=document.getElementById('p2-proof-next');
const resetBtn=document.getElementById('p2-proof-reset');
const cx=130, cy=100, R=65;
function drawProof(ph){
const Tx=cx, Ty=cy-R;
let extras='';
if(ph==='contra'||ph==='inside'||ph==='contradiction'||ph==='done'){
const Hx=cx+40, Hy=Ty;
extras+=`<circle cx="${Hx}" cy="${Hy}" r="4" fill="#ef4444"/>`;
extras+=`<text x="${Hx+4}" y="${Hy-5}" font-size="11" font-weight="700" fill="#7f1d1d">H</text>`;
extras+=`<line x1="${cx}" y1="${cy}" x2="${Hx}" y2="${Hy}" stroke="#ef4444" stroke-width="1.8" stroke-dasharray="4,3"/>`;
if(ph==='inside'||ph==='contradiction'){
extras+=`<circle cx="${Hx}" cy="${Hy}" r="6" fill="none" stroke="#ef4444" stroke-width="1.5" stroke-dasharray="2,2"/>`;
}
}
const color=ph==='done'?'#10b981':'#06b6d4';
const svg=`<svg viewBox="0 0 260 190" style="max-width:280px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<circle cx="${cx}" cy="${cy}" r="${R}" fill="rgba(6,182,212,.09)" stroke="${color}" stroke-width="2"/>
<line x1="${cx}" y1="${cy}" x2="${Tx}" y2="${Ty}" stroke="#0891b2" stroke-width="2"/>
<circle cx="${cx}" cy="${cy}" r="3.5" fill="#0e7490"/>
<circle cx="${Tx}" cy="${Ty}" r="4" fill="${color}"/>
<line x1="20" y1="${Ty}" x2="240" y2="${Ty}" stroke="${color}" stroke-width="2.2"/>
<polyline points="${Tx},${Ty+9} ${Tx+9},${Ty+9} ${Tx+9},${Ty}" fill="none" stroke="#0e7490" stroke-width="1.8"/>
<text x="${cx+4}" y="${cy+5}" font-size="11" font-weight="700" fill="#0e7490">O</text>
<text x="${Tx+4}" y="${Ty-6}" font-size="11" font-weight="700" fill="#0e7490">T</text>
<text x="22" y="${Ty-5}" font-size="10" fill="#065f46" font-style="italic">&#8467;</text>
${extras}
</svg>`;
svgEl.innerHTML=svg;
txtEl.innerHTML=steps.find(s=>s.phase===ph).text;
if(window.renderMathInElement) try{renderMath(txtEl);}catch(e){}
}
const phases=steps.map(s=>s.phase);
function go(){
drawProof(phases[step]);
nextBtn.textContent=step>=phases.length-1?'Готово':'Далее';
if(step<phases.length-1) step++;
}
nextBtn.addEventListener('click',go);
resetBtn.addEventListener('click',()=>{step=0;go();});
go();
})();
/* === INIT ИНТЕРАКТИВ 3: калькулятор === */
(function(){
const btn=document.getElementById('p2-calc-btn');
const out=document.getElementById('p2-calc-out');
btn.addEventListener('click',()=>{
const R=parseFloat(document.getElementById('p2-cr').value);
const OA=parseFloat(document.getElementById('p2-coa').value);
out.style.display='block';
if(isNaN(R)||isNaN(OA)||R<=0||OA<=0){out.style.background='#fee2e2';out.style.color='#7f1d1d';out.textContent='Введите корректные значения';return;}
if(OA<R){out.style.background='#fee2e2';out.style.color='#7f1d1d';out.textContent='|OA| должно быть больше R (точка A должна быть вне окружности)';return;}
if(Math.abs(OA-R)<1e-9){out.style.background='#fef3c7';out.style.color='#92400e';out.textContent='|OA| = R: точка A на окружности, длина касательной = 0';return;}
const AT=Math.sqrt(OA*OA-R*R);
out.style.background='#d1fae5';out.style.color='#065f46';
out.innerHTML='AT = √('+fmt(OA)+ '+fmt(R)+'²) = √'+fmt(OA*OA-R*R)+' = <b>'+fmt(AT)+'</b>';
});
})();
/* === INIT ИНТЕРАКТИВ 4: Тренажёр === */
(function(){
const tasks=[
{q:'Радиус $R = 5$, $|OA| = 13$. Найди длину касательной $AT$.',a:'12',hint:'AT = √(169 25) = √144 = 12'},
{q:'Радиус $R = 8$, $|OA| = 10$. Найди $AT$.',a:'6',hint:'AT = √(100 64) = √36 = 6'},
{q:'Касательная $AT = 24$, $|OA| = 26$. Найди радиус $R$.',a:'10',hint:'R = √(26² 24²) = √(676576) = √100 = 10'},
{q:'Касательная $AT = 15$, радиус $R = 9$. Найди $|OA|$.',a:'√306',hint:'OA = √(225 + 81) = √306 ≈ 17,49. Введи 306 (под корнем)'},
{q:'Угол между радиусом $OT$ и прямой $OA$ равен 60°, $|OA| = 10$. Найди радиус $R = |OT|$.',a:'5',hint:'OT = OA·cos60° = 10·0,5 = 5 (касательная AT ⊥ OT)'},
];
let cur=0,score=0;
const iEl=document.getElementById('p2-tr-i');
const scEl=document.getElementById('p2-tr-score');
const taskEl=document.getElementById('p2-tr-task');
const ansEl=document.getElementById('p2-tr-ans');
const goBtn=document.getElementById('p2-tr-go');
const startBtn=document.getElementById('p2-tr-start');
const fb=document.getElementById('p2-tr-fb');
const numAnswers={'√306':Math.sqrt(306)};
function showTask(){
if(cur>=tasks.length){taskEl.innerHTML='<b>Тренажёр завершён! Очки: '+score+'/'+tasks.length+'</b>';ansEl.style.display='none';goBtn.style.display='none';addXp(score*4,'p2-trainer');bumpProgress('p2',20);return;}
taskEl.innerHTML=tasks[cur].q;iEl.textContent=cur+1;ansEl.value='';fb.style.display='none';
if(window.renderMathInElement) try{renderMath(taskEl);}catch(e){}
}
goBtn.addEventListener('click',()=>{
const raw=ansEl.value.trim().replace(',','.');
const expected=tasks[cur].a;
const expNum=numAnswers[expected]!==undefined?numAnswers[expected]:parseFloat(expected);
const ansNum=parseFloat(raw);
const ok=Math.abs(ansNum-expNum)<0.5;
feedback(fb,ok,ok?'Верно!':'Неверно. '+tasks[cur].hint);
if(ok){score++;scEl.textContent=score;cur++;setTimeout(showTask,900);}
});
startBtn.addEventListener('click',()=>{cur=0;score=0;scEl.textContent=0;ansEl.style.display='';goBtn.style.display='';showTask();});
showTask();
})();
/* === INIT ИНТЕРАКТИВ 5: DnD верные/неверные === */
(function(){
const items=[
{label:'Касательная ⊥ радиусу в точке касания',cat:'true'},
{label:'Касательная параллельна радиусу',cat:'false'},
{label:'Расстояние от центра до касательной = R',cat:'true'},
{label:'Из внешней точки можно провести только одну касательную',cat:'false'},
{label:'AT = √(|OA|² R²)',cat:'true'},
{label:'AT > |OA|',cat:'false'},
];
const pool=document.getElementById('p2-dnd-pool');
const boxes={true:document.getElementById('p2-drop-true-items'),false:document.getElementById('p2-drop-false-items')};
const fb=document.getElementById('p2-dnd-fb');
let placed={};
function reset(){
pool.innerHTML='';placed={};
Object.values(boxes).forEach(b=>b.innerHTML='');
fb.style.display='none';
items.forEach((it,i)=>{
const chip=document.createElement('div');
chip.className='dnd-chip';chip.dataset.i=i;chip.textContent=it.label;
chip.addEventListener('click',()=>chip.classList.toggle('armed'));
Object.entries(boxes).forEach(([cat,box])=>{
box.parentElement.addEventListener('click',()=>{
if(!chip.classList.contains('armed'))return;
chip.classList.remove('armed');
if(placed[i]!==undefined) boxes[placed[i]].removeChild(chip);
placed[i]=cat;
box.appendChild(chip);
});
});
pool.appendChild(chip);
});
}
document.getElementById('p2-dnd-check').addEventListener('click',()=>{
let ok=0;
items.forEach((it,i)=>{if(placed[i]===it.cat)ok++;});
feedback(fb,ok===items.length,'Верно: '+ok+'/'+items.length+(ok===items.length?'. Отлично!':'. Попробуй ещё.'));
if(ok===items.length){addXp(8,'p2-dnd');bumpProgress('p2',15);}
});
document.getElementById('p2-dnd-reset').addEventListener('click',reset);
reset();
})();
/* === INIT ИНТЕРАКТИВ 6: Босс §2 === */
(function(){
const tasks=[
{q:'Касательная $AT = 24$, радиус $R = 7$. Найти $|OA|$.',
opts:['25','√(576+49)=√625=25','√(24²−7²)=√527'],cor:1,
exp:'$|OA| = \\sqrt{AT^2 + R^2} = \\sqrt{576 + 49} = \\sqrt{625} = 25$.'},
{q:'Угол $\\angle TOA = 30°$, $|OA| = 20$. Найти длину касательной $AT$.',
opts:['10','10√3','20√3'],cor:1,
exp:'В прямоугольном треугольнике $OAT$: $AT = OA \\cdot \\sin 30° \\cdot ...$. Нет: $\\angle OTA = 90°$, поэтому $AT = OA \\cdot \\sin(\\angle AOT) \\cdot ...$. Точнее: $\\cos(\\angle TAO) = AT/OA$, $\\angle OAT + \\angle AOT = 90°$, $\\angle OAT = 60°$, $AT = OA \\cdot \\sin 60° = 20 \\cdot \\dfrac{\\sqrt{3}}{2} = 10\\sqrt{3}$.'},
{q:'Точка $A$ находится вне окружности. Из $A$ проведены касательные $AT_1$ и $AT_2$. Угол $\\angle T_1OT_2 = 120°$. Найти $\\angle T_1AT_2$.',
opts:['30°','60°','120°'],cor:1,
exp:'Четырёхугольник $AT_1OT_2$ имеет $\\angle T_1 = \\angle T_2 = 90°$, сумма углов $360°$. Значит $\\angle A + \\angle O = 180°$: $\\angle A = 60°$.'},
{q:'Радиус $R = 10$, касательная $AT = 10\\sqrt{3}$. Найти $|OA|$.',
opts:['10','20','10√2'],cor:1,
exp:'$|OA| = \\sqrt{R^2 + AT^2} = \\sqrt{100 + 300} = \\sqrt{400} = 20$.'},
];
const cont=document.getElementById('p2-boss-tasks');
let html='';
tasks.forEach((t,i)=>{
html+=`<div style="margin-bottom:14px;padding:14px;background:var(--card);border:1px solid var(--border);border-radius:10px">
<div style="font-weight:600;margin-bottom:8px;font-size:.94rem">${i+1}. ${t.q}</div>
<div style="display:flex;flex-direction:column;gap:6px">
${t.opts.map((o,j)=>`<button class="btn" data-boss2="p2-${i}" data-j="${j}" onclick="(function(btn){
const btns=document.querySelectorAll('[data-boss2=\\'p2-${i}\\']');
btns.forEach(b=>b.disabled=true);
const ok=${j}===${t.cor};
btn.style.background=ok?'#d1fae5':'#fee2e2';
btn.style.borderColor=ok?'#10b981':'#ef4444';
btn.style.color=ok?'#065f46':'#7f1d1d';
const exp=document.getElementById('p2-boss-exp-${i}');
exp.style.display='block';exp.innerHTML='<b>'+(ok?'Верно!':'Неверно.')+'</b> ${t.exp.replace(/\\/g,'\\\\').replace(/'/g,"\\'")}';
if(window.renderMathInElement) try{renderMath(exp);}catch(e){}
if(ok){addXp(5,'p2-boss-${i}');bumpProgress('p2',5);}
})(this)" style="text-align:left">${o}</button>`).join('')}
</div>
<div id="p2-boss-exp-${i}" style="display:none;margin-top:8px;padding:8px 12px;border-radius:8px;font-size:.88rem;background:var(--sec-acc-soft,var(--pri-soft));color:var(--sec-acc-d,var(--pri2));line-height:1.55"></div>
</div>`;
});
cont.innerHTML=html;
if(window.renderMathInElement) try{renderMath(cont);}catch(e){}
})();
}
function buildP3(){
const box=document.getElementById('p3-body');
let html='';
html+=makeCard('theory','Свойство касательных из одной точки','3.1',`
<p><b>Теорема.</b> Если из внешней точки $A$ к окружности проведены две касательные с точками касания $T_1$ и $T_2$, то:</p>
<ul style="margin-left:18px;margin-top:6px;line-height:1.9">
<li><b>Равенство отрезков:</b> $|AT_1| = |AT_2|$</li>
<li><b>Биссектриса:</b> прямая $OA$ является биссектрисой угла $T_1AT_2$ (и угла $T_1OT_2$).</li>
</ul>
<div style="display:flex;justify-content:center;margin-top:14px">
<svg viewBox="0 0 260 180" style="max-width:280px;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<circle cx="100" cy="90" r="55" fill="rgba(2,132,199,.08)" stroke="#0284c7" stroke-width="2"/>
<circle cx="100" cy="90" r="3.5" fill="#0369a1"/>
<line x1="100" y1="90" x2="100" y2="35" stroke="#0284c7" stroke-width="1.8" stroke-dasharray="4,3"/>
<line x1="100" y1="90" x2="136" y2="132" stroke="#0284c7" stroke-width="1.8" stroke-dasharray="4,3"/>
<circle cx="100" cy="35" r="4" fill="#0284c7"/>
<circle cx="136" cy="132" r="4" fill="#0284c7"/>
<line x1="100" y1="35" x2="220" y2="82" stroke="#10b981" stroke-width="2.2"/>
<line x1="136" y1="132" x2="220" y2="82" stroke="#10b981" stroke-width="2.2"/>
<line x1="100" y1="90" x2="220" y2="82" stroke="#64748b" stroke-width="1.5" stroke-dasharray="5,3"/>
<circle cx="220" cy="82" r="4" fill="#0369a1"/>
<text x="95" y="107" font-size="11" font-weight="700" fill="#0369a1">O</text>
<text x="97" y="30" font-size="11" font-weight="700" fill="#0369a1">T₁</text>
<text x="140" y="148" font-size="11" font-weight="700" fill="#0369a1">T₂</text>
<text x="224" y="86" font-size="11" font-weight="700" fill="#0369a1">A</text>
<text x="162" y="50" font-size="10" fill="#065f46" font-weight="700">AT₁</text>
<text x="188" y="128" font-size="10" fill="#065f46" font-weight="700">AT₂</text>
<text x="100" y="170" text-anchor="middle" font-size="10" fill="#0369a1" font-weight="700">AT₁ = AT₂</text>
</svg>
</div>`);
html+=makeCard('rule','Доказательство через равные треугольники','3.2',`
<p>Рассмотрим треугольники $\\triangle OAT_1$ и $\\triangle OAT_2$:</p>
<ul style="margin-left:18px;margin-top:6px;line-height:1.9">
<li>$OT_1 = OT_2 = R$ (оба радиусы)</li>
<li>$OA = OA$ (общая гипотенуза)</li>
<li>$\\angle OT_1A = \\angle OT_2A = 90°$ (свойство касательной)</li>
</ul>
<p style="margin-top:8px">По признаку равенства прямоугольных треугольников (по гипотенузе и катету): $\\triangle OAT_1 = \\triangle OAT_2$.</p>
<p style="margin-top:6px">Следовательно, $AT_1 = AT_2$ и $\\angle T_1AO = \\angle T_2AO$. <b>ч.т.д.</b></p>`);
html+=makeCard('example','Пример','3.3',`
<p><b>Задача.</b> Из точки $A$ проведены касательные к окружности радиуса $R = 8$. Расстояние от $A$ до центра $|OA| = 17$. Найти длины касательных.</p>
<p style="margin-top:8px"><b>Решение:</b> $AT_1 = AT_2 = \\sqrt{|OA|^2 - R^2} = \\sqrt{289 - 64} = \\sqrt{225} = 15$.</p>
<p style="margin-top:8px"><b>Ответ:</b> $AT_1 = AT_2 = 15$.</p>`);
/* ИНТЕРАКТИВ 1 — SVG с двумя касательными из внешней точки A */
html+=`<div class="wg" id="p3-two-wg">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Две касательные из внешней точки</div></div>
<div class="wg-help">Двигай слайдер — меняй положение точки $A$. Отрезки $AT_1$ и $AT_2$ всегда равны!</div>
<div class="sliders">
<label>Расстояние $|OA|$: <b id="p3-oa-val">130</b> (R = 65)
<input type="range" min="70" max="200" value="130" id="p3-oa-sl">
</label>
</div>
<div id="p3-two-svg" style="display:flex;justify-content:center"></div>
<div id="p3-two-info" style="margin-top:8px;padding:8px 14px;background:#d1fae5;border-radius:10px;color:#065f46;font-weight:700;font-size:.93rem;text-align:center"></div>
</div>`;
/* ИНТЕРАКТИВ 2 — Пошаговое доказательство */
html+=`<div class="wg" id="p3-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="p3-proof-svg" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div id="p3-proof-text" style="padding:10px 14px;background:var(--sec-acc-soft,var(--pri-soft));border-radius:10px;font-size:.94rem;line-height:1.65;margin-bottom:10px;min-height:64px"></div>
<div style="display:flex;gap:8px">
<button class="btn primary" id="p3-proof-next">Далее</button>
<button class="btn" id="p3-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">Дано $R$ и $|OA|$ — найди $|AT| = \\sqrt{|OA|^2 - R^2}$.</div>
<div style="display:flex;gap:10px;flex-wrap:wrap;align-items:center;margin-bottom:10px">
<label style="font-size:.9rem">R = <input type="number" id="p3-cr" class="tinp" value="8" min="0.1" style="width:80px"></label>
<label style="font-size:.9rem">|OA| = <input type="number" id="p3-coa" class="tinp" value="17" min="0.1" style="width:80px"></label>
<button class="btn primary" id="p3-calc-btn">Вычислить |AT|</button>
</div>
<div id="p3-calc-out" style="display:none;padding:10px 14px;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">Тренажёр §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:1rem;margin-bottom:10px;line-height:1.6"></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:130px">
<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 — 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="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-true"><h5>Верно</h5><div class="drop-items" id="p3-drop-true-items"></div></div>
<div class="drop-box" id="p3-drop-false"><h5>Неверно</h5><div class="drop-items" id="p3-drop-false-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>`;
/* ИНТЕРАКТИВ 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: две касательные из A === */
(function(){
const sl=document.getElementById('p3-oa-sl');
const oaVal=document.getElementById('p3-oa-val');
const svgWrap=document.getElementById('p3-two-svg');
const info=document.getElementById('p3-two-info');
const R=65, cx=100, cy=130, W=280, H=240;
function draw(){
const OA=+sl.value;
oaVal.textContent=OA;
const Ax=cx+OA, Ay=cy;
const AT=Math.sqrt(OA*OA-R*R);
const sinA=R/OA, cosA=AT/OA;
const T1x=cx+R*cosA, T1y=cy-R*sinA;
const T2x=cx+R*cosA, T2y=cy+R*sinA;
// right angle markers
const ur1x=(T1x-cx)/R, ur1y=(T1y-cy)/R;
const ut1x=-(ur1y), ut1y=ur1x;
const s=8;
const rm1=`${(T1x+ur1x*s).toFixed(1)},${(T1y+ur1y*s).toFixed(1)} ${(T1x+ur1x*s+ut1x*s).toFixed(1)},${(T1y+ur1y*s+ut1y*s).toFixed(1)} ${(T1x+ut1x*s).toFixed(1)},${(T1y+ut1y*s).toFixed(1)}`;
const ur2x=(T2x-cx)/R, ur2y=(T2y-cy)/R;
const ut2x=-(ur2y), ut2y=ur2x;
const rm2=`${(T2x+ur2x*s).toFixed(1)},${(T2y+ur2y*s).toFixed(1)} ${(T2x+ur2x*s+ut2x*s).toFixed(1)},${(T2y+ur2y*s+ut2y*s).toFixed(1)} ${(T2x+ut2x*s).toFixed(1)},${(T2y+ut2y*s).toFixed(1)}`;
const svg=`<svg viewBox="0 0 ${W} ${H}" style="max-width:${W}px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<circle cx="${cx}" cy="${cy}" r="${R}" fill="rgba(2,132,199,.09)" stroke="#0284c7" stroke-width="2"/>
<circle cx="${cx}" cy="${cy}" r="3.5" fill="#0369a1"/>
<line x1="${cx}" y1="${cy}" x2="${T1x.toFixed(1)}" y2="${T1y.toFixed(1)}" stroke="#0284c7" stroke-width="1.8" stroke-dasharray="4,3"/>
<line x1="${cx}" y1="${cy}" x2="${T2x.toFixed(1)}" y2="${T2y.toFixed(1)}" stroke="#0284c7" stroke-width="1.8" stroke-dasharray="4,3"/>
<line x1="${T1x.toFixed(1)}" y1="${T1y.toFixed(1)}" x2="${Ax}" y2="${Ay}" stroke="#10b981" stroke-width="2.5"/>
<line x1="${T2x.toFixed(1)}" y1="${T2y.toFixed(1)}" x2="${Ax}" y2="${Ay}" stroke="#10b981" stroke-width="2.5"/>
<line x1="${cx}" y1="${cy}" x2="${Ax}" y2="${Ay}" stroke="#64748b" stroke-width="1.5" stroke-dasharray="5,3"/>
<circle cx="${T1x.toFixed(1)}" cy="${T1y.toFixed(1)}" r="4.5" fill="#0284c7"/>
<circle cx="${T2x.toFixed(1)}" cy="${T2y.toFixed(1)}" r="4.5" fill="#0284c7"/>
<circle cx="${Ax}" cy="${Ay}" r="5" fill="#0369a1"/>
<polyline points="${rm1}" fill="none" stroke="#0369a1" stroke-width="1.8"/>
<polyline points="${rm2}" fill="none" stroke="#0369a1" stroke-width="1.8"/>
<text x="${(T1x-14).toFixed(1)}" y="${(T1y-6).toFixed(1)}" font-size="11" font-weight="700" fill="#0369a1">T₁</text>
<text x="${(T2x-14).toFixed(1)}" y="${(T2y+14).toFixed(1)}" font-size="11" font-weight="700" fill="#0369a1">T₂</text>
<text x="${cx-14}" y="${cy+5}" font-size="11" font-weight="700" fill="#0369a1">O</text>
<text x="${Ax+5}" y="${Ay+5}" font-size="11" font-weight="700" fill="#0369a1">A</text>
<text x="${(T1x+Ax)/2+3}" y="${(T1y+Ay)/2-4}" font-size="10" fill="#065f46" font-weight="700">${fmt(AT)}</text>
<text x="${(T2x+Ax)/2+3}" y="${(T2y+Ay)/2+10}" font-size="10" fill="#065f46" font-weight="700">${fmt(AT)}</text>
</svg>`;
svgWrap.innerHTML=svg;
info.textContent='AT₁ = AT₂ = '+fmt(AT)+' (при R = '+R+', |OA| = '+OA+')';
}
sl.addEventListener('input',draw);
draw();
})();
/* === INIT ИНТЕРАКТИВ 2: пошаговое доказательство === */
(function(){
const steps=[
{text:'<b>Дано:</b> окружность с центром $O$, радиусом $R$; из внешней точки $A$ проведены касательные $AT_1$ и $AT_2$ с точками касания $T_1$ и $T_2$. Доказать: $AT_1 = AT_2$.',
phase:'given'},
{text:'<b>Шаг 1.</b> По свойству касательной: $OT_1 \\perp AT_1$ и $OT_2 \\perp AT_2$, то есть $\\angle OT_1A = \\angle OT_2A = 90°$. Треугольники $\\triangle OAT_1$ и $\\triangle OAT_2$ — прямоугольные.',
phase:'right'},
{text:'<b>Шаг 2.</b> Выпишем элементы для признака равенства: $OA = OA$ (общая гипотенуза), $OT_1 = OT_2 = R$ (катеты — радиусы одной окружности).',
phase:'elements'},
{text:'<b>Шаг 3.</b> По признаку равенства прямоугольных треугольников (по гипотенузе и катету): $\\triangle OAT_1 = \\triangle OAT_2$.',
phase:'equal'},
{text:'<b>Вывод.</b> Из равенства треугольников: $AT_1 = AT_2$ (соответственные катеты) и $\\angle T_1AO = \\angle T_2AO$ (прямая $OA$ — биссектриса). <b>ч.т.д.</b>',
phase:'done'},
];
let step=0;
const svgEl=document.getElementById('p3-proof-svg');
const txtEl=document.getElementById('p3-proof-text');
const nextBtn=document.getElementById('p3-proof-next');
const resetBtn=document.getElementById('p3-proof-reset');
const R=55, cx=100, cy=105;
const OA=130;
const AT=Math.sqrt(OA*OA-R*R);
const sinA=R/OA, cosA=AT/OA;
const T1x=cx+R*cosA, T1y=cy-R*sinA;
const T2x=cx+R*cosA, T2y=cy+R*sinA;
const Ax=cx+OA, Ay=cy;
function drawProof(ph){
let tri1='',tri2='',equal='';
if(ph==='right'||ph==='elements'||ph==='equal'||ph==='done'){
tri1=`<polygon points="${cx},${cy} ${T1x.toFixed(1)},${T1y.toFixed(1)} ${Ax},${Ay}" fill="rgba(6,182,212,.12)" stroke="#06b6d4" stroke-width="1.3"/>`;
tri2=`<polygon points="${cx},${cy} ${T2x.toFixed(1)},${T2y.toFixed(1)} ${Ax},${Ay}" fill="rgba(2,132,199,.10)" stroke="#0284c7" stroke-width="1.3"/>`;
const s=7;
const ur1x=(T1x-cx)/R, ur1y=(T1y-cy)/R;
const ut1x=-(ur1y), ut1y=ur1x;
const rm1=`${(T1x+ur1x*s).toFixed(1)},${(T1y+ur1y*s).toFixed(1)} ${(T1x+ur1x*s+ut1x*s).toFixed(1)},${(T1y+ur1y*s+ut1y*s).toFixed(1)} ${(T1x+ut1x*s).toFixed(1)},${(T1y+ut1y*s).toFixed(1)}`;
const ur2x=(T2x-cx)/R, ur2y=(T2y-cy)/R;
const ut2x=-(ur2y), ut2y=ur2x;
const rm2=`${(T2x+ur2x*s).toFixed(1)},${(T2y+ur2y*s).toFixed(1)} ${(T2x+ur2x*s+ut2x*s).toFixed(1)},${(T2y+ur2y*s+ut2y*s).toFixed(1)} ${(T2x+ut2x*s).toFixed(1)},${(T2y+ut2y*s).toFixed(1)}`;
equal=`<polyline points="${rm1}" fill="none" stroke="#0369a1" stroke-width="1.8"/><polyline points="${rm2}" fill="none" stroke="#0369a1" stroke-width="1.8"/>`;
}
if(ph==='done'||ph==='equal'){
equal+=`<text x="${(T1x+Ax)/2+2}" y="${(T1y+Ay)/2-3}" font-size="9" fill="#065f46" font-weight="800">✓</text>`;
equal+=`<text x="${(T2x+Ax)/2+2}" y="${(T2y+Ay)/2+8}" font-size="9" fill="#065f46" font-weight="800">✓</text>`;
}
const mainColor=ph==='done'?'#10b981':'#0284c7';
const svg=`<svg viewBox="0 0 260 200" style="max-width:280px;width:100%;background:#fafafa;border:1px solid var(--border);border-radius:10px">
<circle cx="${cx}" cy="${cy}" r="${R}" fill="rgba(2,132,199,.08)" stroke="${mainColor}" stroke-width="2"/>
${tri1}${tri2}
<line x1="${cx}" y1="${cy}" x2="${T1x.toFixed(1)}" y2="${T1y.toFixed(1)}" stroke="#0284c7" stroke-width="1.8" stroke-dasharray="4,3"/>
<line x1="${cx}" y1="${cy}" x2="${T2x.toFixed(1)}" y2="${T2y.toFixed(1)}" stroke="#0284c7" stroke-width="1.8" stroke-dasharray="4,3"/>
<line x1="${T1x.toFixed(1)}" y1="${T1y.toFixed(1)}" x2="${Ax}" y2="${Ay}" stroke="#10b981" stroke-width="2.2"/>
<line x1="${T2x.toFixed(1)}" y1="${T2y.toFixed(1)}" x2="${Ax}" y2="${Ay}" stroke="#10b981" stroke-width="2.2"/>
<line x1="${cx}" y1="${cy}" x2="${Ax}" y2="${Ay}" stroke="#64748b" stroke-width="1.5" stroke-dasharray="5,3"/>
${equal}
<circle cx="${T1x.toFixed(1)}" cy="${T1y.toFixed(1)}" r="4" fill="#0284c7"/>
<circle cx="${T2x.toFixed(1)}" cy="${T2y.toFixed(1)}" r="4" fill="#0284c7"/>
<circle cx="${cx}" cy="${cy}" r="3.5" fill="#0369a1"/>
<circle cx="${Ax}" cy="${Ay}" r="4.5" fill="#0369a1"/>
<text x="${cx-12}" y="${cy+5}" font-size="11" font-weight="700" fill="#0369a1">O</text>
<text x="${(T1x-12).toFixed(1)}" y="${(T1y-5).toFixed(1)}" font-size="11" font-weight="700" fill="#0369a1">T₁</text>
<text x="${(T2x-12).toFixed(1)}" y="${(T2y+14).toFixed(1)}" font-size="11" font-weight="700" fill="#0369a1">T₂</text>
<text x="${Ax+4}" y="${Ay+5}" font-size="11" font-weight="700" fill="#0369a1">A</text>
</svg>`;
svgEl.innerHTML=svg;
txtEl.innerHTML=steps.find(s=>s.phase===ph).text;
if(window.renderMathInElement) try{renderMath(txtEl);}catch(e){}
}
const phases=steps.map(s=>s.phase);
function go(){
drawProof(phases[step]);
nextBtn.textContent=step>=phases.length-1?'Готово':'Далее';
if(step<phases.length-1) step++;
}
nextBtn.addEventListener('click',go);
resetBtn.addEventListener('click',()=>{step=0;go();});
go();
})();
/* === INIT ИНТЕРАКТИВ 3: калькулятор === */
(function(){
const btn=document.getElementById('p3-calc-btn');
const out=document.getElementById('p3-calc-out');
btn.addEventListener('click',()=>{
const R=parseFloat(document.getElementById('p3-cr').value);
const OA=parseFloat(document.getElementById('p3-coa').value);
out.style.display='block';
if(isNaN(R)||isNaN(OA)||R<=0||OA<=0){out.style.background='#fee2e2';out.style.color='#7f1d1d';out.textContent='Введите корректные значения';return;}
if(OA<=R){out.style.background='#fee2e2';out.style.color='#7f1d1d';out.textContent='|OA| должно быть больше R';return;}
const AT=Math.sqrt(OA*OA-R*R);
out.style.background='#d1fae5';out.style.color='#065f46';
out.innerHTML='|AT₁| = |AT₂| = √('+fmt(OA)+ '+fmt(R)+'²) = √'+fmt(OA*OA-R*R)+' = <b>'+fmt(AT)+'</b>';
});
})();
/* === INIT ИНТЕРАКТИВ 4: Тренажёр === */
(function(){
const tasks=[
{q:'Из точки $A$ проведены касательные к окружности радиуса $R = 6$, $|OA| = 10$. Найти длину каждой касательной.',a:'8',hint:'AT = √(100 36) = √64 = 8'},
{q:'Касательная $AT_1 = 15$, $R = 9$. Найти $|OA|$.',a:'√306',hint:'OA = √(225 + 81) = √306. Введи приближение: 17'},
{q:'Из точки $A$ проведены две касательные. $AT_1 = 12$, $AT_2 = ?$',a:'12',hint:'Два отрезка касательных из одной точки равны'},
{q:'$|OA| = 25$, $AT = 24$. Найти $R$.',a:'7',hint:'R = √(25² 24²) = √(625576) = √49 = 7'},
{q:'Угол между двумя касательными из точки $A$ равен $60°$. Угол $\\angle T_1OT_2 = ?$ (в градусах)',a:'120',hint:'Сумма углов A и O в четырёхугольнике AT₁OT₂ равна 180°'},
];
let cur=0,score=0;
const iEl=document.getElementById('p3-tr-i');
const scEl=document.getElementById('p3-tr-score');
const taskEl=document.getElementById('p3-tr-task');
const ansEl=document.getElementById('p3-tr-ans');
const goBtn=document.getElementById('p3-tr-go');
const startBtn=document.getElementById('p3-tr-start');
const fb=document.getElementById('p3-tr-fb');
function numOf(s){if(s==='√306')return Math.sqrt(306);return parseFloat(s);}
function showTask(){
if(cur>=tasks.length){taskEl.innerHTML='<b>Тренажёр завершён! Очки: '+score+'/'+tasks.length+'</b>';ansEl.style.display='none';goBtn.style.display='none';addXp(score*4,'p3-trainer');bumpProgress('p3',20);return;}
taskEl.innerHTML=tasks[cur].q;iEl.textContent=cur+1;ansEl.value='';fb.style.display='none';
if(window.renderMathInElement) try{renderMath(taskEl);}catch(e){}
}
goBtn.addEventListener('click',()=>{
const raw=ansEl.value.trim().replace(',','.');
const ok=Math.abs(parseFloat(raw)-numOf(tasks[cur].a))<0.6;
feedback(fb,ok,ok?'Верно!':'Неверно. '+tasks[cur].hint);
if(ok){score++;scEl.textContent=score;cur++;setTimeout(showTask,900);}
});
startBtn.addEventListener('click',()=>{cur=0;score=0;scEl.textContent=0;ansEl.style.display='';goBtn.style.display='';showTask();});
showTask();
})();
/* === INIT ИНТЕРАКТИВ 5: DnD === */
(function(){
const items=[
{label:'AT₁ = AT₂ (из одной внешней точки)',cat:'true'},
{label:'AT₁ > AT₂ если T₁ выше T₂',cat:'false'},
{label:'OA — биссектриса угла T₁AT₂',cat:'true'},
{label:'∠OT₁A = 90°',cat:'true'},
{label:'Из одной точки можно провести 3 касательных',cat:'false'},
{label:'△OAT₁ = △OAT₂',cat:'true'},
];
const pool=document.getElementById('p3-dnd-pool');
const boxes={true:document.getElementById('p3-drop-true-items'),false:document.getElementById('p3-drop-false-items')};
const fb=document.getElementById('p3-dnd-fb');
let placed={};
function reset(){
pool.innerHTML='';placed={};
Object.values(boxes).forEach(b=>b.innerHTML='');
fb.style.display='none';
items.forEach((it,i)=>{
const chip=document.createElement('div');
chip.className='dnd-chip';chip.dataset.i=i;chip.textContent=it.label;
chip.addEventListener('click',()=>chip.classList.toggle('armed'));
Object.entries(boxes).forEach(([cat,box])=>{
box.parentElement.addEventListener('click',()=>{
if(!chip.classList.contains('armed'))return;
chip.classList.remove('armed');
if(placed[i]!==undefined) boxes[placed[i]].removeChild(chip);
placed[i]=cat;
box.appendChild(chip);
});
});
pool.appendChild(chip);
});
}
document.getElementById('p3-dnd-check').addEventListener('click',()=>{
let ok=0;
items.forEach((it,i)=>{if(placed[i]===it.cat)ok++;});
feedback(fb,ok===items.length,'Верно: '+ok+'/'+items.length+(ok===items.length?'. Отлично!':'. Попробуй ещё.'));
if(ok===items.length){addXp(8,'p3-dnd');bumpProgress('p3',15);}
});
document.getElementById('p3-dnd-reset').addEventListener('click',reset);
reset();
})();
/* === INIT ИНТЕРАКТИВ 6: Босс §3 === */
(function(){
const tasks=[
{q:'Из точки $A$ проведены касательные к окружности с центром $O$. $\\angle T_1AT_2 = 60°$, $AT_1 = 6\\sqrt{3}$. Найти $R$.',
opts:['6','3√3','6√3'],cor:0,
exp:'$\\angle T_1AO = 30°$ (биссектриса), $\\tan 30° = R / AT_1$, значит $R = AT_1 \\cdot \\tan 30° = 6\\sqrt{3} \\cdot \\dfrac{1}{\\sqrt{3}} = 6$.'},
{q:'Две касательные из $A$, $|OA| = 10$, $R = 8$. Найти периметр четырёхугольника $OT_1AT_2$.',
opts:['12+16=28','6+12+6+8=32','6·4=24'],cor:0,
exp:'$AT_1 = AT_2 = \\sqrt{100-64} = 6$. $OT_1 = OT_2 = 8$. Периметр $= 6+8+6+8 = 28$.'},
{q:'Из точки $P$ проведены касательные, угол между касательными $90°$. $|OP| = 5\\sqrt{2}$. Найти $R$.',
opts:['5','5√2','10'],cor:0,
exp:'$\\angle T_1PO = 45°$ (половина 90°). $\\sin 45° = R/|OP|$, значит $R = 5\\sqrt{2} \\cdot \\dfrac{\\sqrt{2}}{2} = 5$.'},
{q:'Два отрезка касательных из одной точки равны по теореме, доказанной через:',
opts:['Равенство прямоугольных треугольников (гипотенуза и катет)','Теорему Пифагора','Теорему Фалеса'],cor:0,
exp:'Доказывается через признак равенства прямоугольных треугольников: гипотенуза $OA$ общая, катет $OT_1 = OT_2 = R$.'},
];
const cont=document.getElementById('p3-boss-tasks');
let html='';
tasks.forEach((t,i)=>{
html+=`<div style="margin-bottom:14px;padding:14px;background:var(--card);border:1px solid var(--border);border-radius:10px">
<div style="font-weight:600;margin-bottom:8px;font-size:.94rem">${i+1}. ${t.q}</div>
<div style="display:flex;flex-direction:column;gap:6px">
${t.opts.map((o,j)=>`<button class="btn" data-boss3="p3-${i}" data-j="${j}" onclick="(function(btn){
const btns=document.querySelectorAll('[data-boss3=\\'p3-${i}\\']');
btns.forEach(b=>b.disabled=true);
const ok=${j}===${t.cor};
btn.style.background=ok?'#d1fae5':'#fee2e2';
btn.style.borderColor=ok?'#10b981':'#ef4444';
btn.style.color=ok?'#065f46':'#7f1d1d';
const exp=document.getElementById('p3-boss-exp-${i}');
exp.style.display='block';exp.innerHTML='<b>'+(ok?'Верно!':'Неверно.')+'</b> ${t.exp.replace(/\\/g,'\\\\').replace(/'/g,"\\'")}';
if(window.renderMathInElement) try{renderMath(exp);}catch(e){}
if(ok){addXp(5,'p3-boss-${i}');bumpProgress('p3',5);}
})(this)" style="text-align:left">${o}</button>`).join('')}
</div>
<div id="p3-boss-exp-${i}" style="display:none;margin-top:8px;padding:8px 12px;border-radius:8px;font-size:.88rem;background:var(--sec-acc-soft,var(--pri-soft));color:var(--sec-acc-d,var(--pri2));line-height:1.55"></div>
</div>`;
});
cont.innerHTML=html;
if(window.renderMathInElement) try{renderMath(cont);}catch(e){}
})();
}
function buildP4stub(){ document.getElementById('p4-body').innerHTML='<div class="card"><div class="card-body"><p><b>§4 — Волна 1</b>: содержимое появится в следующем обновлении.</p></div></div>'+secNav('p3','p5'); }
function buildP5stub(){ document.getElementById('p5-body').innerHTML='<div class="card"><div class="card-body"><p><b>§5 — Волна 1</b>: содержимое появится в следующем обновлении.</p></div></div>'+secNav('p4','p6'); }
function buildP6stub(){ document.getElementById('p6-body').innerHTML='<div class="card"><div class="card-body"><p><b>§6 — Волна 1</b>: содержимое появится в следующем обновлении.</p></div></div>'+secNav('p5','p7'); }
function buildP7stub(){ document.getElementById('p7-body').innerHTML='<div class="card"><div class="card-body"><p><b>§7 — Волна 1</b>: содержимое появится в следующем обновлении.</p></div></div>'+secNav('p6','p8'); }
function buildP8stub(){ document.getElementById('p8-body').innerHTML='<div class="card"><div class="card-body"><p><b>§8 — Волна 1</b>: содержимое появится в следующем обновлении.</p></div></div>'+secNav('p7','p9'); }
function buildP9stub(){ document.getElementById('p9-body').innerHTML='<div class="card"><div class="card-body"><p><b>§9 — Волна 1</b>: содержимое появится в следующем обновлении.</p></div></div>'+secNav('p8','p10'); }
function buildP10stub(){ document.getElementById('p10-body').innerHTML='<div class="card"><div class="card-body"><p><b>§10 — Волна 1</b>: содержимое появится в следующем обновлении.</p></div></div>'+secNav('p9','p11'); }
function buildP11stub(){ document.getElementById('p11-body').innerHTML='<div class="card"><div class="card-body"><p><b>§11 — Волна 1</b>: содержимое появится в следующем обновлении.</p></div></div>'+secNav('p10','p12'); }
function buildP12stub(){ document.getElementById('p12-body').innerHTML='<div class="card"><div class="card-body"><p><b>§12 — Волна 1</b>: содержимое появится в следующем обновлении.</p></div></div>'+secNav('p11','p13'); }
function buildP13stub(){ document.getElementById('p13-body').innerHTML='<div class="card"><div class="card-body"><p><b>§13 — Волна 1</b>: содержимое появится в следующем обновлении.</p></div></div>'+secNav('p12','p14'); }
function buildP14stub(){ document.getElementById('p14-body').innerHTML='<div class="card"><div class="card-body"><p><b>§14 — Волна 1</b>: содержимое появится в следующем обновлении.</p></div></div>'+secNav('p13','p15'); }
function buildP15stub(){ document.getElementById('p15-body').innerHTML='<div class="card"><div class="card-body"><p><b>§15 — Волна 1</b>: содержимое появится в следующем обновлении.</p></div></div>'+secNav('p14','p16'); }
function buildP16stub(){ document.getElementById('p16-body').innerHTML='<div class="card"><div class="card-body"><p><b>§16 — Волна 1</b>: содержимое появится в следующем обновлении.</p></div></div>'+secNav('p15','final4'); }
function buildFinal4stub(){ document.getElementById('final4-body').innerHTML='<div class="card"><div class="card-body"><p><b>Финал главы 4 — Волна 1</b>: боссы и итоги появятся в следующем обновлении.</p></div></div>'+secNav('p16',null); }
</script>
</body>
</html>