Files
Learn_System/frontend/textbooks/algebra_11_ch3.html
T
Maxim Dolgolyov 8786cf5e20 fix(textbooks): убраны лишние слэши в LaTeX-формулах (over-escaping)
Формулы в JS-литералах имели \\\\dfrac / \\\\\\\\dfrac (4/8 слэшей) вместо
\\dfrac (2). После JS-анескейпа KaTeX получал \\dfrac, трактовал \\ как
перенос строки и печатал dfrac/cdot/sqrt/pi как текст (карточка пирамиды и
конуса в geometry_11_ch2, и др.).

Схлопнуты прогоны слэшей кратные 4 перед LaTeX-командой -> 2. Прогоны из
3 слэшей (\\ перенос строки + \cmd в \begin{cases}) и перед x/цифрой не
тронуты. 150 правок в 7 файлах (algebra_11_ch1/ch2/ch3, geometry_11_ch1..ch4).

БД чиста: questions (1398) text/explanation/correct_text + options (5187) -
0 багов. Скрипт: backend/scripts/fix_overescaped_latex.js (идемпотентный,
dry-run по умолчанию, --apply, с KaTeX-валидацией).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 11:53:17 +03:00

2862 lines
186 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>Алгебра 11 · Глава 3 · «Логарифмическая функция»</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<script defer src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/contrib/auto-render.min.js"
onload="renderMathInElement(document.body,{delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false})"></script>
<script src="/js/api.js" defer></script>
<script src="/js/xp.js" defer></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&family=Manrope:wght@600;700;800;900&family=Unbounded:wght@700;800;900&family=JetBrains+Mono:wght@500;700&display=swap" rel="stylesheet">
<style>
:root{
--bg:#fafafa; --card:#fff; --card-soft:#f8fafc; --text:#0f172a; --ink:#0f172a; --muted:#64748b;
--border:#e2e8f0; --sh:0 1px 3px rgba(0,0,0,.06); --sh2:0 4px 14px rgba(0,0,0,.08);
--pri:#0891b2; --pri2:#0e7490; --pri-soft:#cffafe;
--acc:#06b6d4; --acc2:#0891b2; --acc-soft:#ecfeff;
--ok:#10b981; --ok-bg:#d1fae5; --warn:#f59e0b; --warn-bg:#fef3c7;
--bad:#ef4444; --fail:#dc2626; --fail-bg:#fee2e2;
}
.dark{--bg:#040e12; --card:#0a1c22; --card-soft:#0e242c; --text:#ecfeff; --ink:#ecfeff; --muted:#84cfdc; --border:#0e3743}
*{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,.22);min-height:130px}
.hdr::before{content:'ГЛАВА 3';position:absolute;right:-12px;top:50%;transform:translateY(-50%);font-family:'Unbounded',sans-serif;font-size:clamp(5rem,15vw,11rem);font-weight:900;letter-spacing:-.04em;color:transparent;-webkit-text-stroke:1.5px rgba(255,255,255,.12);line-height:1;pointer-events:none;user-select:none;z-index:0}
.hdr-row{position:relative;z-index:1;display:flex;align-items:center;gap:14px;flex-wrap:wrap}
.hdr h1{font-family:'Unbounded',sans-serif;font-size:1.5rem;font-weight:900;letter-spacing:-.01em;line-height:1.3;padding-top:4px}
.hdr-sub{font-size:.85rem;opacity:.88;margin-top:6px;font-weight:500;line-height:1.4}
.hdr-side{margin-left:auto;display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.hdr-btn{padding:7px 12px;border-radius:9px;background:rgba(255,255,255,.14);color:#fff;font-weight:600;font-size:.82rem;display:inline-flex;align-items:center;gap:6px;transition:background .15s;text-decoration:none}
.hdr-btn:hover{background:rgba(255,255,255,.24)}
.main{max-width:1240px;margin:0 auto;padding:22px;width:100%;display:grid;grid-template-columns:1fr 280px;gap:24px}
@media(max-width:980px){.main{grid-template-columns:1fr;padding:14px}}
.col-main{min-width:0}
.hero{background:linear-gradient(135deg,var(--pri-soft) 0%,var(--acc-soft) 50%,var(--pri-soft) 100%);background-size:200% 200%;animation:heroShift 12s ease-in-out infinite;border:1px solid var(--border);border-radius:18px;padding:24px 22px;margin-bottom:24px;position:relative;overflow:hidden}
@keyframes heroShift{0%,100%{background-position:0% 50%}50%{background-position:100% 50%}}
.hero::before{content:'log';position:absolute;right:0;top:-30px;font-size:clamp(2rem,12vw,8rem);font-weight:900;color:var(--pri);opacity:.10;line-height:1;pointer-events:none;font-family:'Unbounded',sans-serif}
.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(0,0,0,.18)}
.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(0,0,0,.12);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(0,0,0,.18);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(180px,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:.86rem;font-weight:700;color:var(--text);line-height:1.3;margin-bottom:8px}
.psel-prog{height:4px;background:rgba(0,0,0,.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,var(--acc-soft),var(--pri-soft))}
.psel-card.final .psel-num{color:var(--warn)}
.sec[id="sec-p7"]{ --sec-acc:#d97706; --sec-acc-d:#b45309; --sec-acc-soft:#fef3c7; }
.sec[id="sec-p8"]{ --sec-acc:#f59e0b; --sec-acc-d:#d97706; --sec-acc-soft:#fef9c3; }
.sec[id="sec-p9"]{ --sec-acc:#059669; --sec-acc-d:#047857; --sec-acc-soft:#d1fae5; }
.sec[id="sec-p10"]{ --sec-acc:#7c3aed; --sec-acc-d:#6d28d9; --sec-acc-soft:#ede9fe; }
.sec[id="sec-final3"]{ --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.6rem;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(0,0,0,.04);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(0,0,0,.08)}
.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 .ic{width:18px;height:18px}
.card-title{font-family:'Unbounded',sans-serif;font-size:.82rem;font-weight:800;text-transform:uppercase;letter-spacing:.06em;color:var(--muted);flex:1}
.card-num{font-size:.74rem;font-weight:700;color:var(--muted);background:var(--sec-acc-soft,var(--pri-soft));padding:3px 7px;border-radius:5px}
.card-body{font-size:.94rem;line-height:1.65}
.card-body p{margin-bottom:8px}
.card-body p:last-child{margin-bottom:0}
.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))}
.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)}
.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}
.tinp{padding:8px 12px;border:1.5px solid var(--border);border-radius:8px;background:var(--card);color:var(--text);transition:border-color .15s;font-family:'JetBrains Mono',monospace}
.tinp:focus{outline:0;border-color:var(--sec-acc,var(--pri));box-shadow:0 0 0 3px var(--sec-acc-soft,var(--pri-soft))}
.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}
.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}
.dnd-pool{display:flex;flex-wrap:wrap;gap:8px;margin-bottom:14px;padding:10px;border:1.5px dashed var(--border);border-radius:10px;min-height:54px;transition:border-color .18s,background .18s}
.dnd-pool.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid}
.dnd-pool.col{flex-direction:column;align-items:stretch}
.dnd-pool.col .dnd-chip{width:auto}
.dnd-chip{display:inline-flex;align-items:center;gap:6px;padding:6px 12px;background:var(--card);border:1.5px solid var(--border);border-radius:10px;cursor:grab;user-select:none;font-size:.92rem;line-height:1.4;transition:transform .12s,box-shadow .12s,border-color .12s;touch-action:none;max-width:100%}
.dnd-chip:hover{transform:translateY(-1px);border-color:var(--sec-acc,var(--pri));box-shadow:var(--sh)}
.dnd-chip:active{cursor:grabbing}
.dnd-chip.armed{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));box-shadow:0 0 0 3px rgba(217,119,6,.22);transform:translateY(-1px)}
.dnd-chip.dragging{opacity:.28}
.dnd-chip.placed{background:var(--sec-acc-soft,var(--pri-soft));border-color:var(--sec-acc,var(--pri))}
.dnd-chip .dnd-x{padding:0 5px;color:var(--muted);font-weight:700;font-size:1.05rem;border-radius:4px;cursor:pointer}
.dnd-chip .dnd-x:hover{color:var(--bad,var(--fail));background:var(--fail-bg)}
.drop-box{background:var(--card);border:1.5px dashed var(--border);border-radius:10px;padding:10px;min-height:90px;transition:border-color .15s,background .15s}
.drop-box:hover{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft))}
.drop-box h5{font-family:'Unbounded',sans-serif;font-size:.78rem;color:var(--sec-acc-d,var(--pri2));margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em}
.drop-box.over{border-color:var(--sec-acc,var(--pri));background:var(--sec-acc-soft,var(--pri-soft));border-style:solid;transform:scale(1.015)}
.drop-items{display:flex;flex-wrap:wrap;gap:6px;min-height:32px}
.dnd-hint{font-size:.82rem;color:var(--muted);margin-bottom:8px;display:flex;align-items:center;gap:6px}
.dnd-hint svg{width:14px;height:14px;flex-shrink:0}
.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(0,0,0,.10);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}
.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}
.ach-popup{position:fixed;top:80px;right:18px;background:linear-gradient(135deg,var(--pri),var(--acc));color:#fff;padding:12px 18px;border-radius:11px;font-weight:700;font-size:.9rem;box-shadow:0 8px 28px rgba(0,0,0,.32);z-index:1002;display:none;align-items:center;gap:8px;max-width:340px}
.ach-popup.show{display:flex}
.col-side-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.42);z-index:9990;display:none}
.col-side-backdrop.show{display:block}
@media(min-width:981px){#sidebar-btn{display:none}.col-side-backdrop.show{display:none}}
@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}
}
.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}
.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: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}
.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}
/* === ALG11 POLISH (visual + micro-interactions) === */
@keyframes wgFadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}
.sec.active .wg{animation:wgFadeIn .35s cubic-bezier(.16,1,.3,1) backwards}
.sec.active .wg:nth-of-type(1){animation-delay:.02s}
.sec.active .wg:nth-of-type(2){animation-delay:.08s}
.sec.active .wg:nth-of-type(3){animation-delay:.14s}
.sec.active .wg:nth-of-type(4){animation-delay:.20s}
.sec.active .wg:nth-of-type(5){animation-delay:.26s}
.sec.active .wg:nth-of-type(6){animation-delay:.32s}
.wg svg{transition:filter .25s ease}
.wg:hover svg{filter:drop-shadow(0 4px 16px rgba(0,0,0,.10))}
input[type=range]:active{box-shadow:0 0 0 4px var(--pri-soft);border-radius:8px}
.wg input[type=range]{cursor:ew-resize}
.score-display b{transition:transform .22s cubic-bezier(.16,1,.3,1),color .22s;display:inline-block;transform-origin:center}
.score-display b.bump{transform:scale(1.28);color:var(--pri)}
.katex{transition:color .2s}
.wg-help .katex:hover,.card-body .katex:hover{color:var(--pri2,var(--pri));cursor:help}
.hp-fill,.psel-prog-fill,.xp-fill,[id$="-overall-fill"]{transition:width .6s cubic-bezier(.16,1,.3,1)!important}
.boss-card,.btn.primary,.btn-primary{position:relative;overflow:hidden}
.btn.primary::after,.btn-primary::after{content:'';position:absolute;inset:0;background:radial-gradient(circle at center,rgba(255,255,255,.42) 0%,transparent 60%);opacity:0;transition:opacity .25s;pointer-events:none}
.btn.primary:hover::after,.btn-primary:hover::after{opacity:1}
.psel-card{position:relative}
.psel-card .psel-done{position:absolute;top:6px;right:6px;width:18px;height:18px;border-radius:50%;background:#10b981;display:none;align-items:center;justify-content:center;box-shadow:0 2px 6px rgba(16,185,129,.45);z-index:2}
.psel-card .psel-done svg{width:11px;height:11px;stroke:#fff;fill:none;stroke-width:3;stroke-linecap:round;stroke-linejoin:round}
.psel-card.done .psel-done{display:flex}
.boss-card{transition:border-color .35s,box-shadow .6s,background .3s,transform .2s}
.boss-card.glow{box-shadow:0 0 24px rgba(16,185,129,.6),0 0 0 2px rgba(16,185,129,.45)!important}
@keyframes bossPulse{0%{box-shadow:0 0 0 0 rgba(16,185,129,.55)}70%{box-shadow:0 0 0 14px rgba(16,185,129,0)}100%{box-shadow:0 0 0 0 rgba(16,185,129,0)}}
.boss-card.pulse{animation:bossPulse .8s ease-out}
.sec{transition:opacity .25s}
</style>
</head>
<body>
<header class="hdr">
<div class="hdr-row">
<div>
<h1>Алгебра 11 · Глава 3</h1>
<div class="hdr-sub">Свойства логарифмов · функция $y=\log_a x$ · уравнения и неравенства</div>
</div>
<div class="hdr-side">
<a href="/textbook/algebra-11" class="hdr-btn"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> К алгебре 11</a>
<button id="search-btn" class="hdr-btn"><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"><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"><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> $y = \log_a x$ — обратная к показательной. Решаем <b>уравнения и неравенства</b> с логарифмами, учитывая ОДЗ.</p>
<div class="hero-row">
<button class="btn-primary" onclick="goTo('p7')"><svg class="ic" viewBox="0 0 24 24"><polygon points="6 4 20 12 6 20 6 4" fill="currentColor" stroke="none"/></svg> Начать § 7</button>
<div class="hero-progress">
<span class="hp-label">Прогресс по главе</span>
<div class="hp-bar"><div id="hero-hp-fill" class="hp-fill"></div></div>
<span id="hero-hp-text" class="hp-text">0%</span>
</div>
<div id="hero-xp-badge" class="hero-xp-badge" data-gamified></div>
</div>
</section>
<section class="psel">
<div class="psel-title">Параграфы главы</div>
<div id="psel-grid" class="psel-grid"></div>
</section>
<section id="sec-p7" class="sec" data-watermark="log·log"><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="log_a x"><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="log="><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="log>"><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-final3" class="sec" data-watermark="&#9733;"><div class="sec-header"><span class="sec-num" style="background:linear-gradient(135deg,#0891b2,#06b6d4)">&#9733;</span><h2 class="sec-h">Финал главы</h2></div><div id="final3-body"></div></section>
</div>
<aside class="col-side" id="col-side"><div id="sidebar-content"></div></aside>
<div class="col-side-backdrop" id="col-side-backdrop"></div>
</main>
<footer class="foot">Интерактивный учебник «Алгебра 11» · Глава 3 · «Логарифмическая функция» · LearnSpace</footer>
<div id="ach-popup" class="ach-popup"><svg class="ic" viewBox="0 0 24 24" style="width:22px;height:22px"><polygon points="12,2 22,20 2,20"/></svg><span id="ach-text">Достижение!</span></div>
<div id="search-modal" class="search-modal" role="dialog">
<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>↑↓</kbd> навигация</span><span><kbd>Enter</kbd> открыть</span><span><kbd>Esc</kbd> закрыть</span></div>
</div>
</div>
<script>
'use strict';
const STATE = { current:'p7', progress:{}, achievements:new Map(), xp:0, level:1 };
const TOTAL_PARAS = 5;
const _TB_SLUG = 'algebra-11-ch3';
const PARAS = [
{ id:'p7', num:'§ 7', name:'Свойства логарифмов', sub:'$\\log_a(bc) = \\log_a b + \\log_a c$' },
{ id:'p8', num:'§ 8', name:'Логарифмическая функция', sub:'$y = \\log_a x$' },
{ id:'p9', num:'§ 9', name:'Логарифмические уравнения', sub:'$\\log_a f = \\log_a g$' },
{ id:'p10', num:'§ 10', name:'Логарифмические неравенства', sub:'$\\log_a f > b$' },
{ id:'final3', num:'&#9733;', name:'Финал главы', sub:'Итоги · боссы главы 3', final:true }
];
PARAS.forEach(p => { STATE.progress[p.id] = 0; });
function calcLevel(xp){ return Math.floor(Math.sqrt((xp||0)/100))+1; }
function _xpForLevel(lv){ return (lv-1)*(lv-1)*100; }
const ACH_LABELS = {
start:"Начало главы 3!",
p8_done:"Логарифмическая функция освоена!",
p9_done:"Логарифмические уравнения освоены!",
p10_done:"Логарифмические неравенства освоены!",
ch3_done:"Глава 3 пройдена!"
};
function loadProgress(){
try{
const s=localStorage.getItem('algebra11_ch3_progress'); if(s) Object.assign(STATE.progress, JSON.parse(s));
const a=localStorage.getItem('algebra11_ch3_achievements');
if(a){ const p=JSON.parse(a); if(Array.isArray(p)) p.forEach(id=>STATE.achievements.set(id, ACH_LABELS[id]||id)); else if(p&&typeof p==='object'){ for(const[id,t] of Object.entries(p)) STATE.achievements.set(id,(t&&t!==id)?t:(ACH_LABELS[id]||id)); } }
STATE.xp=+(localStorage.getItem('algebra11_xp')||0); STATE.level=calcLevel(STATE.xp);
}catch(e){}
}
function saveProgress(){
try{
localStorage.setItem('algebra11_ch3_progress', JSON.stringify(STATE.progress));
localStorage.setItem('algebra11_ch3_achievements', JSON.stringify(Object.fromEntries(STATE.achievements)));
localStorage.setItem('algebra11_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 _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,'algebra11-ch3-'+(src||'misc'));
if(STATE.level>prev){
const pop=document.getElementById('ach-popup');
if(pop){ document.getElementById('ach-text').textContent='Уровень '+STATE.level+'!'; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'),2600); }
}
}
function refreshProgressUI(){
const total=Math.round(Object.values(STATE.progress).reduce((a,b)=>a+b,0)/TOTAL_PARAS);
const f=document.getElementById('hero-hp-fill'); if(f) f.style.width=total+'%';
const t=document.getElementById('hero-hp-text'); if(t) t.textContent=total+'% пройдено';
document.querySelectorAll('[data-prog-card]').forEach(el=>{ const k=el.dataset.progCard; const fl=el.querySelector('.psel-prog-fill'); if(fl) fl.style.width=(STATE.progress[k]||0)+'%'; });
const xpBadge=document.getElementById('hero-xp-badge');
if(xpBadge){ xpBadge.innerHTML='<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:13px;height:13px"><polygon points="12 2 22 20 2 20"/></svg> Ур. '+STATE.level+' \xb7 '+(STATE.xp||0)+' XP'; }
if(STATE.current && document.getElementById('sidebar-content')){ try{ buildSidebar(STATE.current); }catch(e){} }
}
function achievement(id,text){
if(STATE.achievements.has(id)) return;
STATE.achievements.set(id, text||ACH_LABELS[id]||id); saveProgress();
const pop=document.getElementById('ach-popup');
if(pop){ document.getElementById('ach-text').textContent=text||ACH_LABELS[id]||id; pop.classList.add('show'); setTimeout(()=>pop.classList.remove('show'),3300); }
addXp(20,'ach-'+id);
}
function buildParaSelector(){
const g=document.getElementById('psel-grid'); g.innerHTML='';
PARAS.forEach(p=>{
const card=document.createElement('div');
card.className='psel-card'+(p.final?' final':'');
card.dataset.id=p.id; card.dataset.progCard=p.id;
card.innerHTML='<div class="psel-num">'+p.num+'</div><div class="psel-name">'+p.name+'</div><div class="psel-prog"><div class="psel-prog-fill"></div></div>';
card.addEventListener('click', ()=>goTo(p.id));
g.appendChild(card);
});
}
const BUILT=new Set();
const BUILDERS = { p7:()=>buildP7(), p8:()=>buildP8(), p9:()=>buildP9(), p10:()=>buildP10(), final3:()=>buildFinal3() };
function ensureBuilt(id){ if(BUILT.has(id)) return; const fn=BUILDERS[id]; if(fn){ fn(); BUILT.add(id); } }
function goTo(id){
STATE.current=id; ensureBuilt(id);
document.querySelectorAll('.sec').forEach(s=>s.classList.remove('active'));
const el=document.getElementById('sec-'+id); if(el) el.classList.add('active');
document.querySelectorAll('.psel-card').forEach(c=>c.classList.toggle('active', c.dataset.id===id));
buildSidebar(id);
window.scrollTo({top:0,behavior:'smooth'});
if((STATE.progress[id]||0)<10) bumpProgress(id, 10);
if(window.renderMathInElement) setTimeout(()=>renderMath(el), 0);
markLastPara(id);
}
const SIDEBARS = {
p7:{title:"Шпаргалка §7",rows:[["Произв.","$\\log_a(bc) = \\log_a b + \\log_a c$"],["Част.","$\\log_a(b/c) = \\log_a b - \\log_a c$"],["Степ.","$\\log_a b^k = k \\log_a b$"],["Перех.","$\\log_a b = \\dfrac{\\log_c b}{\\log_c a}$"]]},
p8:{title:"Шпаргалка §8",rows:[["Вид","$y = \\log_a x$, $a > 0$, $a \\ne 1$"],["D(f)","$x > 0$"],["E(f)","$y \\in \\mathbb{R}$"],["$a > 1$","возрастает"],["$0 < a < 1$","убывает"],["Точка","проходит через $(1; 0)$"]]},
p9:{title:"Шпаргалка §9",rows:[["Базовый","$\\log_a f = \\log_a g \\iff f = g$, $f, g > 0$"],["ОДЗ","всё под $\\log > 0$, основание $a > 0$, $a \\ne 1$"],["Замена","$t = \\log_a x$"]]},
p10:{title:"Шпаргалка §10",rows:[["$a > 1$","$\\log_a f > \\log_a g \\iff f > g > 0$"],["$0 < a < 1$","$\\log_a f > \\log_a g \\iff 0 < f < g$ (знак меняется!)"],["ОДЗ","обязательно учесть"]]},
final3:{title:"Финал главы 3",rows:[["§§710","теория главы 3"],["Награда","+50 XP"]]}
};
const TIPS=[
{sec:'p7',html:"$\\log_a(bc) = \\log_a b + \\log_a c$ — произведение в логарифме превращается в сумму."},
{sec:'p8',html:"График $y = \\log_a x$ — зеркальное отражение $y = a^x$ относительно $y = x$."},
{sec:'p9',html:"<b>Всегда</b> выписывай ОДЗ: всё под $\\log$ должно быть строго положительно."},
{sec:'p10',html:"При $0 < a < 1$ — знак неравенства меняется. ОДЗ обязательно."},
{sec:'final3',html:"Финал главы 3 — синтез логарифмов и логарифмических функций."}
];
function buildSidebar(id){
const box=document.getElementById('sidebar-content');
const sb=SIDEBARS[id]||SIDEBARS[PARAS[0].id];
let html='';
const xpForLv=_xpForLevel(STATE.level), xpNext=_xpForLevel(STATE.level+1);
const xpInLv=STATE.xp-xpForLv, xpRange=xpNext-xpForLv;
const xpPct=xpRange>0?Math.round(xpInLv/xpRange*100):100;
html+='<div class="xp-card" data-gamified><div class="xp-card-title" data-gamified><span>XP-прогресс</span><span class="xp-level">Ур. '+STATE.level+'</span></div><div class="xp-bar"><div class="xp-fill" style="width:'+xpPct+'%"></div></div><div class="xp-nums"><span>'+STATE.xp+' XP</span><span>'+xpNext+' XP</span></div></div>';
html+='<div class="sidecard"><h4>'+sb.title+'</h4>';
sb.rows.forEach(([k,v])=>{ html+='<div class="sidecard-row"><b>'+k+'</b>'+(v?' \u2014 '+v:'')+'</div>'; });
html+='</div>';
const tip=TIPS.find(t=>t.sec===id)||TIPS[0];
if(tip){
html+='<div class="sidecard" style="background:linear-gradient(135deg,var(--warn-bg,#fef3c7),var(--pri-soft));border-color:var(--warn,#f59e0b)"><h4 style="color:#92400e;display:flex;align-items:center;gap:6px"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" style="width:14px;height:14px"><polygon points="12,2 22,20 2,20"/></svg>Подсказка</h4><div class="sidecard-row" style="margin-bottom:0;font-size:.84rem;line-height:1.55">'+tip.html+'</div></div>';
}
if(STATE.achievements.size>0){
html+='<div class="sidecard"><h4>Достижения <span style="color:var(--warn);float:right">'+STATE.achievements.size+'</span></h4>';
[...STATE.achievements.values()].slice(-4).forEach(text=>{ html+='<div class="sidecard-row" style="font-size:.78rem;color:var(--ok)">&#10003; '+text+'</div>'; });
html+='</div>';
}
box.innerHTML=html;
if(window.renderMathInElement) try{ renderMath(box); }catch(e){}
}
function initTheme(){
const t=localStorage.getItem('algebra11_ch3_theme')||'light';
if(t==='dark') document.documentElement.classList.add('dark');
document.getElementById('theme-lab').textContent=t==='dark'?'Светлая':'Тёмная';
document.getElementById('theme-btn').addEventListener('click', ()=>{
document.documentElement.classList.toggle('dark');
const dark=document.documentElement.classList.contains('dark');
localStorage.setItem('algebra11_ch3_theme', dark?'dark':'light');
document.getElementById('theme-lab').textContent=dark?'Светлая':'Тёмная';
});
}
function renderMath(root){ if(window.renderMathInElement){ try{ renderMathInElement(root, {delimiters:[{left:'$$',right:'$$',display:true},{left:'$',right:'$',display:false},{left:'\\[',right:'\\]',display:true},{left:'\\(',right:'\\)',display:false}],throwOnError:false}); }catch(e){} } }
function feedback(elm, ok, text){ if(!elm) return; elm.className='feedback '+(ok?'ok':'fail'); elm.innerHTML=text||(ok?'&#10003; Верно!':'&#10007; Неверно'); elm.style.display='block'; try{renderMath(elm);}catch(e){} }
function fmt(n){ if(!isFinite(n)) return '?'; if(Number.isInteger(n)) return String(n); return Math.abs(n-Math.round(n))<1e-9?String(Math.round(n)):(+n.toFixed(6)).toString(); }
function ipow(base, exp){ let r=1; for(let i=0;i<Math.abs(exp);i++) r*=base; return exp<0 ? 1/r : r; }
function gcd(a,b){ a=Math.abs(a|0); b=Math.abs(b|0); while(b){ const t=b; b=a%b; a=t; } return a||1; }
function makeCard(kind, title, num, body){
const labels = {repeat:'Повторение',theory:'Теория',algo:'Алгоритм',rule:'Правило',example:'Пример',oral:'Устно'};
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 setupSorter(cfg){
const placed = {}; const pool = document.getElementById(cfg.poolId); const scope = document.querySelector(cfg.scopeSelector);
if(!pool||!scope) return {placed,render:()=>{},reset:()=>{}};
pool.classList.add('dnd-pool'); if(cfg.columnLayout) pool.classList.add('col');
let armed = null;
function buildChip(it,isPlaced){ const e=document.createElement('div'); e.className='dnd-chip'+(isPlaced?' placed':''); e.dataset.id=it.id; e.innerHTML='<span class="dnd-txt">'+it.html+'</span><span class="dnd-x" title="Убрать">\xd7</span>'; attach(e,it.id); return e; }
function attach(elm,itId){ let ghost=null,dragging=false,sx=0,sy=0; elm.addEventListener('pointerdown',ev=>{ if(ev.button!==undefined&&ev.button!==0) return;
ev.preventDefault(); if(ev.target.classList&&ev.target.classList.contains('dnd-x')){ ev.stopPropagation(); if(placed[itId]){delete placed[itId];render();}else if(armed===itId){armed=null;render();} return; } sx=ev.clientX;sy=ev.clientY; const r=elm.getBoundingClientRect(); const ox=ev.clientX-r.left,oy=ev.clientY-r.top; try{elm.setPointerCapture(ev.pointerId);}catch(e){} function onMove(e){ const dx=e.clientX-sx,dy=e.clientY-sy; if(!dragging&&Math.hypot(dx,dy)>8){ dragging=true; ghost=elm.cloneNode(true); ghost.classList.remove('armed'); ghost.style.cssText='position:fixed;z-index:9999;pointer-events:none;opacity:.9;transform:rotate(-2.5deg);box-shadow:0 14px 36px rgba(0,0,0,.32);width:'+r.width+'px;left:'+(e.clientX-ox)+'px;top:'+(e.clientY-oy)+'px'; document.body.appendChild(ghost); elm.classList.add('dragging'); } if(dragging&&ghost){ ghost.style.left=(e.clientX-ox)+'px';ghost.style.top=(e.clientY-oy)+'px'; const under=document.elementsFromPoint(e.clientX,e.clientY); scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); const tgt=under.find(n=>n.classList&&(n.classList.contains('drop-box')||n.classList.contains('dnd-pool'))); if(tgt)tgt.classList.add('over'); } } function onUp(e){ elm.removeEventListener('pointermove',onMove);elm.removeEventListener('pointerup',onUp);elm.removeEventListener('pointercancel',onUp);elm.classList.remove('dragging'); if(ghost){ghost.remove();ghost=null;} scope.querySelectorAll('.drop-box.over,.dnd-pool.over').forEach(n=>n.classList.remove('over')); if(dragging){ const under=document.elementsFromPoint(e.clientX,e.clientY); const box=under.find(n=>n.classList&&n.classList.contains('drop-box')); const pl=under.find(n=>n.classList&&n.classList.contains('dnd-pool')); if(box){const di=box.querySelector('[data-cat]');if(di){placed[itId]=di.dataset.cat;armed=null;render();return;}}else if(pl){delete placed[itId];armed=null;render();return;} }else{ if(placed[itId]){delete placed[itId];armed=null;render();}else{armed=(armed===itId)?null:itId;render();} } dragging=false; } elm.addEventListener('pointermove',onMove);elm.addEventListener('pointerup',onUp);elm.addEventListener('pointercancel',onUp); }); }
function attachBoxTaps(){ scope.querySelectorAll('.drop-box').forEach(box=>{ box.addEventListener('click',ev=>{ if(!armed)return; if(ev.target.closest('.dnd-chip'))return; const di=box.querySelector('[data-cat]'); if(di){placed[armed]=di.dataset.cat;armed=null;render();} }); }); }
function render(){ pool.innerHTML=''; cfg.items.forEach(it=>{if(placed[it.id])return;const c=buildChip(it,false);if(armed===it.id)c.classList.add('armed');pool.appendChild(c);}); cfg.cats.forEach(cat=>{const box=scope.querySelector('.drop-items[data-cat="'+cat+'"]');if(!box)return;box.innerHTML='';cfg.items.forEach(it=>{if(placed[it.id]!==cat)return;box.appendChild(buildChip(it,true));});}); if(window.renderMathInElement)try{renderMath(scope);}catch(_){} }
attachBoxTaps(); render();
return {placed,render,reset(){ for(const k in placed)delete placed[k];armed=null;render(); }};
}
/* === SVG-хелперы (axes2D, plotFunc, pointWithDrop, asymptote, snapToValue, геом.) === */
/* Координатная плоскость в SVG */
function axes2D(W, H, pad, xmin, xmax, ymin, ymax){
const ux = (W - 2*pad) / (xmax - xmin);
const uy = (H - 2*pad) / (ymax - ymin);
const toX = v => pad + (v - xmin) * ux;
const toY = v => H - pad - (v - ymin) * uy;
let g = '';
g += '<g stroke="#e5e7eb" stroke-width="1">';
for (let x = Math.ceil(xmin); x <= xmax; x++){
g += '<line x1="'+toX(x)+'" y1="'+pad+'" x2="'+toX(x)+'" y2="'+(H-pad)+'"/>';
}
for (let y = Math.ceil(ymin); y <= ymax; y++){
g += '<line x1="'+pad+'" y1="'+toY(y)+'" x2="'+(W-pad)+'" y2="'+toY(y)+'"/>';
}
g += '</g>';
const y0 = toY(0), x0 = toX(0);
g += '<line x1="'+pad+'" y1="'+y0+'" x2="'+(W-pad)+'" y2="'+y0+'" stroke="#0f172a" stroke-width="1.5"/>';
g += '<line x1="'+x0+'" y1="'+pad+'" x2="'+x0+'" y2="'+(H-pad)+'" stroke="#0f172a" stroke-width="1.5"/>';
g += '<text x="'+(W-pad+2)+'" y="'+(y0-4)+'" font-size="11" fill="#0f172a">x</text>';
g += '<text x="'+(x0+4)+'" y="'+(pad-2)+'" font-size="11" fill="#0f172a">y</text>';
g += '<g font-size="10" fill="#64748b">';
for (let x = Math.ceil(xmin); x <= xmax; x++){
if (x !== 0) g += '<text x="'+(toX(x)-3)+'" y="'+(y0+12)+'">'+x+'</text>';
}
for (let y = Math.ceil(ymin); y <= ymax; y++){
if (y !== 0) g += '<text x="'+(x0+4)+'" y="'+(toY(y)+3)+'">'+y+'</text>';
}
g += '<text x="'+(x0+4)+'" y="'+(y0+12)+'">0</text>';
g += '</g>';
return { content: g, toX, toY, ux, uy };
}
/* График функции y=f(x) — строка <path ...> */
function plotFunc(f, xmin, xmax, toX, toY, color, N){
N = N || 200;
let d = '';
let prevValid = false;
for (let i = 0; i <= N; i++){
const x = xmin + (xmax - xmin) * i / N;
let y;
try { y = f(x); } catch(e){ y = NaN; }
if (!isFinite(y) || isNaN(y) || y < -1e4 || y > 1e4){ prevValid = false; continue; }
d += (prevValid ? ' L' : ' M') + toX(x).toFixed(2) + ',' + toY(y).toFixed(2);
prevValid = true;
}
return '<path d="'+d+'" stroke="'+color+'" stroke-width="2" fill="none" stroke-linecap="round" stroke-linejoin="round"/>';
}
/* Точка на графике с пунктирными проекциями на оси и подписями координат */
function pointWithDrop(x, fx, toX, toY, color, label){
const px = toX(x), py = toY(fx);
let s = '';
s += '<line x1="'+px+'" y1="'+py+'" x2="'+px+'" y2="'+toY(0)+'" stroke="'+color+'" stroke-width="1.2" stroke-dasharray="3 3" opacity=".7"/>';
s += '<line x1="'+px+'" y1="'+py+'" x2="'+toX(0)+'" y2="'+py+'" stroke="'+color+'" stroke-width="1.2" stroke-dasharray="3 3" opacity=".7"/>';
s += '<circle cx="'+px+'" cy="'+py+'" r="4.5" fill="'+color+'" stroke="#fff" stroke-width="2"/>';
if (label){
s += '<text x="'+(px+8)+'" y="'+(py-8)+'" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="'+color+'">'+label+'</text>';
}
return s;
}
/* Горизонтальная или вертикальная асимптота */
function asymptote(orientation, value, toX, toY, xmin, xmax, ymin, ymax, color){
color = color || '#94a3b8';
if (orientation === 'h'){
const y = toY(value);
return '<line x1="'+toX(xmin)+'" y1="'+y+'" x2="'+toX(xmax)+'" y2="'+y+'" stroke="'+color+'" stroke-width="1.3" stroke-dasharray="6 4"/>';
} else {
const x = toX(value);
return '<line x1="'+x+'" y1="'+toY(ymin)+'" x2="'+x+'" y2="'+toY(ymax)+'" stroke="'+color+'" stroke-width="1.3" stroke-dasharray="6 4"/>';
}
}
/* Snap-функция для slider'ов: возвращает ближайшую snap-точку, если значение близко */
function snapToValue(value, snapPoints, tolerance){
tolerance = tolerance || 0.1;
for (const sp of snapPoints){
if (Math.abs(value - sp) < tolerance) return sp;
}
return value;
}
/* Геометрические SVG-хелперы (для будущих интерактивов с углами) */
function rightAngleMark(V, uIn, wIn, s){
s = s || 9;
const p1 = {x: V.x + s*uIn.x, y: V.y + s*uIn.y};
const c = {x: p1.x + s*wIn.x, y: p1.y + s*wIn.y};
const p2 = {x: V.x + s*wIn.x, y: V.y + s*wIn.y};
return p1.x+','+p1.y+' '+c.x+','+c.y+' '+p2.x+','+p2.y;
}
function angleArcAuto(V, uA, uB, R){
const sA = {x: V.x + R*uA.x, y: V.y + R*uA.y};
const eB = {x: V.x + R*uB.x, y: V.y + R*uB.y};
const cross = uA.x*uB.y - uA.y*uB.x;
const sweep = cross > 0 ? 1 : 0;
return 'M'+sA.x+','+sA.y+' A'+R+','+R+' 0 0,'+sweep+' '+eB.x+','+eB.y;
}
function unitVec(p1, p2){
const dx = p2.x - p1.x, dy = p2.y - p1.y;
const len = Math.sqrt(dx*dx + dy*dy) || 1;
return {x: dx/len, y: dy/len};
}
function deg2rad(d){ return d * Math.PI / 180; }
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>',
};
function secNavFor(curId){
const idx = PARAS.findIndex(p => p.id === curId);
const prev = idx > 0 ? PARAS[idx-1].id : null;
const next = idx < PARAS.length - 1 ? PARAS[idx+1].id : null;
return secNav(prev, next);
}
function secNav(prev, next){
const NAMES = {p7:'\xA77',p8:'\xA78',p9:'\xA79',p10:'\xA710',final3:'Финал'};
let h='<div class="sec-nav">';
h+=prev?'<button class="btn" onclick="goTo(\''+prev+'\')"><svg class="ic" viewBox="0 0 24 24"><polyline points="15 18 9 12 15 6"/></svg> '+NAMES[prev]+'</button>':'<span></span>';
h+=next?'<button class="btn primary" onclick="goTo(\''+next+'\')">'+NAMES[next]+' <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></button>':'<span></span>';
h+='</div>'; return h;
}
function readButton(paraId){
const p = PARAS.find(x => x.id === paraId);
const labelTail = p && p.final ? 'финал' : (p ? p.num : '\xA7?');
return '<div style="margin-top:18px;display:flex;justify-content:center">'
+'<button class="btn primary" id="'+paraId+'-read-btn">'
+'<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>'
+' Я прочитал — '+labelTail+' (+10 XP)'
+'</button></div>';
}
function wireReadBtn(paraId){
const btn = document.getElementById(paraId+'-read-btn'); if(!btn) return;
btn.addEventListener('click', ()=>{
addXp(10, paraId+'-read'); bumpProgress(paraId, 30);
btn.textContent='Прочитано! +10 XP'; btn.disabled=true; btn.style.opacity=.6;
const aId = paraId+'_done';
if(ACH_LABELS[aId]) achievement(aId);
});
}
/* ===== STUB BUILDERS — наполнение в Phase 1+ ===== */
function buildP7(){
const box = document.getElementById('p7-body');
let html = '';
/* === ТЕОРИЯ === */
html += makeCard('theory', 'Произведение, частное, степень', '7.1', `
<p>Пусть $a > 0$, $a \\ne 1$ и $b > 0$, $c > 0$. Тогда справедливы три основных свойства:</p>
<p style="text-align:center;font-size:1.04rem">$\\log_a(bc) = \\log_a b + \\log_a c$</p>
<p style="text-align:center;font-size:1.04rem">$\\log_a \\dfrac{b}{c} = \\log_a b - \\log_a c$</p>
<p style="text-align:center;font-size:1.04rem">$\\log_a b^n = n \\cdot \\log_a b$</p>
<details class="spoiler"><summary>Доказательство свойства произведения</summary><div class="spoiler-body">
<p>Пусть $\\log_a b = p$ и $\\log_a c = q$. По определению логарифма $b = a^p$ и $c = a^q$.</p>
<p>Тогда $bc = a^p \\cdot a^q = a^{p+q}$, значит $\\log_a(bc) = p + q = \\log_a b + \\log_a c$.</p>
<p>Свойства для частного и степени доказываются аналогично — через определение логарифма и свойства степеней.</p>
</div></details>
<p style="margin-top:10px"><b>Примеры:</b></p>
<p>$\\log_2 6 = \\log_2(2 \\cdot 3) = \\log_2 2 + \\log_2 3 = 1 + \\log_2 3$.</p>
<p>$\\log_5 \\dfrac{25}{8} = \\log_5 25 - \\log_5 8 = 2 - 3 \\log_5 2$.</p>
<p>$\\log_2 8^3 = 3 \\log_2 8 = 3 \\cdot 3 = 9$.</p>
`);
html += makeCard('rule', 'Корень и перенос показателя из основания', '7.2', `
<p><b>Логарифм корня.</b> Поскольку $\\sqrt[n]{b} = b^{1/n}$, по свойству степени:</p>
<p style="text-align:center;font-size:1.04rem">$\\log_a \\sqrt[n]{b} = \\log_a b^{1/n} = \\dfrac{1}{n} \\log_a b$</p>
<p><b>Перенос показателя из основания.</b></p>
<p style="text-align:center;font-size:1.04rem">$\\log_{a^n} b = \\dfrac{1}{n} \\log_a b$</p>
<p>Эти два свойства часто объединяют в одно:</p>
<p style="text-align:center;font-size:1.06rem">$\\log_{a^k} b^m = \\dfrac{m}{k} \\log_a b$</p>
<p style="margin-top:10px"><b>Примеры:</b></p>
<p>$\\log_2 \\sqrt{8} = \\dfrac{1}{2} \\log_2 8 = \\dfrac{3}{2}$.</p>
<p>$\\log_4 16 = \\log_{2^2} 2^4 = \\dfrac{4}{2} \\log_2 2 = 2$.</p>
<p>$\\log_{27} \\sqrt[3]{9} = \\log_{3^3} 3^{2/3} = \\dfrac{2/3}{3} = \\dfrac{2}{9}$.</p>
`);
html += makeCard('example', 'Переход к новому основанию', '7.3', `
<p>Иногда удобно перейти от $\\log_a b$ к логарифму по другому основанию $c$ ($c > 0$, $c \\ne 1$):</p>
<p style="text-align:center;font-size:1.06rem">$\\log_a b = \\dfrac{\\log_c b}{\\log_c a}$</p>
<p>Чаще всего берут $c = 10$ (десятичные) или $c = e$ (натуральные). Полезное следствие при $a \\ne 1$, $b \\ne 1$:</p>
<p style="text-align:center;font-size:1.04rem">$\\log_a b = \\dfrac{1}{\\log_b a}$</p>
<p style="margin-top:10px"><b>Применение:</b></p>
<p>$\\log_2 7 = \\dfrac{\\lg 7}{\\lg 2} \\approx \\dfrac{0{,}845}{0{,}301} \\approx 2{,}807$.</p>
<p>$\\log_3 5 = \\dfrac{1}{\\log_5 3}$.</p>
<p><b>Связь $\\lg$ и $\\ln$:</b></p>
<p>$\\lg b = \\dfrac{\\ln b}{\\ln 10} \\approx \\dfrac{\\ln b}{2{,}303}$, $\\quad \\ln b = \\dfrac{\\lg b}{\\lg e} \\approx \\dfrac{\\lg b}{0{,}4343}$.</p>
`);
/* === ИНТЕРАКТИВ 1 — пошаговый «применитель» свойств === */
html += `<div class="wg" id="p7-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Применение свойств логарифма</div></div>
<div class="wg-help">Выбери выражение ползунком и нажимай «Следующий шаг ▶», чтобы открывать решение по одному шагу. Просмотри все 5 выражений — получишь XP.</div>
<div class="sliders">
<label>Выражение № <b id="p7-iv1-n">1</b> / 5<input type="range" id="p7-iv1-sn" min="1" max="5" step="1" value="1"></label>
</div>
<div id="p7-iv1-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.10rem;text-align:center;margin-bottom:10px"></div>
<div id="p7-iv1-steps" style="display:flex;flex-direction:column;gap:8px;margin-bottom:10px"></div>
<div class="actions" style="justify-content:center">
<button class="btn primary" id="p7-iv1-next">Следующий шаг ▶</button>
<button class="btn" id="p7-iv1-all">Показать все шаги</button>
<button class="btn" id="p7-iv1-reset">Скрыть шаги</button>
</div>
</div>`;
/* === ИНТЕРАКТИВ 2 — калькулятор логарифмов === */
html += `<div class="wg" id="p7-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор логарифмов</div></div>
<div class="wg-help">Выбери свойство, заполни поля и нажми «Вычислить» — увидишь применение свойства пошагово и численное приближение.</div>
<div style="display:flex;gap:6px;flex-wrap:wrap;justify-content:center;margin-bottom:12px">
<button class="btn" data-mode="prod" id="p7-iv2-m-prod">Произведение $\\log_a(bc)$</button>
<button class="btn" data-mode="quot" id="p7-iv2-m-quot">Частное $\\log_a(b/c)$</button>
<button class="btn" data-mode="pow" id="p7-iv2-m-pow">Степень $\\log_a b^n$</button>
<button class="btn" data-mode="base" id="p7-iv2-m-base">Переход к новому основанию</button>
</div>
<div id="p7-iv2-inputs" style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center;margin-bottom:10px"></div>
<div style="text-align:center;margin-bottom:10px"><button class="btn primary" id="p7-iv2-go">Вычислить</button></div>
<div id="p7-iv2-out" style="padding:12px 14px;background:var(--card);border-radius:9px;font-size:.96rem;min-height:60px;line-height:1.8"></div>
<div class="feedback" id="p7-iv2-fb"></div>
</div>`;
/* === ИНТЕРАКТИВ 3 — DnD-сортер === */
html += `<div class="wg" id="p7-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Сопоставь выражение со значением</div></div>
<div class="wg-help">Перетащи каждое выражение в ящик с верным значением. Используй свойства логарифмов: сумму, разность, степень и корень.</div>
<div class="dnd-hint"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M9 11V6a3 3 0 0 1 6 0v5"/><path d="M9 11h6v8a4 4 0 0 1-8 0z"/></svg> 8 выражений — 4 ящика</div>
<div id="p7-iv3-pool"></div>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(140px,1fr));gap:10px;margin-top:8px">
<div class="drop-box"><h5 data-cat="v1">$=1$</h5><div class="drop-items" data-cat="v1"></div></div>
<div class="drop-box"><h5 data-cat="v2">$=2$</h5><div class="drop-items" data-cat="v2"></div></div>
<div class="drop-box"><h5 data-cat="v3">$=3$</h5><div class="drop-items" data-cat="v3"></div></div>
<div class="drop-box"><h5 data-cat="v5">$=5$</h5><div class="drop-items" data-cat="v5"></div></div>
</div>
<div class="actions"><button class="btn primary" id="p7-iv3-check">Проверить</button><button class="btn" id="p7-iv3-reset">Сначала</button></div>
<div class="feedback" id="p7-iv3-fb"></div>
</div>`;
/* === ИНТЕРАКТИВ 4 — тренажёр === */
html += `<div class="wg" id="p7-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр свойств</div></div>
<div class="wg-help">6 задач на применение свойств. Введи число (целое или десятичное, допуск $\\pm 0{,}05$).</div>
<div class="score-display"><span>Задача <b id="p7-iv4-i">1</b> / 6</span><span>Очки: <b id="p7-iv4-s">0</b> / 6</span></div>
<div id="p7-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.12rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p7-iv4-ans" class="tinp" style="width:120px;text-align:center" step="0.01">
<button class="btn primary" id="p7-iv4-go">Проверить</button>
<button class="btn" id="p7-iv4-start">Заново</button>
</div>
<div class="feedback" id="p7-iv4-fb"></div>
</div>`;
html += secNavFor('p7');
html += readButton('p7');
box.innerHTML = html;
renderMath(box);
/* === IV1 — пошаговый «применитель» === */
(function(){
const TASKS = [
{
q: '$\\log_2 24 - \\log_2 3$',
steps: [
'Используем свойство <b>логарифма частного</b>: $\\log_a b - \\log_a c = \\log_a \\dfrac{b}{c}$.',
'Получаем $\\log_2 24 - \\log_2 3 = \\log_2 \\dfrac{24}{3} = \\log_2 8$.',
'Так как $8 = 2^3$, имеем $\\log_2 8 = 3$.',
'<b>Ответ:</b> $3$.'
]
},
{
q: '$\\log_3 5 + \\log_3 18$',
steps: [
'Используем свойство <b>логарифма произведения</b>: $\\log_a b + \\log_a c = \\log_a(bc)$.',
'Получаем $\\log_3 5 + \\log_3 18 = \\log_3(5 \\cdot 18) = \\log_3 90$.',
'Заметим, что $90 = 9 \\cdot 10$, поэтому $\\log_3 90 = \\log_3 9 + \\log_3 10 = 2 + \\log_3 10$.',
'<b>Ответ:</b> $2 + \\log_3 10 \\approx 4{,}096$.'
]
},
{
q: '$2 \\log_5 3 + \\log_5 \\dfrac{25}{9}$',
steps: [
'По свойству степени: $2 \\log_5 3 = \\log_5 3^2 = \\log_5 9$.',
'Получаем $\\log_5 9 + \\log_5 \\dfrac{25}{9} = \\log_5\\left(9 \\cdot \\dfrac{25}{9}\\right) = \\log_5 25$.',
'Так как $25 = 5^2$, имеем $\\log_5 25 = 2$.',
'<b>Ответ:</b> $2$.'
]
},
{
q: '$\\log_2 \\sqrt[3]{32}$',
steps: [
'Применяем свойство <b>логарифма корня</b>: $\\log_a \\sqrt[n]{b} = \\dfrac{1}{n} \\log_a b$.',
'Получаем $\\log_2 \\sqrt[3]{32} = \\dfrac{1}{3} \\log_2 32$.',
'Так как $32 = 2^5$, имеем $\\log_2 32 = 5$, значит $\\dfrac{1}{3} \\cdot 5 = \\dfrac{5}{3}$.',
'<b>Ответ:</b> $\\dfrac{5}{3} \\approx 1{,}667$.'
]
},
{
q: '$\\log_{16} 8$',
steps: [
'Перепишем основание и аргумент по основанию $2$: $16 = 2^4$, $8 = 2^3$.',
'Применяем формулу $\\log_{a^k} b^m = \\dfrac{m}{k} \\log_a b$: $\\log_{2^4} 2^3 = \\dfrac{3}{4} \\log_2 2 = \\dfrac{3}{4}$.',
'<b>Ответ:</b> $\\dfrac{3}{4} = 0{,}75$.'
]
}
];
const sn = document.getElementById('p7-iv1-sn');
const nL = document.getElementById('p7-iv1-n');
const qEl = document.getElementById('p7-iv1-q');
const stepsEl = document.getElementById('p7-iv1-steps');
const nextBtn = document.getElementById('p7-iv1-next');
const allBtn = document.getElementById('p7-iv1-all');
const resetBtn = document.getElementById('p7-iv1-reset');
const seen = new Set();
let _done = false;
let cur = 0, shown = 0;
function render(){
const t = TASKS[cur];
nL.textContent = (cur + 1);
qEl.innerHTML = t.q;
stepsEl.innerHTML = t.steps.map((s, i) => {
const visible = i < shown;
const isLast = i === t.steps.length - 1 && visible;
const bg = isLast ? '#dcfce7' : 'var(--card)';
const brd = isLast ? '2px solid #16a34a' : '1px solid var(--border)';
return '<div data-i="'+i+'" style="padding:10px 12px;background:'+bg+';border:'+brd+';border-radius:8px;font-size:.95rem;display:'+(visible?'block':'none')+'"><span style="color:var(--muted);font-weight:700;margin-right:6px">Шаг '+(i+1)+':</span>'+s+'</div>';
}).join('');
renderMath(qEl);
renderMath(stepsEl);
if(shown >= t.steps.length){
seen.add(cur);
if(!_done && seen.size >= 5){ _done = true; addXp(10, 'p7-iv1'); bumpProgress('p7', 15); }
}
}
function load(n){ cur = Math.max(0, Math.min(TASKS.length - 1, n)); shown = 1; render(); }
sn.addEventListener('input', () => load((+sn.value) - 1));
nextBtn.addEventListener('click', () => {
if(shown < TASKS[cur].steps.length){ shown++; render(); }
});
allBtn.addEventListener('click', () => { shown = TASKS[cur].steps.length; render(); });
resetBtn.addEventListener('click', () => { shown = 1; render(); });
load(0);
})();
/* === IV2 — калькулятор === */
(function(){
const modes = ['prod','quot','pow','base'];
const inputsEl = document.getElementById('p7-iv2-inputs');
const outEl = document.getElementById('p7-iv2-out');
const fbEl = document.getElementById('p7-iv2-fb');
const goBtn = document.getElementById('p7-iv2-go');
let mode = 'prod';
let used = new Set();
let _done = false;
function setMode(m){
mode = m;
modes.forEach(mm => {
const btn = document.getElementById('p7-iv2-m-'+mm);
if(btn) btn.classList.toggle('primary', mm === m);
});
fbEl.style.display = 'none';
outEl.innerHTML = '<span style="color:var(--muted)">Нажми «Вычислить», чтобы увидеть шаги.</span>';
if(m === 'prod'){
inputsEl.innerHTML =
'<span style="font-family:JetBrains Mono,monospace">$a$ =</span>'+
'<input type="number" id="p7-iv2-a" class="tinp" style="width:70px;text-align:center" value="2" step="1" min="0.1">'+
'<span style="font-family:JetBrains Mono,monospace">$b$ =</span>'+
'<input type="number" id="p7-iv2-b" class="tinp" style="width:70px;text-align:center" value="4" step="1" min="0.1">'+
'<span style="font-family:JetBrains Mono,monospace">$c$ =</span>'+
'<input type="number" id="p7-iv2-c" class="tinp" style="width:70px;text-align:center" value="8" step="1" min="0.1">';
} else if(m === 'quot'){
inputsEl.innerHTML =
'<span style="font-family:JetBrains Mono,monospace">$a$ =</span>'+
'<input type="number" id="p7-iv2-a" class="tinp" style="width:70px;text-align:center" value="2" step="1" min="0.1">'+
'<span style="font-family:JetBrains Mono,monospace">$b$ =</span>'+
'<input type="number" id="p7-iv2-b" class="tinp" style="width:70px;text-align:center" value="32" step="1" min="0.1">'+
'<span style="font-family:JetBrains Mono,monospace">$c$ =</span>'+
'<input type="number" id="p7-iv2-c" class="tinp" style="width:70px;text-align:center" value="8" step="1" min="0.1">';
} else if(m === 'pow'){
inputsEl.innerHTML =
'<span style="font-family:JetBrains Mono,monospace">$a$ =</span>'+
'<input type="number" id="p7-iv2-a" class="tinp" style="width:70px;text-align:center" value="2" step="1" min="0.1">'+
'<span style="font-family:JetBrains Mono,monospace">$b$ =</span>'+
'<input type="number" id="p7-iv2-b" class="tinp" style="width:70px;text-align:center" value="8" step="1" min="0.1">'+
'<span style="font-family:JetBrains Mono,monospace">$n$ =</span>'+
'<input type="number" id="p7-iv2-n" class="tinp" style="width:70px;text-align:center" value="3" step="1">';
} else { // base
inputsEl.innerHTML =
'<span style="font-family:JetBrains Mono,monospace">$a$ =</span>'+
'<input type="number" id="p7-iv2-a" class="tinp" style="width:70px;text-align:center" value="2" step="0.5" min="0.1">'+
'<span style="font-family:JetBrains Mono,monospace">$b$ =</span>'+
'<input type="number" id="p7-iv2-b" class="tinp" style="width:70px;text-align:center" value="7" step="1" min="0.1">'+
'<span style="font-family:JetBrains Mono,monospace">$c$ =</span>'+
'<input type="number" id="p7-iv2-c" class="tinp" style="width:70px;text-align:center" value="10" step="1" min="0.1">';
}
renderMath(inputsEl);
}
modes.forEach(m => {
const btn = document.getElementById('p7-iv2-m-'+m);
if(btn) btn.addEventListener('click', () => setMode(m));
});
setMode('prod');
function num(id){ const el = document.getElementById(id); return el ? +el.value : NaN; }
function r(n){ return (Math.abs(n - Math.round(n)) < 1e-9) ? Math.round(n) : (+n.toFixed(4)); }
goBtn.addEventListener('click', () => {
fbEl.style.display = 'none';
const a = num('p7-iv2-a'), b = num('p7-iv2-b'), c = num('p7-iv2-c'), n = num('p7-iv2-n');
// Базовая валидация
if(mode !== 'pow'){
if(!isFinite(a) || a <= 0 || a === 1 || !isFinite(b) || b <= 0 || !isFinite(c) || c <= 0){
feedback(fbEl, false, '&#10007; Требуется $a > 0$, $a \\ne 1$, $b > 0$, $c > 0$.'); return;
}
} else {
if(!isFinite(a) || a <= 0 || a === 1 || !isFinite(b) || b <= 0 || !isFinite(n)){
feedback(fbEl, false, '&#10007; Требуется $a > 0$, $a \\ne 1$, $b > 0$.'); return;
}
}
let out = '';
if(mode === 'prod'){
const sum = Math.log(b*c) / Math.log(a);
const lb = Math.log(b) / Math.log(a);
const lc = Math.log(c) / Math.log(a);
out =
'<div>$\\log_{'+r(a)+'}('+r(b)+' \\cdot '+r(c)+') = \\log_{'+r(a)+'} '+r(b)+' + \\log_{'+r(a)+'} '+r(c)+'$</div>'+
'<div>$= '+r(lb)+' + '+r(lc)+' \\approx '+r(sum)+'$</div>'+
'<div style="color:var(--muted);font-size:.86rem;margin-top:6px">Проверка через Math.log: $\\log_{'+r(a)+'}('+r(b*c)+') \\approx '+r(sum)+'$.</div>';
} else if(mode === 'quot'){
const diff = Math.log(b/c) / Math.log(a);
const lb = Math.log(b) / Math.log(a);
const lc = Math.log(c) / Math.log(a);
out =
'<div>$\\log_{'+r(a)+'} \\dfrac{'+r(b)+'}{'+r(c)+'} = \\log_{'+r(a)+'} '+r(b)+' - \\log_{'+r(a)+'} '+r(c)+'$</div>'+
'<div>$= '+r(lb)+' - '+r(lc)+' \\approx '+r(diff)+'$</div>'+
'<div style="color:var(--muted);font-size:.86rem;margin-top:6px">Проверка: $\\log_{'+r(a)+'}('+r(b/c)+') \\approx '+r(diff)+'$.</div>';
} else if(mode === 'pow'){
const lb = Math.log(b) / Math.log(a);
const val = n * lb;
out =
'<div>$\\log_{'+r(a)+'} '+r(b)+'^{'+r(n)+'} = '+r(n)+' \\cdot \\log_{'+r(a)+'} '+r(b)+'$</div>'+
'<div>$= '+r(n)+' \\cdot '+r(lb)+' \\approx '+r(val)+'$</div>'+
'<div style="color:var(--muted);font-size:.86rem;margin-top:6px">Проверка: $\\log_{'+r(a)+'}('+r(Math.pow(b,n))+') \\approx '+r(val)+'$.</div>';
} else { // base
if(c === 1){ feedback(fbEl, false, '&#10007; Новое основание $c$ не должно быть равно $1$.'); return; }
const lab = Math.log(b) / Math.log(a);
const lcb = Math.log(b) / Math.log(c);
const lca = Math.log(a) / Math.log(c);
out =
'<div>$\\log_{'+r(a)+'} '+r(b)+' = \\dfrac{\\log_{'+r(c)+'} '+r(b)+'}{\\log_{'+r(c)+'} '+r(a)+'}$</div>'+
'<div>$\\approx \\dfrac{'+r(lcb)+'}{'+r(lca)+'} \\approx '+r(lab)+'$</div>'+
'<div style="color:var(--muted);font-size:.86rem;margin-top:6px">Прямое значение: $\\log_{'+r(a)+'} '+r(b)+' \\approx '+r(lab)+'$.</div>';
}
outEl.innerHTML = out;
renderMath(outEl);
used.add(mode);
if(!_done && used.size >= 3){ _done = true; addXp(10, 'p7-iv2'); bumpProgress('p7', 15); }
});
})();
/* === IV3 — DnD-сортер === */
(function(){
const items = [
{id:'s1', html:'$\\log_2 8 + \\log_2 4$', cat:'v5'},
{id:'s2', html:'$\\log_3 81 - \\log_3 3$', cat:'v3'},
{id:'s3', html:'$2 \\log_5 5$', cat:'v2'},
{id:'s4', html:'$\\log_2 32 - \\log_2 8$', cat:'v2'},
{id:'s5', html:'$\\log_3 9 + \\log_3 3$', cat:'v3'},
{id:'s6', html:'$\\log_2 \\sqrt[3]{8}$', cat:'v1'},
{id:'s7', html:'$\\log_5 125 + \\log_5 1$', cat:'v3'},
{id:'s8', html:'$\\log_4 16 + \\log_4 4$', cat:'v3'}
];
const cats = ['v1','v2','v3','v5'];
const sorter = setupSorter({
poolId: 'p7-iv3-pool',
scopeSelector: '#p7-iv3',
items, cats
});
const checkBtn = document.getElementById('p7-iv3-check');
const resetBtn = document.getElementById('p7-iv3-reset');
const fbEl = document.getElementById('p7-iv3-fb');
let _done = false;
checkBtn.addEventListener('click', () => {
let ok = 0, total = items.length;
let missed = [];
items.forEach(it => {
if(sorter.placed[it.id] === it.cat) ok++;
else if(!sorter.placed[it.id]) missed.push(it.id);
});
if(ok === total){
feedback(fbEl, true, '&#10003; Отлично! Все 8 выражений на месте.');
if(!_done){ _done = true; addXp(15, 'p7-iv3'); bumpProgress('p7', 25); }
} else {
const left = missed.length;
feedback(fbEl, false, '&#10007; Верно: '+ok+' / '+total+'. '+(left ? 'Не размещено: '+left+'.' : 'Проверь свойства логарифмов.'));
}
});
resetBtn.addEventListener('click', () => {
sorter.reset();
fbEl.style.display = 'none';
});
})();
/* === IV4 — тренажёр === */
(function(){
const TASKS = [
{ q: '$\\log_2 6 + \\log_2 \\dfrac{16}{3}$', a: 5, hint: '$\\log_2(6 \\cdot 16/3) = \\log_2 32 = 5$.' },
{ q: '$\\log_3 \\sqrt{27}$', a: 1.5, hint: '$\\dfrac{1}{2} \\log_3 27 = \\dfrac{3}{2}$.' },
{ q: '$\\log_5 100 - \\log_5 4$', a: 2, hint: '$\\log_5 (100/4) = \\log_5 25 = 2$.' },
{ q: '$\\log_4 64$', a: 3, hint: '$\\log_{2^2} 2^6 = 6/2 = 3$.' },
{ q: '$\\log_2 3 \\cdot \\log_3 8$', a: 3, hint: '$\\log_2 3 \\cdot \\dfrac{\\log_2 8}{\\log_2 3} = \\log_2 8 = 3$.' },
{ q: '$\\dfrac{\\lg 100}{\\lg 10}$', a: 2, hint: '$\\lg 100 / \\lg 10 = 2/1 = 2$.' }
];
const qEl = document.getElementById('p7-iv4-q');
const ansEl = document.getElementById('p7-iv4-ans');
const goBtn = document.getElementById('p7-iv4-go');
const startBtn = document.getElementById('p7-iv4-start');
const iEl = document.getElementById('p7-iv4-i');
const sEl = document.getElementById('p7-iv4-s');
const fbEl = document.getElementById('p7-iv4-fb');
let idx = 0, score = 0;
let _done = false;
function render(){
iEl.textContent = (idx + 1);
sEl.textContent = score;
if(idx >= TASKS.length){
qEl.innerHTML = '<b style="color:var(--ok)">Все задачи решены! Очков: '+score+' / '+TASKS.length+'.</b>';
ansEl.disabled = true; goBtn.disabled = true;
if(!_done){ _done = true; addXp(15, 'p7-iv4'); bumpProgress('p7', 25); }
renderMath(qEl);
return;
}
qEl.innerHTML = TASKS[idx].q;
ansEl.value = '';
ansEl.disabled = false; goBtn.disabled = false;
fbEl.style.display = 'none';
renderMath(qEl);
setTimeout(() => ansEl.focus(), 30);
}
goBtn.addEventListener('click', () => {
if(idx >= TASKS.length) return;
const v = +ansEl.value;
if(!isFinite(v) || ansEl.value === ''){ feedback(fbEl, false, '&#10007; Введи число.'); return; }
const t = TASKS[idx];
if(Math.abs(v - t.a) < 0.05){
score++; sEl.textContent = score;
feedback(fbEl, true, '&#10003; Верно! '+t.hint);
setTimeout(() => { idx++; render(); }, 950);
} else {
feedback(fbEl, false, '&#10007; Неверно. Подсказка: '+t.hint+' Правильный ответ: <b>'+t.a+'</b>.');
setTimeout(() => { idx++; render(); }, 1900);
}
});
ansEl.addEventListener('keydown', e => { if(e.key === 'Enter'){ e.preventDefault(); goBtn.click(); } });
startBtn.addEventListener('click', () => { idx = 0; score = 0; _done = false; render(); });
render();
})();
wireReadBtn('p7');
}
function buildP8(){
const box = document.getElementById('p8-body');
let html = '';
/* === ТЕОРИЯ === */
html += makeCard('theory', 'Определение и графики', '8.1', `
<p>Функция вида $y = \\log_a x$, где $a > 0$, $a \\ne 1$, $x > 0$, называется <b>логарифмической</b>.</p>
<p>Логарифмическая функция — <b>обратная</b> к показательной $y = a^x$ (см. §4). Это значит, что:</p>
<ul style="margin:8px 0 8px 22px;line-height:1.75">
<li>$a^{\\log_a x} = x$ при $x > 0$;</li>
<li>$\\log_a a^x = x$ при любом $x \\in \\mathbb{R}$.</li>
</ul>
<p>Графики $y = \\log_a x$ и $y = a^x$ <b>симметричны относительно прямой $y = x$</b> — это общее свойство любой пары взаимно обратных функций.</p>
<p>Примеры логарифмических функций:</p>
<ul style="margin:8px 0 8px 22px;line-height:1.75">
<li>$y = \\log_2 x$, $y = \\log_3 x$, $y = \\log_5 x$ — основания больше единицы;</li>
<li>$y = \\lg x$ — десятичный логарифм ($a = 10$);</li>
<li>$y = \\ln x$ — натуральный логарифм ($a = e \\approx 2{,}718$);</li>
<li>$y = \\log_{1/2} x$, $y = \\log_{0{,}3} x$ — основания между нулём и единицей.</li>
</ul>
<p>График логарифмической функции:</p>
<ul style="margin:8px 0 8px 22px;line-height:1.75">
<li>при $a > 1$ — <b>возрастающая</b> кривая, медленно стремящаяся к $+\\infty$ при $x \\to +\\infty$;</li>
<li>при $0 < a < 1$ — <b>убывающая</b> кривая, стремящаяся к $-\\infty$ при $x \\to +\\infty$.</li>
</ul>
<p>Все графики $y = \\log_a x$ проходят через точку $(1; 0)$ — ведь $\\log_a 1 = 0$ при любом $a > 0$, $a \\ne 1$. Также через $(a; 1)$, потому что $\\log_a a = 1$.</p>`);
html += makeCard('rule', 'Свойства логарифмической функции', '8.2', `
<p>Сведём все ключевые свойства функции $y = \\log_a x$ в одну таблицу — отдельно для двух случаев $a > 1$ и $0 < a < 1$:</p>
<div style="overflow-x:auto;margin:10px 0">
<table style="width:100%;border-collapse:collapse;font-size:.88rem">
<thead>
<tr style="background:var(--sec-acc-soft)">
<th style="padding:8px;border:1px solid var(--border);text-align:left">Свойство</th>
<th style="padding:8px;border:1px solid var(--border);text-align:left;color:#1d4ed8">$a > 1$</th>
<th style="padding:8px;border:1px solid var(--border);text-align:left;color:#c2410c">$0 < a < 1$</th>
</tr>
</thead>
<tbody>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>$D(y)$ — область определения</b></td><td style="padding:7px;border:1px solid var(--border)" colspan="2" align="center">$(0; +\\infty)$ — только положительные $x$</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>$E(y)$ — область значений</b></td><td style="padding:7px;border:1px solid var(--border)" colspan="2" align="center">$\\mathbb{R}$ — вся числовая прямая</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>Нули функции</b></td><td style="padding:7px;border:1px solid var(--border)" colspan="2" align="center">$x = 1$ (так как $\\log_a 1 = 0$)</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>$y > 0$ при</b></td><td style="padding:7px;border:1px solid var(--border)">$x > 1$</td><td style="padding:7px;border:1px solid var(--border)">$0 < x < 1$</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>$y < 0$ при</b></td><td style="padding:7px;border:1px solid var(--border)">$0 < x < 1$</td><td style="padding:7px;border:1px solid var(--border)">$x > 1$</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>Монотонность</b></td><td style="padding:7px;border:1px solid var(--border)"><b>возрастает</b> на $(0; +\\infty)$</td><td style="padding:7px;border:1px solid var(--border)"><b>убывает</b> на $(0; +\\infty)$</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>Контрольные точки</b></td><td style="padding:7px;border:1px solid var(--border)" colspan="2" align="center">$(1; 0)$ и $(a; 1)$</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>Асимптота</b></td><td style="padding:7px;border:1px solid var(--border)" colspan="2" align="center">$x = 0$ (ось ординат)</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>При $x \\to +\\infty$</b></td><td style="padding:7px;border:1px solid var(--border)">$y \\to +\\infty$</td><td style="padding:7px;border:1px solid var(--border)">$y \\to -\\infty$</td></tr>
<tr><td style="padding:7px;border:1px solid var(--border)"><b>При $x \\to 0^+$</b></td><td style="padding:7px;border:1px solid var(--border)">$y \\to -\\infty$</td><td style="padding:7px;border:1px solid var(--border)">$y \\to +\\infty$</td></tr>
</tbody>
</table>
</div>
<p><b>Ключевая идея:</b> два графика $y = \\log_a x$ и $y = \\log_{1/a} x$ симметричны относительно оси $Ox$, потому что $\\log_{1/a} x = -\\log_a x$.</p>`);
html += makeCard('example', 'Обратная функция и применение', '8.3', `
<p><b>Логарифм и показательная — обратные функции.</b> Это означает, что они «отменяют» друг друга:</p>
<p style="text-align:center;font-size:1.05rem">$a^{\\log_a x} = x$ (при $x > 0$),$\\quad \\log_a a^x = x$ (при любом $x$).</p>
<p>Графически: <b>график $y = \\log_a x$ получается из графика $y = a^x$ зеркальным отражением относительно прямой $y = x$</b>. Каждая точка $(p; q)$ на показательной кривой переходит в точку $(q; p)$ на логарифмической.</p>
<p>Например, для $a = 2$: точка $(3; 8)$ лежит на $y = 2^x$, а симметричная ей $(8; 3)$ — на $y = \\log_2 x$.</p>
<p style="margin-top:10px"><b>Применение логарифмической функции в реальной жизни</b> — там, где величина изменяется в очень широких пределах и удобно «сжать» шкалу:</p>
<ul style="margin:8px 0 8px 22px;line-height:1.85">
<li><b>Громкость звука (децибелы):</b> $L = 20 \\lg \\dfrac{p}{p_0}$ — рост звукового давления в 10 раз даёт $+20$ дБ.</li>
<li><b>Магнитуда землетрясения (шкала Рихтера):</b> $M = \\lg E - \\lg E_0$ — увеличение магнитуды на 1 соответствует росту энергии в 10 раз.</li>
<li><b>Кислотность среды (pH):</b> $\\mathrm{pH} = -\\lg [H^+]$ — pH крови $\\approx 7{,}4$, желудочного сока $\\approx 1{,}5$.</li>
<li><b>Объём информации:</b> $I = \\log_2 N$ бит — чтобы закодировать одну букву из $32$ символов, нужно $\\log_2 32 = 5$ бит.</li>
<li><b>Яркость звёзд (звёздная величина):</b> $m_1 - m_2 = -2{,}5 \\lg(F_1/F_2)$ — астрономы со времён Гиппарха используют логарифмическую шкалу.</li>
<li><b>Психофизика (закон Вебера — Фехнера):</b> субъективная сила ощущения пропорциональна логарифму раздражителя.</li>
</ul>
<p>Логарифмическая шкала идеально подходит для природы: наше ухо, глаз и нервная система воспринимают раздражители <b>логарифмически</b>.</p>`);
/* === ИНТЕРАКТИВЫ === */
/* IV1 — двухпанельный визуализатор y = log_a x */
html += `<div class="wg" id="p8-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Двухпанельный визуализатор $y = \\log_a x$</div></div>
<div class="wg-help">Меняй основание $a$ ползунком — наблюдай, как меняется характер логарифмической функции. <b>Левая панель</b> для $a > 1$ (возрастающая, синий), <b>правая</b> — для $0 < a < 1$ (убывающая, оранжевый). Snap-точки: $a \\in \\{0{,}1;\\ 0{,}25;\\ 0{,}5;\\ 1;\\ 2;\\ e;\\ 3;\\ 10\\}$. Точка $(1; 0)$ — всегда на графике, ось $Oy$ — вертикальная асимптота.</div>
<div class="sliders">
<label>Основание $a$ = <b id="p8-iv1-a">2</b><input type="range" id="p8-iv1-sa" min="0.1" max="10" step="0.05" value="2"></label>
</div>
<div style="background:var(--card);border-radius:9px;padding:10px;text-align:center"><svg id="p8-iv1-svg" viewBox="0 0 720 360" width="100%" style="max-width:720px;height:auto"></svg></div>
<div id="p8-iv1-desc" style="margin-top:10px;padding:12px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem;line-height:1.6;min-height:80px"></div>
</div>`;
/* IV2 — сравнение y = a^x и y = log_a x (симметрия) */
html += `<div class="wg" id="p8-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Сравнение $y = a^x$ и $y = \\log_a x$</div></div>
<div class="wg-help">Ползунок меняет основание $a > 1$. На одних осях рисуются обе функции — показательная (синий) и логарифмическая (оранжевый), а серым пунктиром — ось симметрии $y = x$. Нажми «Показать симметрию», чтобы увидеть пары симметричных точек.</div>
<div class="sliders">
<label>Основание $a$ = <b id="p8-iv2-a">2</b><input type="range" id="p8-iv2-sa" min="1.5" max="5" step="0.1" value="2"></label>
</div>
<div style="background:var(--card);border-radius:9px;padding:10px;text-align:center"><svg id="p8-iv2-svg" viewBox="0 0 480 420" width="100%" style="max-width:480px;height:auto"></svg></div>
<div class="actions" style="justify-content:center">
<button class="btn primary" id="p8-iv2-sym">Показать симметрию</button>
<button class="btn" id="p8-iv2-hide">Скрыть</button>
</div>
<div id="p8-iv2-desc" style="margin-top:10px;padding:12px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:.92rem;line-height:1.6;min-height:60px"></div>
</div>`;
/* IV3 — квикфайр «Возрастает или убывает?» */
html += `<div class="wg" id="p8-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Возрастает или убывает?</div></div>
<div class="wg-help">Для каждой логарифмической функции определи характер монотонности. 8 заданий.</div>
<div class="score-display"><span>Задача <b id="p8-iv3-i">1</b> / 8</span><span>Очки: <b id="p8-iv3-s">0</b> / 8</span></div>
<div id="p8-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.1rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div id="p8-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:10px;max-width:420px;margin:0 auto"></div>
<div class="feedback" id="p8-iv3-fb"></div>
<div class="actions" style="justify-content:center"><button class="btn" id="p8-iv3-restart">Начать заново</button></div>
</div>`;
/* IV4 — тренажёр значений логарифмической функции */
html += `<div class="wg" id="p8-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр свойств</div></div>
<div class="wg-help">Вычисли значение логарифмической функции. Введи число (целое или десятичное до 2 знаков, допуск $\\pm 0{,}05$). 6 задач.</div>
<div class="score-display"><span>Задача <b id="p8-iv4-i">1</b> / 6</span><span>Очки: <b id="p8-iv4-s">0</b> / 6</span></div>
<div id="p8-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.15rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p8-iv4-ans" class="tinp" style="width:120px;text-align:center" step="0.01">
<button class="btn primary" id="p8-iv4-go">Проверить</button>
<button class="btn" id="p8-iv4-start">Заново</button>
</div>
<div class="feedback" id="p8-iv4-fb"></div>
</div>`;
html += secNavFor('p8');
html += readButton('p8');
box.innerHTML = html;
renderMath(box);
/* === IV1 — двухпанельный визуализатор y = log_a x === */
(function(){
const sa = document.getElementById('p8-iv1-sa');
const aL = document.getElementById('p8-iv1-a');
const svg = document.getElementById('p8-iv1-svg');
const desc = document.getElementById('p8-iv1-desc');
const E = Math.E;
const SNAP = [0.1, 0.25, 0.5, 1, 2, E, 3, 10];
const seen = new Set();
let _done = false;
const COL_GROW = '#2563eb';
const COL_DECAY = '#ea580c';
const COL_NEUTRAL = '#94a3b8';
const COL_POINT10 = '#10b981';
const COL_POINTA1 = '#dc2626';
function aLabel(a){
if(Math.abs(a - E) < 0.03) return 'e \\approx 2{,}72';
if(Math.abs(a - Math.round(a)) < 0.005) return String(Math.round(a));
if(Math.abs(a - 0.5) < 0.005) return '\\tfrac{1}{2}';
if(Math.abs(a - 0.25) < 0.005) return '\\tfrac{1}{4}';
if(Math.abs(a - 0.1) < 0.005) return '\\tfrac{1}{10}';
return (+a.toFixed(2)).toString();
}
function aPlain(a){
if(Math.abs(a - E) < 0.03) return 'e';
return (+a.toFixed(2)).toString();
}
function drawPanel(active, isGrow, a, ox){
const W = 360, H = 360, pad = 28;
const xmin = -0.5, xmax = 8, ymin = -4, ymax = 4;
const ax = axes2D(W, H, pad, xmin, xmax, ymin, ymax);
let g = '<g transform="translate('+ox+',0)">';
g += ax.content;
const col = isGrow ? COL_GROW : COL_DECAY;
// заголовок панели
g += '<text x="'+(W/2)+'" y="16" font-family="Inter,sans-serif" font-size="13" font-weight="700" text-anchor="middle" fill="'+(active?col:COL_NEUTRAL)+'">'
+ (isGrow ? 'a &gt; 1 — возрастающая' : '0 &lt; a &lt; 1 — убывающая')
+ '</text>';
if(!active){
g += '<rect x="'+pad+'" y="'+(pad+8)+'" width="'+(W-2*pad)+'" height="'+(H-2*pad-8)+'" fill="#f1f5f9" opacity=".55"/>';
g += '<text x="'+(W/2)+'" y="'+(H/2)+'" font-family="Inter,sans-serif" font-size="12" font-weight="600" text-anchor="middle" fill="#94a3b8">';
g += isGrow ? 'выбери a &gt; 1' : 'выбери 0 &lt; a &lt; 1';
g += '</text>';
g += '</g>';
return g;
}
// вертикальная асимптота x = 0
g += asymptote('v', 0, ax.toX, ax.toY, xmin, xmax, ymin, ymax, '#94a3b8');
// график y = log_a x — только при x > 0
const lnA = Math.log(a);
g += plotFunc(x => (x > 0 ? Math.log(x) / lnA : NaN), 0.001, xmax, ax.toX, ax.toY, col, 300);
// точка (1; 0) — зелёная
g += pointWithDrop(1, 0, ax.toX, ax.toY, COL_POINT10, '(1; 0)');
// точка (a; 1) — красная (если a в пределах xmax)
if(a > 0 && a <= xmax){
g += pointWithDrop(a, 1, ax.toX, ax.toY, COL_POINTA1, '('+aPlain(a)+'; 1)');
}
g += '</g>';
return g;
}
function drawConstPanel(){
// обе панели — серые заглушки для a = 1
const W = 720, H = 360, pad = 28;
const xmin = -0.5, xmax = 8, ymin = -4, ymax = 4;
let out = '';
// левая
{
const ax = axes2D(360, H, pad, xmin, xmax, ymin, ymax);
let g = '<g transform="translate(0,0)">' + ax.content;
g += '<text x="180" y="16" font-family="Inter,sans-serif" font-size="13" font-weight="700" text-anchor="middle" fill="'+COL_NEUTRAL+'">a = 1: $\\log_1 x$ не определён</text>';
g += asymptote('v', 0, ax.toX, ax.toY, xmin, xmax, ymin, ymax, '#cbd5e1');
g += '</g>';
out += g;
}
// правая
{
const ax = axes2D(360, H, pad, xmin, xmax, ymin, ymax);
let g = '<g transform="translate(360,0)">' + ax.content;
g += '<text x="180" y="16" font-family="Inter,sans-serif" font-size="13" font-weight="700" text-anchor="middle" fill="'+COL_NEUTRAL+'">a = 1 — не логарифмическая</text>';
g += asymptote('v', 0, ax.toX, ax.toY, xmin, xmax, ymin, ymax, '#cbd5e1');
g += '</g>';
out += g;
}
return out;
}
function describe(a){
if(Math.abs(a - 1) < 0.01){
return '<b>$a = 1$:</b> функция $y = \\log_1 x$ <b>не определена</b> — основание логарифма не должно равняться $1$.';
}
if(a > 1){
const aTxt = aLabel(a);
return '<b>$a = '+aTxt+' > 1$ — функция возрастает.</b><br>'
+ '$D = (0; +\\infty)$, $E = \\mathbb{R}$. Проходит через $(1; 0)$ и $('+aPlain(a)+'; 1)$.<br>'
+ 'При $x \\to +\\infty$: $y \\to +\\infty$. При $x \\to 0^+$: $y \\to -\\infty$ (асимптота $x = 0$).<br>'
+ '$y > 0$ при $x > 1$; $y < 0$ при $0 < x < 1$.';
}
// 0 < a < 1
const aTxt = aLabel(a);
return '<b>$a = '+aTxt+'$, $0 < a < 1$ — функция убывает.</b><br>'
+ '$D = (0; +\\infty)$, $E = \\mathbb{R}$. Проходит через $(1; 0)$ и $('+aPlain(a)+'; 1)$.<br>'
+ 'При $x \\to +\\infty$: $y \\to -\\infty$. При $x \\to 0^+$: $y \\to +\\infty$ (асимптота $x = 0$).<br>'
+ '$y > 0$ при $0 < x < 1$; $y < 0$ при $x > 1$.';
}
function draw(){
let a = +sa.value;
a = snapToValue(a, SNAP, 0.06);
aL.textContent = aLabel(a);
let svgContent = '';
if(Math.abs(a - 1) < 0.01){
svgContent = drawConstPanel();
} else if(a > 1){
svgContent += drawPanel(true, true, a, 0);
svgContent += drawPanel(false, false, a, 360);
} else {
svgContent += drawPanel(false, true, a, 0);
svgContent += drawPanel(true, false, a, 360);
}
// разделитель
svgContent += '<line x1="360" y1="6" x2="360" y2="354" stroke="#e2e8f0" stroke-width="1.5" stroke-dasharray="4 4"/>';
svg.innerHTML = svgContent;
desc.innerHTML = describe(a);
renderMath(desc);
const key = (+a.toFixed(2)).toString();
seen.add(key);
if(!_done && seen.size >= 6){ _done = true; addXp(10, 'p8-iv1'); bumpProgress('p8', 15); }
}
sa.addEventListener('input', draw);
draw();
})();
/* === IV2 — сравнение y = a^x и y = log_a x === */
(function(){
const sa = document.getElementById('p8-iv2-sa');
const aL = document.getElementById('p8-iv2-a');
const svg = document.getElementById('p8-iv2-svg');
const desc = document.getElementById('p8-iv2-desc');
const symBtn = document.getElementById('p8-iv2-sym');
const hideBtn = document.getElementById('p8-iv2-hide');
const seen = new Set();
let _done = false;
let showSym = false;
const COL_EXP = '#2563eb';
const COL_LOG = '#ea580c';
const COL_AXIS = '#94a3b8';
function aPlain(a){ return (+a.toFixed(2)).toString(); }
function draw(){
const a = +sa.value;
aL.textContent = aPlain(a);
const W = 480, H = 420, pad = 32;
const xmin = -3, xmax = 6, ymin = -3, ymax = 6;
const ax = axes2D(W, H, pad, xmin, xmax, ymin, ymax);
let g = ax.content;
// ось симметрии y = x — серый пунктир
g += '<line x1="'+ax.toX(xmin)+'" y1="'+ax.toY(xmin)+'" x2="'+ax.toX(ymax)+'" y2="'+ax.toY(ymax)+'" stroke="'+COL_AXIS+'" stroke-width="1.3" stroke-dasharray="6 4"/>';
g += '<text x="'+ax.toX(5.4)+'" y="'+ax.toY(5.8)+'" font-family="Inter,sans-serif" font-size="11" font-weight="600" fill="'+COL_AXIS+'">y = x</text>';
// показательная y = a^x
const lnA = Math.log(a);
g += plotFunc(x => Math.pow(a, x), xmin, xmax, ax.toX, ax.toY, COL_EXP, 240);
// логарифмическая y = log_a x
g += plotFunc(x => (x > 0 ? Math.log(x) / lnA : NaN), 0.001, xmax, ax.toX, ax.toY, COL_LOG, 240);
// подписи кривых
g += '<text x="'+ax.toX(2)+'" y="'+ax.toY(Math.min(Math.pow(a,2)+0.4, 5.6))+'" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="'+COL_EXP+'">y = a^x</text>';
const xLogLabel = Math.min(5, Math.pow(a, 1.4));
g += '<text x="'+ax.toX(Math.max(xLogLabel, 2))+'" y="'+ax.toY(Math.log(Math.max(xLogLabel, 2))/lnA - 0.4)+'" font-family="Inter,sans-serif" font-size="12" font-weight="700" fill="'+COL_LOG+'">y = log_a x</text>';
// ключевые точки
g += pointWithDrop(0, 1, ax.toX, ax.toY, COL_EXP, ''); // (0;1) на y=a^x
g += pointWithDrop(1, 0, ax.toX, ax.toY, COL_LOG, ''); // (1;0) на y=log_a x
// (1; a) на показательной и (a; 1) на логарифмической
if(a <= ymax) g += pointWithDrop(1, a, ax.toX, ax.toY, COL_EXP, '');
if(a <= xmax) g += pointWithDrop(a, 1, ax.toX, ax.toY, COL_LOG, '');
if(showSym){
// линии симметрии: (0;1) ↔ (1;0) и (1;a) ↔ (a;1)
g += '<line x1="'+ax.toX(0)+'" y1="'+ax.toY(1)+'" x2="'+ax.toX(1)+'" y2="'+ax.toY(0)+'" stroke="#16a34a" stroke-width="1.4" stroke-dasharray="4 4" opacity=".85"/>';
if(a <= ymax && a <= xmax){
g += '<line x1="'+ax.toX(1)+'" y1="'+ax.toY(a)+'" x2="'+ax.toX(a)+'" y2="'+ax.toY(1)+'" stroke="#16a34a" stroke-width="1.4" stroke-dasharray="4 4" opacity=".85"/>';
}
// дополнительная пара (2; a^2) ↔ (a^2; 2), если в пределах
const a2 = Math.pow(a, 2);
if(a2 <= ymax && a2 <= xmax){
g += pointWithDrop(2, a2, ax.toX, ax.toY, COL_EXP, '');
g += pointWithDrop(a2, 2, ax.toX, ax.toY, COL_LOG, '');
g += '<line x1="'+ax.toX(2)+'" y1="'+ax.toY(a2)+'" x2="'+ax.toX(a2)+'" y2="'+ax.toY(2)+'" stroke="#16a34a" stroke-width="1.4" stroke-dasharray="4 4" opacity=".85"/>';
}
}
svg.innerHTML = g;
desc.innerHTML = '<b>$y = '+aPlain(a)+'^x$ (синий) и $y = \\log_{'+aPlain(a)+'} x$ (оранжевый)</b> симметричны относительно прямой $y = x$ — это <b>обратные функции</b>.<br>'
+ 'Точка $(0; 1)$ на показательной соответствует точке $(1; 0)$ на логарифмической; точка $(1; '+aPlain(a)+')$ — точке $('+aPlain(a)+'; 1)$.';
renderMath(desc);
const key = (+a.toFixed(1)).toString();
seen.add(key);
if(!_done && seen.size >= 4){ _done = true; addXp(10, 'p8-iv2'); bumpProgress('p8', 15); }
}
sa.addEventListener('input', draw);
symBtn.addEventListener('click', () => { showSym = true; draw(); });
hideBtn.addEventListener('click', () => { showSym = false; draw(); });
draw();
})();
/* === IV3 — квикфайр «Возрастает или убывает?» === */
(function(){
const Q = [
{ q:'$y = \\log_2 x$', ans:0 },
{ q:'$y = \\log_{1/2} x$', ans:1 },
{ q:'$y = \\lg x$', ans:0 },
{ q:'$y = \\log_{0{,}1} x$', ans:1 },
{ q:'$y = \\ln x$', ans:0 },
{ q:'$y = \\log_{1/3} x$', ans:1 },
{ q:'$y = \\log_5 x$', ans:0 },
{ q:'$y = \\log_{0{,}5} x$', ans:1 },
];
const OPTS = ['Возрастает', 'Убывает'];
let i = 0, score = 0;
const qEl = document.getElementById('p8-iv3-q');
const oEl = document.getElementById('p8-iv3-opts');
const fb = document.getElementById('p8-iv3-fb');
const iEl = document.getElementById('p8-iv3-i');
const sEl = document.getElementById('p8-iv3-s');
function show(){
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: '+score+' / '+Q.length;
oEl.innerHTML = '';
if(score === Q.length){ addXp(15, 'p8-iv3'); bumpProgress('p8', 25); }
else if(score >= 5){ addXp(8, 'p8-iv3'); bumpProgress('p8', 15); }
return;
}
iEl.textContent = (i+1);
sEl.textContent = score;
const item = Q[i];
qEl.innerHTML = 'Какая монотонность у функции ' + item.q + ' ?';
oEl.innerHTML = OPTS.map((o, k) => '<button class="btn primary" data-k="'+k+'">'+o+'</button>').join('');
fb.style.display = 'none';
renderMath(qEl);
oEl.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
const k = +b.dataset.k;
if(k === item.ans){ score++; feedback(fb, true, '&#10003; Верно! Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Правильно: <b>'+OPTS[item.ans]+'</b>. Дальше ▶');
sEl.textContent = score;
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
i++;
setTimeout(show, 1100);
});
});
}
document.getElementById('p8-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
/* === IV4 — тренажёр свойств === */
(function(){
const Q = [
{ q:'$\\log_3 9 = \\,?$', ans:2, hint:'$3^2 = 9$, поэтому $\\log_3 9 = 2$.' },
{ q:'$\\log_{1/2} 4 = \\,?$', ans:-2, hint:'$(1/2)^{-2} = 4$, поэтому $\\log_{1/2} 4 = -2$.' },
{ q:'$\\lg 100 = \\,?$', ans:2, hint:'$10^2 = 100$, поэтому $\\lg 100 = 2$.' },
{ q:'$\\log_5 1 = \\,?$', ans:0, hint:'$\\log_a 1 = 0$ при любом $a$.' },
{ q:'$\\log_2 \\sqrt{8} = \\,?$', ans:1.5, hint:'$\\sqrt{8} = 2^{3/2}$, поэтому $\\log_2 \\sqrt{8} = \\dfrac{3}{2} = 1{,}5$.' },
{ q:'$\\log_{0{,}1} 100 = \\,?$', ans:-2, hint:'$0{,}1 = 10^{-1}$ и $100 = 10^2$, значит $\\log_{0{,}1} 100 = -2$.' },
];
let i = 0, score = 0;
function show(){
const qEl = document.getElementById('p8-iv4-q');
const iEl = document.getElementById('p8-iv4-i');
const sEl = document.getElementById('p8-iv4-s');
const fb = document.getElementById('p8-iv4-fb');
const ansI = document.getElementById('p8-iv4-ans');
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: '+score+' / '+Q.length;
if(score === Q.length){ addXp(15, 'p8-iv4'); bumpProgress('p8', 25); }
else if(score >= 4){ addXp(8, 'p8-iv4'); bumpProgress('p8', 15); }
return;
}
iEl.textContent = (i+1);
sEl.textContent = score;
qEl.innerHTML = Q[i].q;
ansI.value = '';
renderMath(qEl);
fb.style.display = 'none';
setTimeout(() => ansI.focus(), 30);
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p8-iv4-fb');
const raw = document.getElementById('p8-iv4-ans').value.replace(',', '.');
const ans = parseFloat(raw);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) < 0.05){
score++;
feedback(fb, true, '&#10003; Верно! '+Q[i].hint+' Дальше ▶');
} else {
feedback(fb, false, '&#10007; Неверно. Ответ $= '+Q[i].ans+'$. '+Q[i].hint+' Дальше ▶');
}
document.getElementById('p8-iv4-s').textContent = score;
i++;
setTimeout(show, 1500);
}
document.getElementById('p8-iv4-go').addEventListener('click', go);
document.getElementById('p8-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
document.getElementById('p8-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p8');
}
function buildP9(){
const box = document.getElementById('p9-body');
let html = '';
/* === ТЕОРИЯ === */
html += makeCard('theory', 'Основной метод: потенциирование', '9.1', `
<p><b>Логарифмическим уравнением</b> называется уравнение, в котором переменная содержится <b>под знаком логарифма</b> (или в его основании).</p>
<p>Простейший вид: $\\log_a f(x) = \\log_a g(x)$, где $a > 0$, $a \\ne 1$.</p>
<p>Поскольку логарифмическая функция $y = \\log_a x$ <b>монотонна</b> (см. §8) — строго возрастает при $a > 1$ или строго убывает при $0 < a < 1$, — она <b>инъективна</b>. Из равенства логарифмов следует равенство аргументов:</p>
<p style="text-align:center;padding:10px;background:var(--sec-acc-soft);border-radius:9px;margin:10px 0">
$\\log_a f(x) = \\log_a g(x) \\;\\Leftrightarrow\\; \\begin{cases} f(x) = g(x) \\\\\\\\ f(x) > 0 \\\\\\\\ g(x) > 0 \\end{cases}$
</p>
<p>Этот переход называется <b>потенциированием</b>. Условия $f(x) > 0$ и $g(x) > 0$ — это <b>ОДЗ</b> (область допустимых значений) уравнения.</p>
<p><b>Алгоритм решения</b>:</p>
<ol style="margin:8px 0 8px 22px;line-height:1.75">
<li>Привести обе части к одному основанию.</li>
<li>Применить свойства логарифмов (см. §7) для упрощения.</li>
<li>Записать ОДЗ: для каждого $\\log_a h(x)$ требуем $h(x) > 0$.</li>
<li>Потенциировать: $f(x) = g(x)$ — и решить.</li>
<li><b>Проверить</b> корни на принадлежность ОДЗ — отбросить посторонние.</li>
</ol>
<p><b>Пример.</b> $\\log_3 (5 - x) = \\log_3 (x + 1)$.</p>
<p>ОДЗ: $5 - x > 0$ и $x + 1 > 0$, то есть $-1 < x < 5$.</p>
<p>Потенциируем: $5 - x = x + 1 \\Rightarrow 2x = 4 \\Rightarrow x = 2$. Проверка: $2 \\in (-1; 5)$ — корень подходит.</p>
<p><b>Ответ:</b> $x = 2$.</p>`);
html += makeCard('rule', 'Определение логарифма и замена переменной', '9.2', `
<p><b>Метод 1. Через определение логарифма.</b> Уравнение вида $\\log_a f(x) = b$ (число справа) — это просто определение логарифма наоборот:</p>
<p style="text-align:center;padding:10px;background:var(--sec-acc-soft);border-radius:9px;margin:10px 0">
$\\log_a f(x) = b \\;\\Leftrightarrow\\; f(x) = a^b$ &nbsp;(при $f(x) > 0$).
</p>
<p><b>Пример.</b> $\\log_3 (2x - 1) = 2 \\Rightarrow 2x - 1 = 3^2 = 9 \\Rightarrow x = 5$. ОДЗ: $2x - 1 > 0 \\Leftrightarrow x > 0{,}5$. $5 > 0{,}5$ — корень подходит.</p>
<hr style="margin:14px 0;border:none;border-top:1px solid var(--border)">
<p><b>Метод 2. Замена переменной.</b> Уравнения вида</p>
<p style="text-align:center;padding:10px;background:var(--sec-acc-soft);border-radius:9px;margin:10px 0">
$\\alpha \\log_a^2 x + \\beta \\log_a x + \\gamma = 0$
</p>
<p>— квадратные относительно $t = \\log_a x$. После замены получим $\\alpha t^2 + \\beta t + \\gamma = 0$; найдём $t_1, t_2$, потом обратно $x_i = a^{t_i}$. <b>Все</b> такие $x$ автоматически положительны (это степени положительного числа), поэтому ОДЗ выполняется автоматически.</p>
<p><b>Пример.</b> $\\log_2 x + \\log_x 2 = \\dfrac{5}{2}$.</p>
<p>По формуле перехода $\\log_x 2 = \\dfrac{1}{\\log_2 x}$. Пусть $t = \\log_2 x$, тогда</p>
<p>$t + \\dfrac{1}{t} = \\dfrac{5}{2} \\Rightarrow 2t^2 - 5t + 2 = 0 \\Rightarrow t = 2$ или $t = \\dfrac{1}{2}$.</p>
<p>Обратно: $\\log_2 x = 2 \\Rightarrow x = 4$; $\\;\\log_2 x = \\dfrac{1}{2} \\Rightarrow x = \\sqrt{2}$.</p>
<p><b>Ответ:</b> $x_1 = 4,\\; x_2 = \\sqrt{2}$.</p>`);
html += makeCard('example', 'Применение свойств логарифма', '9.3', `
<p>Часто уравнения содержат <b>сумму или разность</b> логарифмов — их сводят к одному через свойства из §7:</p>
<ul style="margin:8px 0 8px 22px;line-height:1.75">
<li>$\\log_a u + \\log_a v = \\log_a (u v)$;</li>
<li>$\\log_a u - \\log_a v = \\log_a \\dfrac{u}{v}$;</li>
<li>$n \\log_a u = \\log_a u^n$.</li>
</ul>
<p style="padding:10px 12px;background:#fef3c7;border-left:4px solid #f59e0b;border-radius:6px;margin:12px 0">
<b>Внимание!</b> После применения свойств <b>ОДЗ может расшириться</b>. Например, $\\log_a u + \\log_a v$ требует $u > 0$ <b>и</b> $v > 0$, а получившийся $\\log_a (uv)$ требует только $uv > 0$ — то есть допустимо и $u < 0, v < 0$ одновременно.<br>
Поэтому при проверке корней используйте ОДЗ <b>исходного</b> уравнения, а не упрощённого!
</p>
<p><b>Пример.</b> $\\log_2 (x - 1) + \\log_2 (x + 1) = 3$.</p>
<p><b>ОДЗ исходного:</b> $x - 1 > 0$ <b>и</b> $x + 1 > 0$, то есть $x > 1$.</p>
<p>Объединяем логарифмы: $\\log_2 \\bigl((x-1)(x+1)\\bigr) = 3 \\Rightarrow (x-1)(x+1) = 2^3$.</p>
<p>$x^2 - 1 = 8 \\Rightarrow x^2 = 9 \\Rightarrow x = \\pm 3$.</p>
<p>Проверка по ОДЗ: $x = 3 > 1$ — подходит. $x = -3$ не входит в ОДЗ — <b>посторонний корень</b>.</p>
<p><b>Ответ:</b> $x = 3$.</p>
<p><b>Второй пример.</b> $\\log_5 x + \\log_5 (x - 4) = 1$.</p>
<p><b>ОДЗ:</b> $x > 0$ и $x - 4 > 0 \\Rightarrow x > 4$.</p>
<p>$\\log_5 \\bigl(x(x-4)\\bigr) = 1 \\Rightarrow x(x-4) = 5 \\Rightarrow x^2 - 4x - 5 = 0 \\Rightarrow x = 5$ или $x = -1$.</p>
<p>Проверка: $x = 5 > 4$ — подходит. $x = -1$ — посторонний.</p>
<p><b>Ответ:</b> $x = 5$.</p>`);
/* === ИНТЕРАКТИВЫ === */
/* IV1 — пошаговый решатель с ОДЗ */
html += `<div class="wg" id="p9-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Пошаговый решатель с ОДЗ</div></div>
<div class="wg-help">Выбери задачу ползунком и нажимай «Следующий шаг ▶», чтобы открывать решение по одному шагу. Особое внимание — записи ОДЗ и проверке корней. Просмотри все 5 задач — получишь XP.</div>
<div class="sliders">
<label>Задача № <b id="p9-iv1-n">1</b> / 5<input type="range" id="p9-iv1-sn" min="1" max="5" step="1" value="1"></label>
</div>
<div id="p9-iv1-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.08rem;text-align:center;margin-bottom:10px"></div>
<div id="p9-iv1-steps" style="display:flex;flex-direction:column;gap:8px;margin-bottom:10px"></div>
<div class="actions" style="justify-content:center">
<button class="btn primary" id="p9-iv1-next">Следующий шаг ▶</button>
<button class="btn" id="p9-iv1-all">Показать все шаги</button>
<button class="btn" id="p9-iv1-reset">Скрыть шаги</button>
</div>
</div>`;
/* IV2 — калькулятор log_a (kx + n) = b */
html += `<div class="wg" id="p9-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор $\\log_a (kx + n) = b$</div></div>
<div class="wg-help">Введи основание $a$ ($a > 0$, $a \\ne 1$) и коэффициенты $k$, $n$, $b$ — калькулятор решит уравнение через определение логарифма и проверит ОДЗ.</div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center;margin-bottom:8px">
<span style="font-family:'JetBrains Mono',monospace">$a$ =</span>
<input type="number" id="p9-iv2-a" class="tinp" style="width:70px;text-align:center" value="2" step="1">
<span style="font-family:'JetBrains Mono',monospace">$k$ =</span>
<input type="number" id="p9-iv2-k" class="tinp" style="width:70px;text-align:center" value="1" step="1">
<span style="font-family:'JetBrains Mono',monospace">$n$ =</span>
<input type="number" id="p9-iv2-n" class="tinp" style="width:70px;text-align:center" value="3" step="1">
<span style="font-family:'JetBrains Mono',monospace">$b$ =</span>
<input type="number" id="p9-iv2-b" class="tinp" style="width:70px;text-align:center" value="4" step="1">
<button class="btn primary" id="p9-iv2-go">Решить</button>
</div>
<div style="display:flex;gap:6px;flex-wrap:wrap;justify-content:center;margin-bottom:10px;font-size:.82rem">
<span style="color:var(--muted)">Примеры:</span>
<button class="btn" data-set="2,1,3,4" style="padding:4px 10px;font-size:.82rem">$\\log_2(x+3)=4$</button>
<button class="btn" data-set="3,2,-1,2" style="padding:4px 10px;font-size:.82rem">$\\log_3(2x-1)=2$</button>
<button class="btn" data-set="10,1,1,2" style="padding:4px 10px;font-size:.82rem">$\\lg(x+1)=2$</button>
<button class="btn" data-set="5,1,0,1" style="padding:4px 10px;font-size:.82rem">$\\log_5 x=1$</button>
</div>
<div id="p9-iv2-out" style="padding:12px 14px;background:var(--card);border-radius:9px;font-size:.96rem;min-height:60px;line-height:1.8"></div>
<div class="feedback" id="p9-iv2-fb"></div>
</div>`;
/* IV3 — посторонний корень или нет? */
html += `<div class="wg" id="p9-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Посторонний корень или нет?</div></div>
<div class="wg-help">Дано уравнение и найденный корень. Проверь, удовлетворяет ли он ОДЗ исходного уравнения. 6 заданий.</div>
<div class="score-display"><span>Задача <b id="p9-iv3-i">1</b> / 6</span><span>Очки: <b id="p9-iv3-s">0</b> / 6</span></div>
<div id="p9-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.08rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div id="p9-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:10px;max-width:420px;margin:0 auto"></div>
<div class="feedback" id="p9-iv3-fb"></div>
<div class="actions" style="justify-content:center"><button class="btn" id="p9-iv3-restart">Начать заново</button></div>
</div>`;
/* IV4 — тренажёр уравнений */
html += `<div class="wg" id="p9-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр уравнений</div></div>
<div class="wg-help">Реши уравнение и введи корень (целое или десятичное число, допуск $\\pm 0{,}05$). Где указано — введи <b>сумму</b> корней. 6 задач.</div>
<div class="score-display"><span>Задача <b id="p9-iv4-i">1</b> / 6</span><span>Очки: <b id="p9-iv4-s">0</b> / 6</span></div>
<div id="p9-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.12rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p9-iv4-ans" class="tinp" style="width:120px;text-align:center" step="0.01">
<button class="btn primary" id="p9-iv4-go">Проверить</button>
<button class="btn" id="p9-iv4-start">Заново</button>
</div>
<div class="feedback" id="p9-iv4-fb"></div>
</div>`;
html += secNavFor('p9');
html += readButton('p9');
box.innerHTML = html;
renderMath(box);
/* === IV1 — пошаговый решатель === */
(function(){
const TASKS = [
{
q: '$\\log_2 (x + 3) = 4$',
steps: [
'<b>ОДЗ:</b> $x + 3 > 0 \\Rightarrow x > -3$.',
'По определению логарифма: $x + 3 = 2^4 = 16$.',
'Решаем: $x = 16 - 3 = 13$.',
'Проверка по ОДЗ: $13 > -3$ — корень подходит.',
'<b>Ответ:</b> $x = 13$.'
]
},
{
q: '$\\log_3 (x - 1) = \\log_3 (5 - x)$',
steps: [
'<b>ОДЗ:</b> $x - 1 > 0$ и $5 - x > 0 \\Rightarrow 1 < x < 5$.',
'Потенциируем (основания равны): $x - 1 = 5 - x$.',
'Решаем: $2x = 6 \\Rightarrow x = 3$.',
'Проверка: $3 \\in (1; 5)$ — корень подходит.',
'<b>Ответ:</b> $x = 3$.'
]
},
{
q: '$\\log_5 x + \\log_5 (x - 4) = 1$',
steps: [
'<b>ОДЗ:</b> $x > 0$ и $x - 4 > 0 \\Rightarrow x > 4$.',
'Объединяем по свойству суммы: $\\log_5 \\bigl(x(x-4)\\bigr) = 1$.',
'По определению: $x(x - 4) = 5 \\Rightarrow x^2 - 4x - 5 = 0$.',
'По теореме Виета: $x_1 = 5$, $x_2 = -1$.',
'Проверка по ОДЗ: $5 > 4$ — подходит; $-1$ не входит в ОДЗ — <b>посторонний корень</b>.',
'<b>Ответ:</b> $x = 5$.'
]
},
{
q: '$\\log_2^2 x - 3 \\log_2 x + 2 = 0$',
steps: [
'<b>ОДЗ:</b> $x > 0$.',
'Замена $t = \\log_2 x$: $t^2 - 3t + 2 = 0$.',
'По теореме Виета: $t_1 = 1$, $t_2 = 2$.',
'Обратно: $\\log_2 x = 1 \\Rightarrow x = 2$; $\\;\\log_2 x = 2 \\Rightarrow x = 4$.',
'Оба корня положительны — ОДЗ выполняется.',
'<b>Ответ:</b> $x_1 = 2,\\; x_2 = 4$.'
]
},
{
q: '$\\lg^2 x - \\lg x = 6$',
steps: [
'<b>ОДЗ:</b> $x > 0$.',
'Замена $t = \\lg x$: $t^2 - t - 6 = 0$.',
'По теореме Виета: $t_1 = 3$, $t_2 = -2$.',
'Обратно: $\\lg x = 3 \\Rightarrow x = 10^3 = 1000$; $\\;\\lg x = -2 \\Rightarrow x = 10^{-2} = 0{,}01$.',
'Оба корня положительны — ОДЗ выполняется.',
'<b>Ответ:</b> $x_1 = 1000,\\; x_2 = 0{,}01$.'
]
}
];
const sn = document.getElementById('p9-iv1-sn');
const nL = document.getElementById('p9-iv1-n');
const qEl = document.getElementById('p9-iv1-q');
const stepsEl = document.getElementById('p9-iv1-steps');
const nextBtn = document.getElementById('p9-iv1-next');
const allBtn = document.getElementById('p9-iv1-all');
const resetBtn = document.getElementById('p9-iv1-reset');
const seen = new Set();
let _done = false;
let cur = 0, shown = 0;
function render(){
const t = TASKS[cur];
nL.textContent = (cur + 1);
qEl.innerHTML = t.q;
stepsEl.innerHTML = t.steps.map((s, i) => {
const visible = i < shown;
const isLast = i === t.steps.length - 1 && visible;
const bg = isLast ? '#dcfce7' : 'var(--card)';
const brd = isLast ? '2px solid #16a34a' : '1px solid var(--border)';
return '<div data-i="'+i+'" style="padding:10px 12px;background:'+bg+';border:'+brd+';border-radius:8px;font-size:.95rem;display:'+(visible?'block':'none')+'"><span style="color:var(--muted);font-weight:700;margin-right:6px">Шаг '+(i+1)+':</span>'+s+'</div>';
}).join('');
renderMath(qEl);
renderMath(stepsEl);
if(shown >= t.steps.length){
seen.add(cur);
if(!_done && seen.size >= 5){ _done = true; addXp(10, 'p9-iv1'); bumpProgress('p9', 15); }
}
}
function load(n){ cur = Math.max(0, Math.min(TASKS.length - 1, n)); shown = 1; render(); }
sn.addEventListener('input', () => load((+sn.value) - 1));
nextBtn.addEventListener('click', () => {
if(shown < TASKS[cur].steps.length){ shown++; render(); }
});
allBtn.addEventListener('click', () => { shown = TASKS[cur].steps.length; render(); });
resetBtn.addEventListener('click', () => { shown = 1; render(); });
load(0);
})();
/* === IV2 — калькулятор log_a (kx + n) = b === */
(function(){
const aI = document.getElementById('p9-iv2-a');
const kI = document.getElementById('p9-iv2-k');
const nI = document.getElementById('p9-iv2-n');
const bI = document.getElementById('p9-iv2-b');
const go = document.getElementById('p9-iv2-go');
const out = document.getElementById('p9-iv2-out');
const fb = document.getElementById('p9-iv2-fb');
const used = new Set();
let _done = false;
document.querySelectorAll('#p9-iv2 [data-set]').forEach(btn => {
btn.addEventListener('click', () => {
const v = btn.dataset.set.split(',');
aI.value = v[0]; kI.value = v[1]; nI.value = v[2]; bI.value = v[3];
calc();
});
});
function aLabel(a){
if(a === 10) return '\\lg';
return '\\log_{' + a + '}';
}
function fxLabel(k, n){
let s = '';
if(k === 1) s = 'x';
else if(k === -1) s = '-x';
else s = k + 'x';
if(n > 0) s += ' + ' + n;
else if(n < 0) s += ' - ' + (-n);
return s;
}
function calc(){
const a = parseFloat(aI.value);
const k = parseFloat(kI.value);
const n = parseFloat(nI.value);
const b = parseFloat(bI.value);
if(![a,k,n,b].every(isFinite)){ feedback(fb, false, '&#10007; Введи все четыре числа.'); return; }
if(a <= 0 || Math.abs(a - 1) < 1e-9){ feedback(fb, false, '&#10007; Основание $a$ должно быть $> 0$ и $\\ne 1$.'); return; }
if(k === 0){ feedback(fb, false, '&#10007; При $k = 0$ выражение под логарифмом не зависит от $x$.'); return; }
const fx = fxLabel(k, n);
const eq = '$' + aLabel(a) + '(' + fx + ') = ' + b + '$';
let html = '<div style="font-size:1.04rem;margin-bottom:8px;text-align:center">Уравнение: '+eq+'</div>';
// ОДЗ: kx + n > 0
let odzStr;
if(k > 0){
const bound = -n / k;
odzStr = '$x > ' + (+bound.toFixed(4)) + '$';
} else {
const bound = -n / k;
odzStr = '$x < ' + (+bound.toFixed(4)) + '$';
}
html += '<div style="margin-bottom:6px"><b>ОДЗ:</b> $' + fx + ' > 0 \\Rightarrow$ ' + odzStr + '.</div>';
// По определению: kx + n = a^b
const ab = Math.pow(a, b);
html += '<div style="margin-bottom:6px">По определению логарифма: $' + fx + ' = ' + a + '^{' + b + '} = ' + (+ab.toFixed(4)) + '$.</div>';
// x = (a^b - n) / k
const xVal = (ab - n) / k;
html += '<div style="margin-bottom:6px">Решаем: $x = \\dfrac{' + (+ab.toFixed(4)) + ' - (' + n + ')}{' + k + '} = ' + (+xVal.toFixed(4)) + '$.</div>';
// Проверка ОДЗ
const check = k * xVal + n;
const ok = check > 1e-9;
if(ok){
html += '<div style="margin-bottom:4px;color:#15803d"><b>Проверка ОДЗ:</b> $' + fx.replace('x', '(' + (+xVal.toFixed(4)) + ')') + ' = ' + (+check.toFixed(4)) + ' > 0$ — корень подходит.</div>';
html += '<div style="font-weight:700;color:#15803d">$\\Rightarrow x = ' + (+xVal.toFixed(4)) + '$</div>';
feedback(fb, true, '&#10003; Решено.');
} else {
html += '<div style="margin-bottom:4px;color:#b91c1c"><b>Проверка ОДЗ:</b> аргумент логарифма получается $\\le 0$ — посторонний корень.</div>';
html += '<div style="font-weight:700;color:#b91c1c">Уравнение не имеет решений.</div>';
feedback(fb, true, '&#10003; Решено: корней нет.');
}
out.innerHTML = html;
renderMath(out);
used.add(aI.value+','+kI.value+','+nI.value+','+bI.value);
if(!_done && used.size >= 4){ _done = true; addXp(10, 'p9-iv2'); bumpProgress('p9', 15); }
}
go.addEventListener('click', calc);
[aI, kI, nI, bI].forEach(i => i.addEventListener('keydown', e => { if(e.key === 'Enter') calc(); }));
calc();
})();
/* === IV3 — посторонний корень или нет? === */
(function(){
const OPTS = ['Подходит', 'Посторонний'];
const Q = [
{ q: '$\\log_2 (x + 1) = 3$, найден $x = 7$', ans: 0, hint: 'ОДЗ: $x + 1 > 0 \\Rightarrow x > -1$. $7 > -1$ — подходит.' },
{ q: '$\\log_2 (x - 5) = 1$, найден $x = 7$', ans: 0, hint: 'ОДЗ: $x - 5 > 0 \\Rightarrow x > 5$. $7 > 5$ — подходит.' },
{ q: '$\\log_3 (x - 1) + \\log_3 (x + 1) = 1$, найден $x = -2$', ans: 1, hint: 'ОДЗ исходного: $x - 1 > 0$ и $x + 1 > 0 \\Rightarrow x > 1$. $-2 < 1$ — посторонний.' },
{ q: '$\\log_2 (4 - x) = 0$, найден $x = 3$', ans: 0, hint: 'ОДЗ: $4 - x > 0 \\Rightarrow x < 4$. $3 < 4$ — подходит (и $4 - 3 = 1 > 0$).' },
{ q: '$\\lg (x^2 - 5) = \\lg (4x)$, найден $x = -1$', ans: 1, hint: 'ОДЗ: $4x > 0 \\Rightarrow x > 0$. $-1 < 0$ — посторонний.' },
{ q: '$\\log_5 (x - 3) = 1$, найден $x = 8$', ans: 0, hint: 'ОДЗ: $x - 3 > 0 \\Rightarrow x > 3$. $8 > 3$ — подходит ($x - 3 = 5 > 0$).' },
];
let i = 0, score = 0;
const qEl = document.getElementById('p9-iv3-q');
const oEl = document.getElementById('p9-iv3-opts');
const fb = document.getElementById('p9-iv3-fb');
const iEl = document.getElementById('p9-iv3-i');
const sEl = document.getElementById('p9-iv3-s');
function show(){
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: '+score+' / '+Q.length;
oEl.innerHTML = '';
if(score === Q.length){ addXp(15, 'p9-iv3'); bumpProgress('p9', 25); }
else if(score >= 4){ addXp(8, 'p9-iv3'); bumpProgress('p9', 15); }
return;
}
iEl.textContent = (i + 1);
sEl.textContent = score;
const item = Q[i];
qEl.innerHTML = item.q;
oEl.innerHTML = OPTS.map((o, k) => '<button class="btn primary" data-k="'+k+'">'+o+'</button>').join('');
fb.style.display = 'none';
renderMath(qEl);
oEl.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
const k = +b.dataset.k;
if(k === item.ans){ score++; feedback(fb, true, '&#10003; Верно! '+item.hint+' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Правильно: <b>'+OPTS[item.ans]+'</b>. '+item.hint+' Дальше ▶');
sEl.textContent = score;
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
i++;
setTimeout(show, 1700);
});
});
}
document.getElementById('p9-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
/* === IV4 — тренажёр уравнений === */
(function(){
const Q = [
{ q: '$\\log_3 x = 2$', ans: 9, hint: '$x = 3^2 = 9$.' },
{ q: '$\\log_2 (x - 1) = 3$', ans: 9, hint: '$x - 1 = 2^3 = 8 \\Rightarrow x = 9$.' },
{ q: '$\\lg (x + 1) = 2$', ans: 99, hint: '$x + 1 = 10^2 = 100 \\Rightarrow x = 99$.' },
{ q: '$\\log_5 (2x + 1) = 1$', ans: 2, hint: '$2x + 1 = 5 \\Rightarrow x = 2$.' },
{ q: '$\\log_2^2 x - 5 \\log_2 x + 6 = 0$ — введи <b>сумму</b> корней', ans: 12, hint: 'Замена $t = \\log_2 x$: $t = 2$ или $t = 3$. Корни $x = 4, x = 8$, сумма $= 12$.' },
{ q: '$\\log_3 x + \\log_3 (x - 2) = 1$ — введи корень', ans: 3, hint: 'ОДЗ $x > 2$. $x(x-2) = 3 \\Rightarrow x^2 - 2x - 3 = 0 \\Rightarrow x = 3$ или $-1$. $-1$ — посторонний.' },
];
let i = 0, score = 0;
function show(){
const qEl = document.getElementById('p9-iv4-q');
const iEl = document.getElementById('p9-iv4-i');
const sEl = document.getElementById('p9-iv4-s');
const fb = document.getElementById('p9-iv4-fb');
const ansI = document.getElementById('p9-iv4-ans');
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: '+score+' / '+Q.length;
if(score === Q.length){ addXp(15, 'p9-iv4'); bumpProgress('p9', 25); }
else if(score >= 4){ addXp(8, 'p9-iv4'); bumpProgress('p9', 15); }
return;
}
iEl.textContent = (i + 1);
sEl.textContent = score;
qEl.innerHTML = Q[i].q;
ansI.value = '';
renderMath(qEl);
fb.style.display = 'none';
setTimeout(() => ansI.focus(), 30);
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p9-iv4-fb');
const raw = document.getElementById('p9-iv4-ans').value.replace(',', '.');
const ans = parseFloat(raw);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) < 0.05){
score++;
feedback(fb, true, '&#10003; Верно! '+Q[i].hint+' Дальше ▶');
} else {
feedback(fb, false, '&#10007; Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+' Дальше ▶');
}
document.getElementById('p9-iv4-s').textContent = score;
i++;
setTimeout(show, 1700);
}
document.getElementById('p9-iv4-go').addEventListener('click', go);
document.getElementById('p9-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
document.getElementById('p9-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p9');
}
function buildP10(){
const box = document.getElementById('p10-body');
let html = '';
/* === ТЕОРИЯ === */
html += makeCard('theory', 'Основное правило и ОДЗ', '10.1', `
<p><b>Логарифмическим неравенством</b> называется неравенство, в котором переменная содержится <b>под знаком логарифма</b>. Простейший вид:</p>
<p style="text-align:center;font-size:1.04rem">$\\log_a f(x) > \\log_a g(x)$ &nbsp;(или $<$, $\\ge$, $\\le$).</p>
<p>Здесь $a > 0$, $a \\ne 1$. Подход к решению опирается на <b>монотонность</b> логарифмической функции (см. §8):</p>
<p style="text-align:center;padding:10px;background:var(--sec-acc-soft);border-radius:9px;margin:10px 0">
$\\log_a f(x) > \\log_a g(x) \\;\\Leftrightarrow\\; \\begin{cases} f(x) > g(x), & \\text{если } a > 1 \\\\\\\\ f(x) < g(x), & \\text{если } 0 < a < 1 \\end{cases}$
</p>
<p>Кроме того <b>всегда</b> требуем положительности выражений под логарифмами (ОДЗ):</p>
<p style="text-align:center;padding:10px;background:var(--sec-acc-soft);border-radius:9px;margin:10px 0">
$\\begin{cases} f(x) > 0 \\\\\\\\ g(x) > 0 \\end{cases}$
</p>
<p style="padding:10px 12px;background:#fef3c7;border-left:4px solid #f59e0b;border-radius:6px;margin:12px 0"><b>Запомни:</b></p>
<ul style="margin:8px 0 8px 22px;line-height:1.75">
<li>При $a > 1$ — функция возрастает, знак неравенства <b>сохраняется</b>.</li>
<li>При $0 < a < 1$ — функция убывает, знак неравенства <b>меняется</b>.</li>
<li>ОДЗ требует <b>положительности обоих</b> выражений под логарифмами.</li>
</ul>
<details class="spoiler"><summary>Почему знак меняется при $0 < a < 1$?</summary><div class="spoiler-body">
<p>Функция $y = \\log_a x$ при $0 < a < 1$ строго убывает: чем больше $x$, тем меньше $y$.</p>
<p>Поэтому неравенство $\\log_a f > \\log_a g$ означает $f < g$: чтобы $\\log$ был больше, аргумент должен быть меньше.</p>
</div></details>`);
html += makeCard('rule', 'Алгоритм решения', '10.2', `
<p><b>Шаги</b> для решения неравенства $\\log_a f(x) \\gtrless \\log_a g(x)$:</p>
<ol style="margin:8px 0 8px 22px;line-height:1.75">
<li>Записать <b>ОДЗ</b>: $f(x) > 0$ и $g(x) > 0$.</li>
<li>Привести обе части к <b>одному основанию</b> (если нужно).</li>
<li>Использовать правило монотонности: при $a > 1$ <b>сохранить</b> знак, при $0 < a < 1$ — <b>изменить</b>.</li>
<li>Решить полученное алгебраическое неравенство.</li>
<li><b>Пересечь</b> ответ с ОДЗ — это и есть финальное решение.</li>
</ol>
<hr style="margin:14px 0;border:none;border-top:1px solid var(--border)">
<p><b>Пример 1.</b> $\\log_2 (x - 1) > 3$.</p>
<p><b>ОДЗ:</b> $x - 1 > 0 \\Rightarrow x > 1$.</p>
<p>Перепишем $3 = \\log_2 8$: $\\log_2 (x - 1) > \\log_2 8$. $\\;a = 2 > 1$ — знак сохраняется:</p>
<p>$x - 1 > 8 \\Rightarrow x > 9$.</p>
<p>Пересечение с ОДЗ ($x > 1$): <b>$x > 9$</b>.</p>
<p><b>Ответ:</b> $x \\in (9; +\\infty)$.</p>
<hr style="margin:14px 0;border:none;border-top:1px solid var(--border)">
<p><b>Пример 2.</b> $\\log_{1/3} (x - 2) > -2$.</p>
<p><b>ОДЗ:</b> $x - 2 > 0 \\Rightarrow x > 2$.</p>
<p>Перепишем $-2 = \\log_{1/3} 9$ (т.к. $(1/3)^{-2} = 9$): $\\log_{1/3} (x - 2) > \\log_{1/3} 9$. $\\;a = 1/3 < 1$ — знак <b>меняется</b>:</p>
<p>$x - 2 < 9 \\Rightarrow x < 11$.</p>
<p>Пересечение с ОДЗ: $2 < x < 11$.</p>
<p><b>Ответ:</b> $x \\in (2; 11)$.</p>`);
html += makeCard('example', 'Замена переменной и графический метод', '10.3', `
<p><b>Метод замены.</b> При наличии в неравенстве $\\log_a x$ и $\\log_a^2 x$ выполняют подстановку $t = \\log_a x$ — и получают <b>квадратное</b> неравенство относительно $t$.</p>
<p><b>Пример.</b> $\\log_2^2 x - 3 \\log_2 x + 2 \\le 0$.</p>
<p>ОДЗ: $x > 0$. Замена $t = \\log_2 x$: $t^2 - 3t + 2 \\le 0$.</p>
<p>Корни уравнения $t^2 - 3t + 2 = 0$: $t_1 = 1$, $t_2 = 2$. Парабола ветвями вверх $\\le 0$ между корнями: $t \\in [1; 2]$.</p>
<p>Обратно: $1 \\le \\log_2 x \\le 2 \\Leftrightarrow 2^1 \\le x \\le 2^2 \\Leftrightarrow 2 \\le x \\le 4$.</p>
<p><b>Ответ:</b> $x \\in [2; 4]$.</p>
<hr style="margin:14px 0;border:none;border-top:1px solid var(--border)">
<p><b>Графический метод.</b> Если неравенство нельзя свести к одному основанию или замене — используем графики. Строим $y = \\log_a f(x)$ и $y = \\log_a g(x)$ (или константу) на одной координатной плоскости и смотрим, где один график выше другого.</p>
<p style="padding:10px 12px;background:var(--sec-acc-soft);border-radius:9px"><b>Когда применять?</b> Когда $f$ и $g$ имеют разную природу (например, степенная и логарифмическая). Метод не даёт точный ответ, но позволяет понять <b>число и расположение</b> решений.</p>
<p><b>Пример.</b> $\\log_2 x < 3 - x$. Строим $y = \\log_2 x$ (растёт) и $y = 3 - x$ (убывает). Точка пересечения примерно при $x \\approx 2$. При $x < 2$ кривая логарифма ниже прямой — это и есть решение. Уточнение: проверяем $x = 2$: $\\log_2 2 = 1$, $3 - 2 = 1$. Значит ответ $x \\in (0; 2)$.</p>`);
/* === ИНТЕРАКТИВЫ === */
/* IV1 — пошаговый решатель неравенств с числовой прямой */
html += `<div class="wg" id="p10-iv1">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 1</span><div class="wg-title">Решатель логарифмических неравенств</div></div>
<div class="wg-help">Выбери задачу ползунком и нажимай «Следующий шаг ▶». Особое внимание — ОДЗ и правилу монотонности. После шага «Ответ» появится визуализация на числовой прямой. Просмотри все 5 задач — получишь XP.</div>
<div class="sliders">
<label>Задача № <b id="p10-iv1-n">1</b> / 5<input type="range" id="p10-iv1-sn" min="1" max="5" step="1" value="1"></label>
</div>
<div id="p10-iv1-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.08rem;text-align:center;margin-bottom:10px"></div>
<div id="p10-iv1-steps" style="display:flex;flex-direction:column;gap:8px;margin-bottom:10px"></div>
<div id="p10-iv1-axis" style="display:flex;justify-content:center;margin-bottom:10px"></div>
<div class="actions" style="justify-content:center">
<button class="btn primary" id="p10-iv1-next">Следующий шаг ▶</button>
<button class="btn" id="p10-iv1-all">Показать все шаги</button>
<button class="btn" id="p10-iv1-reset">Скрыть шаги</button>
</div>
</div>`;
/* IV2 — калькулятор log_a (kx + b) ⋚ c */
html += `<div class="wg" id="p10-iv2">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 2</span><div class="wg-title">Калькулятор $\\log_a (kx + b) \\gtrless c$</div></div>
<div class="wg-help">Введи основание $a$, коэффициенты $k$, $b$, правую часть $c$ и выбери знак — калькулятор решит неравенство с учётом монотонности и ОДЗ, покажет числовую прямую.</div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center;margin-bottom:8px">
<span style="font-family:'JetBrains Mono',monospace">$a$ =</span>
<input type="number" id="p10-iv2-a" class="tinp" style="width:70px;text-align:center" value="2" step="0.5">
<span style="font-family:'JetBrains Mono',monospace">$k$ =</span>
<input type="number" id="p10-iv2-k" class="tinp" style="width:70px;text-align:center" value="1" step="1">
<span style="font-family:'JetBrains Mono',monospace">$b$ =</span>
<input type="number" id="p10-iv2-b" class="tinp" style="width:70px;text-align:center" value="-1" step="1">
<select id="p10-iv2-sign" class="tinp" style="width:70px;text-align:center">
<option value="gt">&gt;</option>
<option value="lt">&lt;</option>
<option value="ge">&ge;</option>
<option value="le">&le;</option>
</select>
<span style="font-family:'JetBrains Mono',monospace">$c$ =</span>
<input type="number" id="p10-iv2-c" class="tinp" style="width:70px;text-align:center" value="3" step="1">
<button class="btn primary" id="p10-iv2-go">Решить</button>
</div>
<div style="display:flex;gap:6px;flex-wrap:wrap;justify-content:center;margin-bottom:10px;font-size:.82rem">
<span style="color:var(--muted)">Примеры:</span>
<button class="btn" data-set="2,1,-1,gt,3" style="padding:4px 10px;font-size:.82rem">$\\log_2(x-1)>3$</button>
<button class="btn" data-set="0.333,1,-2,gt,-2" style="padding:4px 10px;font-size:.82rem">$\\log_{1/3}(x-2)>-2$</button>
<button class="btn" data-set="5,2,1,lt,1" style="padding:4px 10px;font-size:.82rem">$\\log_5(2x+1)<1$</button>
<button class="btn" data-set="10,1,1,le,1" style="padding:4px 10px;font-size:.82rem">$\\lg(x+1) \\le 1$</button>
</div>
<div id="p10-iv2-out" style="padding:12px 14px;background:var(--card);border-radius:9px;font-size:.96rem;min-height:60px;line-height:1.8"></div>
<div id="p10-iv2-axis" style="display:flex;justify-content:center;margin-top:10px"></div>
<div class="feedback" id="p10-iv2-fb"></div>
</div>`;
/* IV3 — знак сохраняется или меняется? */
html += `<div class="wg" id="p10-iv3">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 3</span><div class="wg-title">Знак меняется или сохраняется?</div></div>
<div class="wg-help">Дано логарифмическое неравенство. Определи: при переходе к выражениям под логарифмом знак <b>сохраняется</b> ($a > 1$) или <b>меняется</b> ($0 < a < 1$)? 8 заданий.</div>
<div class="score-display"><span>Задача <b id="p10-iv3-i">1</b> / 8</span><span>Очки: <b id="p10-iv3-s">0</b> / 8</span></div>
<div id="p10-iv3-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.08rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div id="p10-iv3-opts" style="display:grid;grid-template-columns:1fr 1fr;gap:10px;max-width:420px;margin:0 auto"></div>
<div class="feedback" id="p10-iv3-fb"></div>
<div class="actions" style="justify-content:center"><button class="btn" id="p10-iv3-restart">Начать заново</button></div>
</div>`;
/* IV4 — тренажёр неравенств */
html += `<div class="wg" id="p10-iv4">
<div class="wg-header"><span class="wg-badge">ИНТЕРАКТИВ 4</span><div class="wg-title">Тренажёр неравенств</div></div>
<div class="wg-help">Реши неравенство и введи <b>число</b> — указанную границу интервала ответа. 6 задач.</div>
<div class="score-display"><span>Задача <b id="p10-iv4-i">1</b> / 6</span><span>Очки: <b id="p10-iv4-s">0</b> / 6</span></div>
<div id="p10-iv4-q" style="padding:14px;background:var(--sec-acc-soft);border-radius:10px;font-size:1.12rem;margin-bottom:10px;text-align:center;min-height:54px"></div>
<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;justify-content:center">
<span style="font-family:'JetBrains Mono',monospace">ответ =</span>
<input type="number" id="p10-iv4-ans" class="tinp" style="width:120px;text-align:center" step="0.01">
<button class="btn primary" id="p10-iv4-go">Проверить</button>
<button class="btn" id="p10-iv4-start">Заново</button>
</div>
<div class="feedback" id="p10-iv4-fb"></div>
</div>`;
html += secNav('p9', 'final3');
html += readButton('p10');
box.innerHTML = html;
renderMath(box);
/* Хелпер: рисует числовую прямую с интервалом */
function drawAxis(lo, hi, segs){
// segs: [{a, b, closedA, closedB, color}]
// a/b могут быть -Infinity / +Infinity
const W = 480, H = 80;
const padL = 40, padR = 40;
const ax = padL, bx = W - padR;
const y0 = 46;
const range = hi - lo;
function xpos(v){
if(v === -Infinity) return ax;
if(v === +Infinity) return bx;
return ax + (v - lo) / range * (bx - ax);
}
let svg = '<svg viewBox="0 0 '+W+' '+H+'" width="100%" style="max-width:480px;background:var(--card);border-radius:8px;border:1px solid var(--border)">';
// ось
svg += '<line x1="'+ax+'" y1="'+y0+'" x2="'+bx+'" y2="'+y0+'" stroke="#475569" stroke-width="1.5"/>';
// стрелки
svg += '<polygon points="'+(bx)+','+y0+' '+(bx-8)+','+(y0-4)+' '+(bx-8)+','+(y0+4)+'" fill="#475569"/>';
svg += '<polygon points="'+(ax)+','+y0+' '+(ax+8)+','+(y0-4)+' '+(ax+8)+','+(y0+4)+'" fill="#475569"/>';
// деления
const ticks = [];
for(let v = Math.ceil(lo); v <= Math.floor(hi); v++) ticks.push(v);
ticks.forEach(v => {
const x = xpos(v);
svg += '<line x1="'+x+'" y1="'+(y0-3)+'" x2="'+x+'" y2="'+(y0+3)+'" stroke="#475569" stroke-width="1"/>';
svg += '<text x="'+x+'" y="'+(y0+18)+'" text-anchor="middle" font-size="11" fill="#475569" font-family="JetBrains Mono,monospace">'+v+'</text>';
});
// интервалы
segs.forEach(s => {
const x1 = xpos(s.a), x2 = xpos(s.b);
const color = s.color || '#7c3aed';
svg += '<line x1="'+x1+'" y1="'+y0+'" x2="'+x2+'" y2="'+y0+'" stroke="'+color+'" stroke-width="5" opacity="0.7"/>';
// концы
if(s.a !== -Infinity){
if(s.closedA) svg += '<circle cx="'+x1+'" cy="'+y0+'" r="5" fill="'+color+'"/>';
else svg += '<circle cx="'+x1+'" cy="'+y0+'" r="5" fill="var(--card)" stroke="'+color+'" stroke-width="2"/>';
}
if(s.b !== +Infinity){
if(s.closedB) svg += '<circle cx="'+x2+'" cy="'+y0+'" r="5" fill="'+color+'"/>';
else svg += '<circle cx="'+x2+'" cy="'+y0+'" r="5" fill="var(--card)" stroke="'+color+'" stroke-width="2"/>';
}
});
svg += '</svg>';
return svg;
}
/* === IV1 — пошаговый решатель неравенств === */
(function(){
const TASKS = [
{
q: '$\\log_2 (x - 1) > 3$',
steps: [
'<b>ОДЗ:</b> $x - 1 > 0 \\Rightarrow x > 1$.',
'Переписываем правую часть: $3 = \\log_2 2^3 = \\log_2 8$. Неравенство: $\\log_2 (x - 1) > \\log_2 8$.',
'Учитываем монотонность: $a = 2 > 1$, знак <b>сохраняется</b>: $x - 1 > 8$.',
'Решаем: $x > 9$.',
'Пересечение с ОДЗ ($x > 1$): $x > 9$.',
'<b>Ответ:</b> $x \\in (9; +\\infty)$.'
],
axis: { lo: 0, hi: 14, segs: [{a:9, b:+Infinity, closedA:false, color:'#7c3aed'}] }
},
{
q: '$\\log_{1/3} (x - 2) > -2$',
steps: [
'<b>ОДЗ:</b> $x - 2 > 0 \\Rightarrow x > 2$.',
'Переписываем: $-2 = \\log_{1/3} (1/3)^{-2} = \\log_{1/3} 9$. Неравенство: $\\log_{1/3} (x - 2) > \\log_{1/3} 9$.',
'Учитываем монотонность: $a = 1/3 < 1$, знак <b>меняется</b>: $x - 2 < 9$.',
'Решаем: $x < 11$.',
'Пересечение с ОДЗ ($x > 2$): $2 < x < 11$.',
'<b>Ответ:</b> $x \\in (2; 11)$.'
],
axis: { lo: 0, hi: 14, segs: [{a:2, b:11, closedA:false, closedB:false, color:'#7c3aed'}] }
},
{
q: '$\\log_5 (2x + 1) < 1$',
steps: [
'<b>ОДЗ:</b> $2x + 1 > 0 \\Rightarrow x > -\\dfrac{1}{2}$.',
'Переписываем: $1 = \\log_5 5$. Неравенство: $\\log_5 (2x + 1) < \\log_5 5$.',
'Учитываем монотонность: $a = 5 > 1$, знак сохраняется: $2x + 1 < 5$.',
'Решаем: $2x < 4 \\Rightarrow x < 2$.',
'Пересечение с ОДЗ ($x > -1/2$): $-\\dfrac{1}{2} < x < 2$.',
'<b>Ответ:</b> $x \\in \\left(-\\dfrac{1}{2};\\,2\\right)$.'
],
axis: { lo: -3, hi: 5, segs: [{a:-0.5, b:2, closedA:false, closedB:false, color:'#7c3aed'}] }
},
{
q: '$\\log_2^2 x - 3 \\log_2 x + 2 \\le 0$',
steps: [
'<b>ОДЗ:</b> $x > 0$.',
'Замена $t = \\log_2 x$: $t^2 - 3t + 2 \\le 0$.',
'Корни уравнения: $t = 1, t = 2$. Парабола ветвями вверх $\\le 0$ <b>между</b> корнями: $1 \\le t \\le 2$.',
'Обратно: $1 \\le \\log_2 x \\le 2 \\Leftrightarrow 2 \\le x \\le 4$.',
'$[2; 4] \\subset (0; +\\infty)$ — ОДЗ выполняется.',
'<b>Ответ:</b> $x \\in [2; 4]$.'
],
axis: { lo: 0, hi: 6, segs: [{a:2, b:4, closedA:true, closedB:true, color:'#7c3aed'}] }
},
{
q: '$\\lg (x + 1) \\le 1$',
steps: [
'<b>ОДЗ:</b> $x + 1 > 0 \\Rightarrow x > -1$.',
'Переписываем: $1 = \\lg 10$. Неравенство: $\\lg (x + 1) \\le \\lg 10$.',
'Учитываем монотонность: $a = 10 > 1$, знак сохраняется: $x + 1 \\le 10$.',
'Решаем: $x \\le 9$.',
'Пересечение с ОДЗ ($x > -1$): $-1 < x \\le 9$.',
'<b>Ответ:</b> $x \\in (-1; 9]$.'
],
axis: { lo: -3, hi: 12, segs: [{a:-1, b:9, closedA:false, closedB:true, color:'#7c3aed'}] }
}
];
const sn = document.getElementById('p10-iv1-sn');
const nL = document.getElementById('p10-iv1-n');
const qEl = document.getElementById('p10-iv1-q');
const stepsEl = document.getElementById('p10-iv1-steps');
const axisEl = document.getElementById('p10-iv1-axis');
const nextBtn = document.getElementById('p10-iv1-next');
const allBtn = document.getElementById('p10-iv1-all');
const resetBtn = document.getElementById('p10-iv1-reset');
const seen = new Set();
let _done = false;
let cur = 0, shown = 0;
function render(){
const t = TASKS[cur];
nL.textContent = (cur + 1);
qEl.innerHTML = t.q;
stepsEl.innerHTML = t.steps.map((s, i) => {
const visible = i < shown;
const isLast = i === t.steps.length - 1 && visible;
const bg = isLast ? '#dcfce7' : 'var(--card)';
const brd = isLast ? '2px solid #16a34a' : '1px solid var(--border)';
return '<div data-i="'+i+'" style="padding:10px 12px;background:'+bg+';border:'+brd+';border-radius:8px;font-size:.95rem;display:'+(visible?'block':'none')+'"><span style="color:var(--muted);font-weight:700;margin-right:6px">Шаг '+(i+1)+':</span>'+s+'</div>';
}).join('');
renderMath(qEl);
renderMath(stepsEl);
if(shown >= t.steps.length){
axisEl.innerHTML = drawAxis(t.axis.lo, t.axis.hi, t.axis.segs);
seen.add(cur);
if(!_done && seen.size >= 5){ _done = true; addXp(10, 'p10-iv1'); bumpProgress('p10', 15); }
} else {
axisEl.innerHTML = '';
}
}
function load(n){ cur = Math.max(0, Math.min(TASKS.length - 1, n)); shown = 1; render(); }
sn.addEventListener('input', () => load((+sn.value) - 1));
nextBtn.addEventListener('click', () => {
if(shown < TASKS[cur].steps.length){ shown++; render(); }
});
allBtn.addEventListener('click', () => { shown = TASKS[cur].steps.length; render(); });
resetBtn.addEventListener('click', () => { shown = 1; render(); });
load(0);
})();
/* === IV2 — калькулятор log_a (kx + b) ⋚ c === */
(function(){
const aI = document.getElementById('p10-iv2-a');
const kI = document.getElementById('p10-iv2-k');
const bI = document.getElementById('p10-iv2-b');
const cI = document.getElementById('p10-iv2-c');
const sSel = document.getElementById('p10-iv2-sign');
const go = document.getElementById('p10-iv2-go');
const out = document.getElementById('p10-iv2-out');
const axisEl = document.getElementById('p10-iv2-axis');
const fb = document.getElementById('p10-iv2-fb');
const used = new Set();
let _done = false;
document.querySelectorAll('#p10-iv2 [data-set]').forEach(btn => {
btn.addEventListener('click', () => {
const v = btn.dataset.set.split(',');
aI.value = v[0]; kI.value = v[1]; bI.value = v[2]; sSel.value = v[3]; cI.value = v[4];
calc();
});
});
function aLabel(a){
if(Math.abs(a - 10) < 1e-9) return '\\lg';
if(Math.abs(a - 1/3) < 1e-3) return '\\log_{1/3}';
if(Math.abs(a - 0.5) < 1e-9) return '\\log_{1/2}';
return '\\log_{' + (+a.toFixed(4)) + '}';
}
function fxLabel(k, b){
let s = '';
if(k === 1) s = 'x';
else if(k === -1) s = '-x';
else s = k + 'x';
if(b > 0) s += ' + ' + b;
else if(b < 0) s += ' - ' + (-b);
return s;
}
function signStr(s){
return {gt:'>', lt:'<', ge:'\\ge', le:'\\le'}[s];
}
function flipSign(s){
return {gt:'lt', lt:'gt', ge:'le', le:'ge'}[s];
}
function isStrict(s){ return s === 'gt' || s === 'lt'; }
function calc(){
const a = parseFloat(aI.value);
const k = parseFloat(kI.value);
const b = parseFloat(bI.value);
const c = parseFloat(cI.value);
const s = sSel.value;
if(![a,k,b,c].every(isFinite)){ feedback(fb, false, '&#10007; Введи все четыре числа.'); return; }
if(a <= 0 || Math.abs(a - 1) < 1e-9){ feedback(fb, false, '&#10007; Основание $a$ должно быть $> 0$ и $\\ne 1$.'); return; }
if(k === 0){ feedback(fb, false, '&#10007; При $k = 0$ выражение под логарифмом не зависит от $x$.'); return; }
const fx = fxLabel(k, b);
const eq = '$' + aLabel(a) + '(' + fx + ') ' + signStr(s) + ' ' + c + '$';
let html = '<div style="font-size:1.04rem;margin-bottom:8px;text-align:center">Неравенство: '+eq+'</div>';
// ОДЗ: kx + b > 0
const odzBound = -b / k;
const odzDir = k > 0 ? 'gt' : 'lt';
const odzStr = k > 0 ? '$x > ' + (+odzBound.toFixed(4)) + '$' : '$x < ' + (+odzBound.toFixed(4)) + '$';
html += '<div style="margin-bottom:6px"><b>Шаг 1. ОДЗ:</b> $' + fx + ' > 0 \\Rightarrow$ ' + odzStr + '.</div>';
// Шаг 2: перевод правой части a^c
const ac = Math.pow(a, c);
html += '<div style="margin-bottom:6px"><b>Шаг 2.</b> Переписываем правую часть: $' + c + ' = ' + aLabel(a) + ' ' + a + '^{' + c + '} = ' + aLabel(a) + ' ' + (+ac.toFixed(4)) + '$.</div>';
// Шаг 3: монотонность
let curSign = s;
let monoText;
if(a > 1){
monoText = '$a = ' + a + ' > 1$ — знак <b>сохраняется</b>';
} else {
monoText = '$0 < a = ' + (+a.toFixed(4)) + ' < 1$ — знак <b>меняется</b>';
curSign = flipSign(curSign);
}
html += '<div style="margin-bottom:6px"><b>Шаг 3.</b> Монотонность: ' + monoText + '. Переход: $' + fx + ' ' + signStr(curSign) + ' ' + (+ac.toFixed(4)) + '$.</div>';
// Шаг 4: решить относительно x
// kx + b [sign] ac => kx [sign] ac - b => x [sign|flip if k<0] (ac - b)/k
const rhs = (ac - b) / k;
let finalSign = curSign;
if(k < 0) finalSign = flipSign(finalSign);
html += '<div style="margin-bottom:6px"><b>Шаг 4.</b> Решаем: $kx ' + signStr(curSign) + ' ' + (+ac.toFixed(4)) + ' - (' + b + ')';
if(k < 0) html += '$, делим на $k = ' + k + ' < 0$ — знак меняется: $x ' + signStr(finalSign) + ' ' + (+rhs.toFixed(4)) + '$.</div>';
else html += ' \\Rightarrow x ' + signStr(finalSign) + ' ' + (+rhs.toFixed(4)) + '$.</div>';
// Шаг 5: пересечь с ОДЗ
// Решение исходного: x [finalSign] rhs. ОДЗ: x [odzDir] odzBound.
// Найдём интервал пересечения
function rangeFromIneq(dir, bound){
// dir in {gt, lt, ge, le}
if(dir === 'gt') return {lo: bound, hi: +Infinity, closedLo: false, closedHi: false};
if(dir === 'ge') return {lo: bound, hi: +Infinity, closedLo: true, closedHi: false};
if(dir === 'lt') return {lo: -Infinity, hi: bound, closedLo: false, closedHi: false};
if(dir === 'le') return {lo: -Infinity, hi: bound, closedLo: false, closedHi: true};
}
const r1 = rangeFromIneq(finalSign, rhs);
const r2 = rangeFromIneq(odzDir, odzBound);
const lo = Math.max(r1.lo, r2.lo);
const hi = Math.min(r1.hi, r2.hi);
const closedLo = (lo === r1.lo ? r1.closedLo : false) && (lo === r2.lo ? r2.closedLo : false) || (lo === r1.lo && lo !== r2.lo && r1.closedLo) || (lo === r2.lo && lo !== r1.lo && r2.closedLo);
// Простая логика: closedLo true только если lo пришёл из границы, которая closed; ОДЗ всегда строгое, так что closedLo возможен только если lo === rhs && finalSign in {ge, le}
let cLo = false, cHi = false;
if(lo === r1.lo) cLo = r1.closedLo;
if(lo === r2.lo) cLo = cLo && r2.closedLo;
if(hi === r1.hi) cHi = r1.closedHi;
if(hi === r2.hi) cHi = cHi && r2.closedHi;
// Однако если lo берётся только из одной стороны (другая = -Inf), то closed зависит только от той стороны.
if(r1.lo === -Infinity) cLo = r2.closedLo;
if(r2.lo === -Infinity) cLo = r1.closedLo;
if(r1.hi === +Infinity) cHi = r2.closedHi;
if(r2.hi === +Infinity) cHi = r1.closedHi;
if(lo >= hi && !(lo === hi && cLo && cHi)){
html += '<div style="margin-bottom:4px;color:#b91c1c"><b>Шаг 5.</b> Пересечение пустое — решений нет.</div>';
html += '<div style="font-weight:700;color:#b91c1c">Ответ: $x \\in \\varnothing$.</div>';
axisEl.innerHTML = '';
} else {
// Сформируем строку интервала
const lp = (lo === -Infinity) ? '(-\\infty' : ((cLo ? '[' : '(') + (+lo.toFixed(4)));
const rp = (hi === +Infinity) ? '+\\infty)' : ((+hi.toFixed(4)) + (cHi ? ']' : ')'));
html += '<div style="margin-bottom:4px;color:#15803d"><b>Шаг 5.</b> Пересечение исходного и ОДЗ: $' + lp + ';\\, ' + rp + '$.</div>';
html += '<div style="font-weight:700;color:#15803d">Ответ: $x \\in ' + lp + ';\\, ' + rp + '$.</div>';
// Визуализация
const lo2 = (lo === -Infinity) ? Math.floor(hi) - 5 : Math.floor(lo) - 2;
const hi2 = (hi === +Infinity) ? Math.ceil(lo) + 5 : Math.ceil(hi) + 2;
const segLo = (lo === -Infinity) ? -Infinity : lo;
const segHi = (hi === +Infinity) ? +Infinity : hi;
axisEl.innerHTML = drawAxis(lo2, hi2, [{a:segLo, b:segHi, closedA:cLo, closedB:cHi, color:'#7c3aed'}]);
}
out.innerHTML = html;
renderMath(out);
feedback(fb, true, '&#10003; Решено.');
used.add(aI.value+','+kI.value+','+bI.value+','+sSel.value+','+cI.value);
if(!_done && used.size >= 4){ _done = true; addXp(10, 'p10-iv2'); bumpProgress('p10', 15); }
}
go.addEventListener('click', calc);
[aI, kI, bI, cI].forEach(i => i.addEventListener('keydown', e => { if(e.key === 'Enter') calc(); }));
sSel.addEventListener('change', calc);
calc();
})();
/* === IV3 — знак сохраняется или меняется? === */
(function(){
const OPTS = ['Сохраняется', 'Меняется'];
const Q = [
{ q: '$\\log_2 (x - 1) > \\log_2 5$', ans: 0, hint: 'Основание $a = 2 > 1$ — функция возрастает, знак сохраняется.' },
{ q: '$\\log_{1/3} (x + 2) < \\log_{1/3} 7$', ans: 1, hint: 'Основание $a = 1/3 < 1$ — функция убывает, знак меняется.' },
{ q: '$\\log_5 x > 2$', ans: 0, hint: 'Перепишем $2 = \\log_5 25$. $a = 5 > 1$ — знак сохраняется.' },
{ q: '$\\log_{0{,}5} (x - 1) \\ge 3$', ans: 1, hint: 'Основание $a = 0{,}5 < 1$ — знак меняется.' },
{ q: '$\\lg (2x) > 1$', ans: 0, hint: '$\\lg = \\log_{10}$, $a = 10 > 1$ — знак сохраняется.' },
{ q: '$\\log_{1/2} x \\le 4$', ans: 1, hint: 'Основание $a = 1/2 < 1$ — знак меняется.' },
{ q: '$\\log_3 (x + 4) > 0$', ans: 0, hint: 'Основание $a = 3 > 1$ — знак сохраняется. (А $0 = \\log_3 1$.)' },
{ q: '$\\log_{0{,}1} x \\ge -1$', ans: 1, hint: 'Основание $a = 0{,}1 < 1$ — знак меняется.' },
];
let i = 0, score = 0;
const qEl = document.getElementById('p10-iv3-q');
const oEl = document.getElementById('p10-iv3-opts');
const fb = document.getElementById('p10-iv3-fb');
const iEl = document.getElementById('p10-iv3-i');
const sEl = document.getElementById('p10-iv3-s');
function show(){
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: '+score+' / '+Q.length;
oEl.innerHTML = '';
if(score === Q.length){ addXp(15, 'p10-iv3'); bumpProgress('p10', 25); }
else if(score >= 6){ addXp(8, 'p10-iv3'); bumpProgress('p10', 15); }
return;
}
iEl.textContent = (i + 1);
sEl.textContent = score;
const item = Q[i];
qEl.innerHTML = item.q;
oEl.innerHTML = OPTS.map((o, k) => '<button class="btn primary" data-k="'+k+'">'+o+'</button>').join('');
fb.style.display = 'none';
renderMath(qEl);
oEl.querySelectorAll('button').forEach(b => {
b.addEventListener('click', () => {
const k = +b.dataset.k;
if(k === item.ans){ score++; feedback(fb, true, '&#10003; Верно! '+item.hint+' Дальше ▶'); }
else feedback(fb, false, '&#10007; Неверно. Правильно: <b>'+OPTS[item.ans]+'</b>. '+item.hint+' Дальше ▶');
sEl.textContent = score;
oEl.querySelectorAll('button').forEach(x => x.disabled = true);
i++;
setTimeout(show, 1700);
});
});
}
document.getElementById('p10-iv3-restart').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
/* === IV4 — тренажёр неравенств === */
(function(){
const Q = [
{ q: '$\\log_2 (x - 1) > 3$ — введи <b>нижнюю</b> границу интервала ответа', ans: 9, hint: 'ОДЗ $x > 1$. $x - 1 > 8 \\Rightarrow x > 9$. Ответ $(9; +\\infty)$, граница — $9$.' },
{ q: '$\\log_{1/3} (x - 2) > -2$ — введи <b>верхнюю</b> границу интервала ответа', ans: 11, hint: 'ОДЗ $x > 2$. $-2 = \\log_{1/3} 9$, знак меняется: $x - 2 < 9 \\Rightarrow x < 11$. Ответ $(2; 11)$, верхняя граница — $11$.' },
{ q: '$\\log_5 x < 2$ — введи <b>верхнюю</b> границу интервала ответа', ans: 25, hint: 'ОДЗ $x > 0$. $2 = \\log_5 25$, знак сохраняется: $x < 25$. Ответ $(0; 25)$, верхняя граница — $25$.' },
{ q: '$\\log_{1/2} (x - 3) \\ge -2$ — введи <b>верхнюю</b> границу интервала ответа', ans: 7, hint: 'ОДЗ $x > 3$. $-2 = \\log_{1/2} 4$, знак меняется: $x - 3 \\le 4 \\Rightarrow x \\le 7$. Ответ $(3; 7]$, верхняя граница — $7$.' },
{ q: '$\\lg x > 1$ — введи <b>нижнюю</b> границу интервала ответа', ans: 10, hint: 'ОДЗ $x > 0$. $1 = \\lg 10$, знак сохраняется: $x > 10$. Ответ $(10; +\\infty)$, граница — $10$.' },
{ q: '$\\log_3 (x + 1) \\le 2$ — введи <b>верхнюю</b> границу интервала ответа', ans: 8, hint: 'ОДЗ $x > -1$. $2 = \\log_3 9$, знак сохраняется: $x + 1 \\le 9 \\Rightarrow x \\le 8$. Ответ $(-1; 8]$, верхняя граница — $8$.' },
];
let i = 0, score = 0;
function show(){
const qEl = document.getElementById('p10-iv4-q');
const iEl = document.getElementById('p10-iv4-i');
const sEl = document.getElementById('p10-iv4-s');
const fb = document.getElementById('p10-iv4-fb');
const ansI = document.getElementById('p10-iv4-ans');
if(i >= Q.length){
qEl.innerHTML = '<b>Готово!</b> Результат: '+score+' / '+Q.length;
if(score === Q.length){ addXp(15, 'p10-iv4'); bumpProgress('p10', 25); }
else if(score >= 4){ addXp(8, 'p10-iv4'); bumpProgress('p10', 15); }
return;
}
iEl.textContent = (i + 1);
sEl.textContent = score;
qEl.innerHTML = Q[i].q;
ansI.value = '';
renderMath(qEl);
fb.style.display = 'none';
setTimeout(() => ansI.focus(), 30);
}
function go(){
if(i >= Q.length) return;
const fb = document.getElementById('p10-iv4-fb');
const raw = document.getElementById('p10-iv4-ans').value.replace(',', '.');
const ans = parseFloat(raw);
if(isNaN(ans)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(ans - Q[i].ans) < 0.05){
score++;
feedback(fb, true, '&#10003; Верно! '+Q[i].hint+' Дальше ▶');
} else {
feedback(fb, false, '&#10007; Неверно. Ответ: $'+Q[i].ans+'$. '+Q[i].hint+' Дальше ▶');
}
document.getElementById('p10-iv4-s').textContent = score;
i++;
setTimeout(show, 1700);
}
document.getElementById('p10-iv4-go').addEventListener('click', go);
document.getElementById('p10-iv4-ans').addEventListener('keydown', e => { if(e.key === 'Enter') go(); });
document.getElementById('p10-iv4-start').addEventListener('click', () => { i = 0; score = 0; show(); });
show();
})();
wireReadBtn('p10');
}
function buildFinal3(){
const box = document.getElementById('final3-body');
let html = '';
/* Часть А — Шпаргалка главы 3 (4 mini-карточки) */
html += `<div class="card">
<div class="card-header">
<div class="card-icon theory">${ICONS.theory}</div>
<div class="card-title">Шпаргалка главы 3</div>
<div class="card-num">Итог</div>
</div>
<div class="card-body">
<p>Ключевые формулы и идеи всех четырёх параграфов в одном месте — просмотри перед битвой с боссами.</p>
<div style="display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:12px;margin-top:10px">
<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
<svg viewBox="0 0 24 24" fill="none" stroke="#7c3aed" stroke-width="2" style="width:18px;height:18px"><path d="M12 3v18"/><path d="M3 12h18"/><circle cx="12" cy="12" r="9"/></svg>
<div style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 7 · Свойства логарифмов</div>
</div>
<div style="font-size:.95rem">$\\log_a (bc) = \\log_a b + \\log_a c$, $\\;\\log_a \\dfrac{b}{c} = \\log_a b - \\log_a c$, $\\;\\log_a b^n = n \\log_a b$, $\\;\\log_a b = \\dfrac{\\log_c b}{\\log_c a}$.</div>
</div>
<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
<svg viewBox="0 0 24 24" fill="none" stroke="#6d28d9" stroke-width="2" style="width:18px;height:18px"><path d="M3 21c4-12 8-18 18-18"/><line x1="3" y1="21" x2="21" y2="21"/></svg>
<div style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 8 · Логарифмическая функция</div>
</div>
<div style="font-size:.95rem">$y = \\log_a x$. $D = (0; +\\infty)$, $E = \\mathbb{R}$. При $a > 1$ — возрастает, при $0 < a < 1$ — убывает. Точка $(1; 0)$. Асимптота $x = 0$. Обратная к $y = a^x$.</div>
</div>
<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
<svg viewBox="0 0 24 24" fill="none" stroke="#0891b2" stroke-width="2" style="width:18px;height:18px"><path d="M5 12h14"/><polyline points="12 5 19 12 12 19"/></svg>
<div style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 9 · Логарифм. уравнения</div>
</div>
<div style="font-size:.95rem">$\\log_a f = \\log_a g \\Rightarrow f = g$ + ОДЗ ($f, g > 0$). Методы: потенциирование, замена, свойства, графический. <b>Проверять корни!</b></div>
</div>
<div style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:11px;border-left:3px solid var(--pri)">
<div style="display:flex;align-items:center;gap:8px;margin-bottom:6px">
<svg viewBox="0 0 24 24" fill="none" stroke="#f59e0b" stroke-width="2" style="width:18px;height:18px"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="18" x2="21" y2="18"/><polyline points="7 10 3 14 7 18"/></svg>
<div style="font-family:'Unbounded',sans-serif;font-weight:700;color:var(--pri2);font-size:.92rem">§ 10 · Логарифм. неравенства</div>
</div>
<div style="font-size:.95rem">При $a > 1$ знак <b>сохраняется</b>, при $0 < a < 1$ — <b>меняется</b>. ВСЕГДА учитываем ОДЗ: всё под $\\log$ должно быть строго положительно.</div>
</div>
</div>
</div>
</div>`;
/* Часть Б — 5 боссов (intro) */
html += `<div class="card">
<div class="card-header">
<div class="card-icon rule">${ICONS.rule}</div>
<div class="card-title">Боссы главы 3</div>
<div class="card-num">5</div>
</div>
<div class="card-body">
<p>5 интегрированных задач по всей главе. За каждого побеждённого босса: <b>+10 XP, +18% к прогрессу</b>. Победишь всех — ачивка <b>«Магистр логарифмической функции»</b> и <b>+50 XP бонус</b>.</p>
</div>
</div>`;
html += '<div id="ch3-bosses-container"></div>';
html += `<div style="margin-top:18px;padding:18px 20px;background:linear-gradient(135deg,var(--pri-soft),var(--sec-acc-soft));border-radius:14px;border:1.5px solid var(--pri);text-align:center" id="ch3-final-summary">
<div style="font-family:'Unbounded',sans-serif;font-weight:800;color:var(--pri2);font-size:1.1rem;margin-bottom:6px">Прогресс по боссам</div>
<div id="ch3-boss-overall" style="font-size:.95rem;color:var(--text);margin-bottom:10px">0 / 5 боссов побеждено</div>
<div style="height:12px;background:var(--card);border-radius:8px;overflow:hidden;border:1px solid var(--border)">
<div id="ch3-boss-overall-fill" style="height:100%;width:0%;background:linear-gradient(90deg,#7c3aed,#a78bfa);transition:width .35s"></div>
</div>
<div id="ch3-final-reward" style="margin-top:14px;display:none;padding:14px;background:var(--card);border-radius:11px;border:2px solid #7c3aed">
<div style="font-family:'Unbounded',sans-serif;font-weight:800;color:#6d28d9;font-size:1.05rem;margin-bottom:6px">Магистр логарифмической функции</div>
<div style="font-size:.92rem;margin-bottom:10px">Все 3 главы изучены. Готовы к финалу курса! +50 XP бонус.</div>
<a class="btn primary" href="/textbook/algebra-11" style="text-decoration:none">К хабу Алгебры 11 <svg class="ic" viewBox="0 0 24 24"><polyline points="9 18 15 12 9 6"/></svg></a>
</div>
</div>`;
html += secNav('p10', null);
box.innerHTML = html;
renderMath(box);
/* Боссы */
const BOSSES = [
{
n:1, color:'#10b981',
title:'Циклоп Свойств',
tag:'§ 7',
q:'Вычислите: $\\log_2 24 - \\log_2 3 - \\log_2 \\sqrt{16}$. Введите числовой ответ.',
ans:1,
hint:'$\\log_2 24 - \\log_2 3 = \\log_2 \\dfrac{24}{3} = \\log_2 8 = 3$. $\\;\\log_2 \\sqrt{16} = \\log_2 4 = 2$. Итог: $3 - 2 = 1$.'
},
{
n:2, color:'#0891b2',
title:'Минотавр Логарифмической',
tag:'§ 8',
q:'У функции $y = \\log_{1/3} x$ найдите $y(81)$.',
ans:-4,
hint:'$\\log_{1/3} 81 = \\log_{1/3} (1/3)^{-4} = -4$ (т.к. $(1/3)^{-4} = 3^4 = 81$).'
},
{
n:3, color:'#7c3aed',
title:'Гарпия Уравнений',
tag:'§ 9',
q:'Решите $\\log_3 (x^2 - 2x) = \\log_3 3$. Найдите <b>сумму корней</b>.',
ans:2,
hint:'Потенциируем: $x^2 - 2x = 3 \\Rightarrow (x-3)(x+1) = 0 \\Rightarrow x = 3, x = -1$. ОДЗ $x^2 - 2x > 0$: $x < 0$ или $x > 2$. Оба корня подходят. Сумма $= 3 + (-1) = 2$.'
},
{
n:4, color:'#dc2626',
title:'Дракон Неравенств',
tag:'§ 10',
q:'Решите $\\log_{1/2} (x - 3) \\ge -2$. Найдите <b>наибольшее целое</b> $x$ из решения.',
ans:7,
hint:'ОДЗ $x > 3$. $-2 = \\log_{1/2} 4$, $a < 1$ — знак меняется: $x - 3 \\le 4 \\Rightarrow x \\le 7$. Решение $(3; 7]$, наибольшее целое — $7$.'
},
{
n:5, color:'#f59e0b',
title:'Мастер Логарифмов',
tag:'§ 7 + § 8 + § 9 + § 10',
q:'Решите $\\log_2^2 x - \\log_2 x = 2$. Найдите <b>произведение корней</b>.',
ans:2,
hint:'Замена $t = \\log_2 x$: $t^2 - t - 2 = 0 \\Rightarrow t = 2$ или $t = -1$. $x_1 = 2^2 = 4$, $x_2 = 2^{-1} = 1/2$. Произведение: $4 \\cdot 1/2 = 2$.'
},
];
const cont = document.getElementById('ch3-bosses-container');
const STATE_KEY = 'algebra11_ch3_bosses';
const BOSS_STATE = (function(){
try{ const s = localStorage.getItem(STATE_KEY); if(s){ const p = JSON.parse(s); if(Array.isArray(p) && p.length === BOSSES.length) return p; } }catch(e){}
return BOSSES.map(()=>({defeated:false}));
})();
function saveBosses(){ try{ localStorage.setItem(STATE_KEY, JSON.stringify(BOSS_STATE)); }catch(e){} }
cont.innerHTML = BOSSES.map((b, idx)=>{
return '<div class="boss-card" id="boss3-'+b.n+'-card" style="padding:16px;background:var(--card);border-radius:12px;border:2px solid '+b.color+';margin-bottom:14px">'
+'<div style="display:flex;align-items:center;gap:10px;margin-bottom:10px;flex-wrap:wrap">'
+'<svg viewBox="0 0 24 24" fill="none" stroke="'+b.color+'" stroke-width="2.2" style="width:28px;height:28px;flex-shrink:0"><polygon points="12,2 22,20 2,20"/></svg>'
+'<div style="font-family:\'Unbounded\',sans-serif;font-weight:800;color:'+b.color+';font-size:1.05rem">Босс '+b.n+': '+b.title+'</div>'
+'<div style="margin-left:auto;font-size:.78rem;color:var(--muted);padding:3px 8px;background:var(--sec-acc-soft);border-radius:6px">'+b.tag+'</div>'
+'</div>'
+'<div class="boss-q" id="boss3-'+b.n+'-q" style="padding:12px 14px;background:var(--sec-acc-soft);border-radius:9px;font-size:1rem;line-height:1.5;margin-bottom:10px">'+b.q+'</div>'
+'<div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">'
+'<span style="font-family:\'JetBrains Mono\',monospace;font-size:.92rem">ответ =</span>'
+'<input type="number" id="boss3-'+b.n+'-ans" class="tinp" style="width:120px;text-align:center" step="0.01" placeholder="число">'
+'<button class="btn primary" id="boss3-'+b.n+'-go" style="background:'+b.color+';border-color:'+b.color+'">Атаковать</button>'
+'<button class="btn" id="boss3-'+b.n+'-hint">Подсказка</button>'
+'</div>'
+'<div class="feedback" id="boss3-'+b.n+'-fb"></div>'
+'</div>';
}).join('');
renderMath(cont);
function refreshOverall(){
const won = BOSS_STATE.filter(s => s.defeated).length;
const txt = document.getElementById('ch3-boss-overall');
const fill = document.getElementById('ch3-boss-overall-fill');
if(txt) txt.textContent = won + ' / ' + BOSSES.length + ' боссов побеждено';
if(fill) fill.style.width = (won * 100 / BOSSES.length) + '%';
if(won >= BOSSES.length){
const reward = document.getElementById('ch3-final-reward');
if(reward && reward.style.display === 'none'){
reward.style.display = 'block';
if(!STATE.achievements.has('ch3_done')){
achievement('ch3_done','Магистр логарифмической функции');
addXp(50, 'ch3-bonus');
bumpProgress('final3', 30);
if(window.confetti){ try{ confetti(); }catch(e){} }
}
}
}
}
BOSSES.forEach((b, idx)=>{
const card = document.getElementById('boss3-'+b.n+'-card');
const goBtn = document.getElementById('boss3-'+b.n+'-go');
const hintBtn = document.getElementById('boss3-'+b.n+'-hint');
const ansInp = document.getElementById('boss3-'+b.n+'-ans');
if(BOSS_STATE[idx].defeated){
card.style.background = 'linear-gradient(135deg,var(--sec-acc-soft),var(--pri-soft))';
card.classList.add('glow');
goBtn.disabled = true; goBtn.style.opacity = .55; goBtn.innerHTML = '&#10003; Повержен';
ansInp.disabled = true;
}
goBtn.addEventListener('click', ()=>{
if(BOSS_STATE[idx].defeated) return;
const fb = document.getElementById('boss3-'+b.n+'-fb');
const raw = ansInp.value.replace(',', '.');
const val = parseFloat(raw);
if(isNaN(val)){ feedback(fb, false, '&#10007; Введи число.'); return; }
if(Math.abs(val - b.ans) < 0.05){
BOSS_STATE[idx].defeated = true; saveBosses();
feedback(fb, true, '&#10003; Босс '+b.n+' повержен! +10 XP. '+b.hint);
addXp(10, 'boss-ch3-'+b.n);
bumpProgress('final3', 18);
goBtn.disabled = true; goBtn.style.opacity = .55; goBtn.innerHTML = '&#10003; Повержен';
ansInp.disabled = true;
card.style.background = 'linear-gradient(135deg,var(--sec-acc-soft),var(--pri-soft))';
card.classList.add('glow','pulse');
setTimeout(()=>card.classList.remove('pulse'), 900);
refreshOverall();
} else {
feedback(fb, false, '&#10007; Промах. Попробуй ещё. Подсказка доступна.');
}
});
hintBtn.addEventListener('click', ()=>{
const fb = document.getElementById('boss3-'+b.n+'-fb');
fb.className = 'feedback ok';
fb.innerHTML = '<b>Подсказка:</b> '+b.hint;
fb.style.display = 'block';
fb.style.background = 'var(--warn-bg)';
fb.style.color = '#92400e';
fb.style.borderLeftColor = 'var(--warn)';
renderMath(fb);
});
ansInp.addEventListener('keydown', e=>{ if(e.key === 'Enter') goBtn.click(); });
});
refreshOverall();
}
/* ===== Search ===== */
const SEARCH_INDEX = (function(){
const arr=[];
PARAS.forEach(p=>arr.push({kind:'Параграф',title:p.num+' '+p.name,desc:p.sub||'',sec:p.id}));
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); }
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(); initSearch();
buildParaSelector(); refreshProgressUI(); loadServerReadState(); goTo(PARAS[0].id);
setTimeout(()=>achievement('start'), 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);
/* === ALG11 POLISH JS === */
(function(){
function bumpScore(el){
if(!el) return;
el.classList.remove('bump');
void el.offsetWidth;
el.classList.add('bump');
setTimeout(function(){ try{ el.classList.remove('bump'); }catch(e){} }, 270);
}
window.__alg11BumpScore = bumpScore;
function observeScores(root){
root = root || document;
var nodes = root.querySelectorAll('.score-display b');
nodes.forEach(function(b){
if(b.__scoreObs) return;
b.__scoreObs = true;
var last = b.textContent;
try{
var mo = new MutationObserver(function(){
var nv = b.textContent;
if(nv !== last){ last = nv; bumpScore(b); }
});
mo.observe(b, {childList:true, characterData:true, subtree:true});
}catch(e){}
});
}
function rescanScores(){ try{ observeScores(document); }catch(e){} }
if(document.readyState === 'loading') document.addEventListener('DOMContentLoaded', rescanScores);
else rescanScores();
try{
var rootObs = new MutationObserver(function(muts){
var need = false;
for(var i=0;i<muts.length && !need;i++){
var m = muts[i];
for(var j=0;j<m.addedNodes.length;j++){
var n = m.addedNodes[j];
if(n.nodeType===1){
if(n.classList && n.classList.contains('score-display')) { need = true; break; }
if(n.querySelector && n.querySelector('.score-display b')) { need = true; break; }
}
}
}
if(need) rescanScores();
});
rootObs.observe(document.body, {childList:true, subtree:true});
}catch(e){}
function refreshDoneMarks(){
try{
if(typeof STATE === 'undefined' || !STATE.progress) return;
document.querySelectorAll('.psel-card').forEach(function(c){
var id = c.dataset.id || c.dataset.progCard;
if(!id) return;
var pct = +STATE.progress[id] || 0;
if(!c.querySelector('.psel-done')){
var s = document.createElement('span');
s.className = 'psel-done';
s.setAttribute('title','Прочитано');
s.innerHTML = '<svg viewBox="0 0 24 24"><polyline points="20 6 9 17 4 12"/></svg>';
c.appendChild(s);
}
c.classList.toggle('done', pct >= 50);
});
}catch(e){}
}
try{
if(typeof window.refreshProgressUI === 'function'){
var _origRP = window.refreshProgressUI;
window.refreshProgressUI = function(){ var r = _origRP.apply(this, arguments); setTimeout(refreshDoneMarks, 0); return r; };
}
}catch(e){}
setTimeout(refreshDoneMarks, 600);
setTimeout(refreshDoneMarks, 1800);
window.addEventListener('focus', function(){ setTimeout(refreshDoneMarks, 200); });
document.addEventListener('click', function(e){
var card = e.target.closest && e.target.closest('.psel-card');
if(!card) return;
var id = card.dataset.id;
if(!id) return;
setTimeout(function(){
var sec = document.getElementById('sec-' + id);
if(sec) try{ sec.scrollIntoView({behavior:'smooth', block:'start'}); }catch(e){}
}, 60);
});
})();
</script>
</body>
</html>