@@ -7,61 +7,200 @@
< meta http-equiv = "Expires" content = "0" >
< meta name = "viewport" content = "width=device-width,initial-scale=1.0" >
< title > Химия 8 · Вводный раздел · «Количественные понятия в химии»< / title >
< link href = "https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800;900&family=Inter:wght@400;500;600;700&family=Unbounded:wght@700;800;900&display=swap" rel = "stylesheet" >
< link href = "https://fonts.googleapis.com/css2?family=Outfit:wght@400;500;600;700;800;900&family=Inter:wght@400;500;600;700&family=Unbounded:wght@700;800;900&family=JetBrains+Mono:wght@500;700& display=swap" rel = "stylesheet" >
< 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" > < / 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}],throwOnError:false})" > < / script >
< script src = "/js/api.js" defer > < / script >
< script src = "/js/xp.js" defer > < / script >
< script src = "/js/biochem-core.js" defer > < / script >
< script src = "/js/chem8_svg.js" defer > < / script >
< style >
: root {
--bg : #fffbeb ; --card : #fff ; --text : #1c1917 ; --muted : #78716c ; --border : #e7e5e4 ;
--bg : #fffbeb ; --card : #fff ; --card-soft : #fef9ec ; --text : #1c1917 ; --muted : #78716c ; --border : #f0e6cf ;
--pri : #d97706 ; --pri-d : #b45309 ; --pri-l : #fbbf24 ; --pri-soft : #fef3c7 ;
--sh : 0 4 px 16 px rgba ( 0 , 0 , 0 , .06 ) ; --sh-h : 0 12 px 32 px rgba ( 0 , 0 , 0 , .12 ) ;
--ok : #15803d ; --ok-bg : #dcfce7 ; --bad : #b91c1c ; --bad-bg : #fee2e2 ; --warn : #b45309 ; --warn-bg : #fef3c7 ;
--sh : 0 1 px 3 px rgba ( 120 , 80 , 10 , .07 ) ; --sh2 : 0 8 px 28 px rgba ( 120 , 80 , 10 , .13 ) ;
--mono : 'JetBrains Mono' , ui-monospace , monospace ;
}
html . dark { --bg : #1c1410 ; --card : #271c14 ; --text : #fef3c7 ; --muted : #a8a29e ; --border : #3 a3026 ; --pri-soft : rgba ( 0 , 0 , 0 , .2 ) ; }
* { margin : 0 ; padding : 0 ; box-sizing : border-box }
html . dark { --bg : #1c1410 ; --card : #271c14 ; --card-soft : #2e2118 ; --text : #fef3c7 ; --muted : #c9ab82 ; --border : #4 a3520 ;
--pri-soft : rgba ( 217 , 119 , 6 , .18 ) ; --ok-bg : rgba ( 21 , 128 , 61 , .2 ) ; --bad-bg : rgba ( 185 , 28 , 28 , .2 ) ; --warn-bg : rgba ( 180 , 83 , 9 , .2 ) ; }
* { margin : 0 ; padding : 0 ; box-sizing : border-box ; -webkit- tap-highlight-color : transparent }
html , body { min-height : 100 vh }
body { font-family : 'Inter' , system-ui , sans-serif ; background : var ( - - bg ) ; color : var ( - - text ) ; line-height : 1.55 ; transition : background .25 s , color .25 s }
. ic { width : 16 px ; height : 16 px ; stroke : currentColor ; fill : none ; stroke-width : 2 ; stroke-linecap : round ; stroke-linejoin : round }
body { font-family : 'Inter' , system-ui , sans-serif ; background : var ( - - bg ) ; color : var ( - - text ) ; line-height : 1.6 ; transition : background .25 s , color .25 s }
a { color : inherit }
. ic { width : 16 px ; height : 16 px ; stroke : currentColor ; fill : none ; stroke-width : 2 ; stroke-linecap : round ; stroke-linejoin : round ; flex-shrink : 0 }
. hdr { position : relative ; background : linear-gradient ( 110 deg , #b45309 0 % , #d97706 55 % , #fbbf24 100 % ) ; color : #fff ; padding : 34 px 24 px 30 px ; overflow : hidden ; border-bottom : 2 px solid rgba ( 255 , 255 , 255 , .18 ) }
. hdr :: before { content : 'ВВОДНЫЙ РАЗДЕЛ' ; position : absolute ; right : -12 px ; top : 50 % ; transform : translateY ( -50 % ) ; font-family : 'Unbounded' , sans-serif ; font-size : clamp ( 4 rem , 13 vw , 10 rem ) ; font-weight : 900 ; letter-spacing : -.04 em ; color : transparent ; -webkit- text-stroke : 1.5 px rgba ( 255 , 255 , 255 , .13 ) ; line-height : 1 ; pointer-events : none ; user-select : none ; z-index : 0 }
. hdr-inner { position : relative ; z-index : 1 ; max-width : 1000 px ; margin : 0 auto ; display : flex ; align-items : center ; gap : 16 px ; flex-wrap : wrap }
/* HEADER */
. hdr { position : relative ; background : linear-gradient ( 110 deg , #92400e 0 % , #d97706 55 % , #fbbf24 100 % ) ; color : #fff ; padding : 34 px 24 px 30 px ; overflow : hidden ; border-bottom : 2 px solid rgba ( 255 , 255 , 255 , .18 ) }
. hdr :: before { content : 'ВВОДНЫЙ РАЗДЕЛ' ; position : absolute ; right : -12 px ; top : 50 % ; transform : translateY ( -50 % ) ; font-family : 'Unbounded' , sans-serif ; font-size : clamp ( 3 rem , 9 vw , 7 rem ) ; font-weight : 900 ; letter-spacing : -.04 em ; color : transparent ; -webkit- text-stroke : 1.5 px rgba ( 255 , 255 , 255 , .12 ) ; line-height : 1 ; pointer-events : none ; user-select : none ; z-index : 0 ; white-space : nowrap }
. hdr-inner { position : relative ; z-index : 1 ; max-width : 1180 px ; margin : 0 auto ; display : flex ; align-items : center ; gap : 16 px ; flex-wrap : wrap }
. hdr-back { display : inline-flex ; align-items : center ; gap : 8 px ; padding : 8 px 14 px ; background : rgba ( 255 , 255 , 255 , .16 ) ; border-radius : 9 px ; color : #fff ; text-decoration : none ; font-size : .85 rem ; font-weight : 600 ; transition : background .15 s }
. hdr-back : hover { background : rgba ( 255 , 255 , 255 , .26 ) }
. hdr-kicker { font-size : .72 rem ; font-weight : 800 ; text-transform : uppercase ; letter-spacing : .14 em ; opacity : .85 }
. hdr h1 { font-family : 'Outfit' , sans-serif ; font-size : 1.55 rem ; font-weight : 900 ; letter-spacing : -.01 em ; line-height : 1.25 ; margin-top : 3 px }
. hdr-side { margin-left : auto }
. hdr h1 { font-family : 'Outfit' , sans-serif ; font-size : 1.5 rem ; font-weight : 900 ; letter-spacing : -.01 em ; line-height : 1.25 ; margin-top : 3 px }
. hdr-side { margin-left : auto ; display : flex ; gap : 8 px }
. hdr-btn { padding : 8 px 12 px ; background : rgba ( 255 , 255 , 255 , .16 ) ; border : none ; color : #fff ; border-radius : 9 px ; cursor : pointer ; font-weight : 600 ; font-size : .82 rem ; display : inline-flex ; align-items : center ; gap : 6 px ; transition : background .15 s ; font-family : inherit }
. hdr-btn : hover { background : rgba ( 255 , 255 , 255 , .26 ) }
main { max-width : 1000 px ; margin : 0 auto ; padding : 28 px 24 px 60 px }
/* HERO */
. hero { max-width : 1180 px ; margin : 18 px auto 0 ; padding : 0 24 px }
. hero-card { background : linear-gradient ( 135 deg , var ( - - pri - soft ) , rgba ( 251 , 191 , 36 , .1 ) ) ; border : 1 px solid var ( - - border ) ; border-radius : 16 px ; padding : 16 px 20 px ; display : flex ; gap : 16 px ; align-items : center ; flex-wrap : wrap }
. hero-ic { width : 46 px ; height : 46 px ; border-radius : 12 px ; background : linear-gradient ( 135 deg , #d97706 , #fbbf24 ) ; color : #fff ; display : flex ; align-items : center ; justify-content : center ; flex-shrink : 0 ; font-family : 'Outfit' ; font-weight : 900 ; font-size : 1.2 rem }
. hero-t { flex : 1 ; min-width : 180 px }
. hero-lab { font-size : .74 rem ; font-weight : 700 ; color : var ( - - muted ) ; text-transform : uppercase ; letter-spacing : .06 em }
. hero-bar { height : 8 px ; background : rgba ( 217 , 119 , 6 , .16 ) ; border-radius : 5 px ; overflow : hidden ; margin-top : 6 px }
. hero-fill { height : 100 % ; background : linear-gradient ( 90 deg , var ( - - pri ) , var ( - - pri - l ) ) ; border-radius : 5 px ; width : 0 ; transition : width .5 s }
. hero-xp { display : inline-flex ; align-items : center ; gap : 6 px ; padding : 6 px 14 px ; background : linear-gradient ( 135 deg , #f59e0b , var ( - - pri ) ) ; color : #fff ; border-radius : 99 px ; font-size : .8 rem ; font-weight : 800 ; font-family : 'Unbounded' }
. wip { display : flex ; gap : 14 px ; align-items : flex-start ; background : linear-gradient ( 135 deg , var ( - - pri - soft ) , rgba ( 0 , 0 , 0 , .02 ) ) ; border : 1.5 px dashed var ( - - pri ) ; border-radius : 16 px ; padding : 18 px 20 px ; margin-bottom : 26 px }
. wip-ic { width : 42 px ; height : 42 px ; border-radius : 11 px ; background : var ( - - pri ) ; color : #fff ; display : flex ; align-items : center ; justify-content : center ; flex-shrink : 0 }
. wip-ic svg { width : 22 px ; height : 22 px ; stroke : #fff ; fill : none ; stroke-width : 2 ; stroke-linecap : round ; stroke-linejoin : round }
. wip h2 { font-family : 'Outfit' , sans-serif ; font-size : 1.05 rem ; color : var ( - - pri - d ) ; margin-bottom : 4 px }
html . dark . wip h2 { color : var ( - - pri - l ) }
. wip p { font-size : .9 rem ; color : var ( - - muted ) ; line-height : 1.55 }
/* LAYOUT */
. wrap { max-width : 1180 px ; margin : 0 auto ; padding : 24 px ; display : grid ; grid-template-columns : 240 px 1 fr ; gap : 26 px ; align-items : start }
@ media ( max-width : 900px ) { . wrap { grid-template-columns : 1 fr ; padding : 16 px } }
. side { position : sticky ; top : 14 px ; background : var ( - - card ) ; border : 1 px solid var ( - - border ) ; border-radius : 14 px ; padding : 10 px ; box-shadow : var ( - - sh ) ; max-height : calc ( 100 vh - 28 px ) ; overflow : auto }
@ media ( max-width : 900px ) { . side { position : static ; max-height : none } }
. side-h { font-size : .7 rem ; font-weight : 800 ; text-transform : uppercase ; letter-spacing : .08 em ; color : var ( - - muted ) ; padding : 6 px 10 px }
. side a { display : flex ; gap : 9 px ; align-items : center ; padding : 8 px 10 px ; border-radius : 9 px ; text-decoration : none ; font-size : .86 rem ; color : var ( - - text ) ; transition : background .14 s }
. side a : hover { background : var ( - - pri - soft ) }
. side a . active { background : var ( - - pri - soft ) ; color : var ( - - pri - d ) ; font-weight : 700 }
. side a . note { color : var ( - - muted ) ; font-size : .8 rem }
. side-num { font-weight : 800 ; color : var ( - - pri ) ; min-width : 30 px ; font-size : .82 rem }
. side a . done . side-num :: after { content : '\2713' ; margin-left : 3 px ; color : var ( - - ok ) }
. ol-title { font-family : 'Outfit' , sans-serif ; font-size : 1.15 rem ; font-weight : 800 ; margin : 6 px 0 14 px ; display : flex ; align-items : center ; gap : 9 px }
. ol-title svg { width : 20 px ; height : 20 px ; stroke : var ( - - pri ) ; fill : none ; stroke-width : 2 ; stroke-linecap : round ; stroke-linejoin : round }
. ol-list { list-style : none ; background : var ( - - card ) ; border : 1 px solid var ( - - border ) ; border-radius : 14 px ; overflow : hidden ; box-shadow : var ( - - sh ) }
. ol-para , . ol-note { display : flex ; gap : 12 px ; align-items : baseline ; padding : 12 px 18 px ; border-bottom : 1 px solid var ( - - border ) }
. ol-list li : last-child { border-bottom : 0 }
. ol-num { flex-shrink : 0 ; min-width : 46 px ; font-weight : 800 ; color : var ( - - pri ) ; font-size : .92 rem }
. ol-name { font-size : .94 rem ; color : var ( - - text ) }
. ol-note { background : var ( - - pri - soft ) ; align-items : center ; gap : 10 px }
. ol-note-ic { display : inline-flex ; color : var ( - - pri - d ) }
html . dark . ol-note-ic { color : var ( - - pri - l ) }
. ol-note-ic svg { width : 16 px ; height : 16 px ; stroke : currentColor ; fill : none ; stroke-width : 2 ; stroke-linecap : round ; stroke-linejoin : round }
. ol-note span : last-child { font-size : .88 rem ; font-weight : 600 ; color : var ( - - pri - d ) }
html . dark . ol-note span : last-child { color : var ( - - pri - l ) }
/* SECTIONS */
. col { min-width : 0 ; display : flex ; flex-direction : column ; gap : 26 px }
. sec { scroll-margin-top : 14 px }
. ph { border-radius : 16 px ; padding : 20 px 22 px ; color : #fff ; position : relative ; overflow : hidden ; background : linear-gradient ( 135 deg , var ( - - pri - d ) , var ( - - pri ) 60 % , var ( - - pri - l ) ) }
. ph : :after { content : '' ; position : absolute ; right : -30 px ; top : -30 px ; width : 150 px ; height : 150 px ; border-radius : 50 % ; background : rgba ( 255 , 255 , 255 , .1 ) }
. ph-num { font-size : .72 rem ; font-weight : 800 ; letter-spacing : .1 em ; text-transform : uppercase ; opacity : .85 }
. ph h2 { font-family : 'Outfit' ; font-size : 1.22 rem ; font-weight : 800 ; margin : 4 px 0 8 px ; line-height : 1.25 ; position : relative ; z-index : 1 }
. ph-key { display : inline-block ; background : rgba ( 255 , 255 , 255 , .18 ) ; border : 1 px solid rgba ( 255 , 255 , 255 , .25 ) ; border-radius : 10 px ; padding : 6 px 14 px ; font-size : .95 rem ; font-weight : 700 ; position : relative ; z-index : 1 }
. foot { text-align : center ; padding : 24 px 16 px ; color : var ( - - muted ) ; font-size : .78 rem ; border-top : 1 px solid var ( - - border ) ; margin-top : 30 px }
. card { background : var ( - - card ) ; border : 1 px solid var ( - - border ) ; border-radius : 14 px ; padding : 16 px 18 px ; box-shadow : var ( - - sh ) }
. card + . card { margin-top : 14 px }
. card h3 { font-family : 'Outfit' ; font-size : 1 rem ; font-weight : 800 ; margin-bottom : 8 px ; display : flex ; align-items : center ; gap : 8 px }
. card h3 . tg { font-size : .64 rem ; font-weight : 800 ; text-transform : uppercase ; letter-spacing : .05 em ; padding : 2 px 8 px ; border-radius : 99 px ; background : var ( - - pri - soft ) ; color : var ( - - pri - d ) }
. card p { font-size : .94 rem ; margin-bottom : 8 px }
. card p : last-child { margin-bottom : 0 }
. card ul { margin : 6 px 0 8 px 20 px ; font-size : .92 rem }
. card li { margin-bottom : 4 px }
. def { background : var ( - - pri - soft ) ; border-left : 4 px solid var ( - - pri ) ; border-radius : 0 10 px 10 px 0 ; padding : 11 px 14 px ; font-size : .94 rem ; margin : 8 px 0 }
. def b { color : var ( - - pri - d ) }
html . dark . def b { color : var ( - - pri - l ) }
. exa { background : var ( - - card - soft ) ; border : 1 px dashed var ( - - border ) ; border-radius : 10 px ; padding : 12 px 14 px ; font-size : .92 rem ; margin-top : 8 px }
. exa . step { margin : 5 px 0 ; padding-left : 14 px ; position : relative }
. exa . step :: before { content : '' ; position : absolute ; left : 0 ; top : 9 px ; width : 6 px ; height : 6 px ; border-radius : 50 % ; background : var ( - - pri ) }
. note-safe { display : flex ; gap : 9 px ; background : var ( - - warn - bg ) ; border : 1 px solid var ( - - pri - l ) ; border-radius : 10 px ; padding : 10 px 13 px ; font-size : .88 rem ; margin-top : 8 px }
. note-safe svg { stroke : var ( - - pri - d ) ; margin-top : 2 px }
/* WIDGET shell */
. wgt { background : var ( - - card ) ; border : 1.5 px solid var ( - - pri - soft ) ; border-radius : 14 px ; padding : 16 px 18 px ; box-shadow : var ( - - sh ) ; margin-top : 14 px }
. wgt-h { font-family : 'Outfit' ; font-size : .96 rem ; font-weight : 800 ; color : var ( - - pri - d ) ; margin-bottom : 10 px ; display : flex ; align-items : center ; gap : 8 px }
html . dark . wgt-h { color : var ( - - pri - l ) }
. wgt-h svg { stroke : var ( - - pri ) }
. fld { display : flex ; gap : 8 px ; align-items : center ; flex-wrap : wrap ; margin : 8 px 0 }
. fld label { font-size : .86 rem ; font-weight : 600 ; color : var ( - - muted ) }
input [ type = text ] , input [ type = number ] , select { font-family : inherit ; font-size : .95 rem ; padding : 8 px 11 px ; border : 1.5 px solid var ( - - border ) ; border-radius : 9 px ; background : var ( - - card ) ; color : var ( - - text ) ; transition : border-color .14 s }
input : focus , select : focus { outline : 0 ; border-color : var ( - - pri ) ; box-shadow : 0 0 0 3 px var ( - - pri - soft ) }
. btn { font-family : inherit ; font-weight : 700 ; font-size : .9 rem ; padding : 8 px 16 px ; border-radius : 9 px ; border : 1.5 px solid var ( - - border ) ; background : var ( - - card ) ; color : var ( - - text ) ; cursor : pointer ; transition : .14 s }
. btn : hover { border-color : var ( - - pri ) ; background : var ( - - pri - soft ) }
. btn . primary { background : linear-gradient ( 135 deg , var ( - - pri ) , var ( - - pri - l ) ) ; color : #fff ; border-color : transparent }
. btn . primary : hover { filter : brightness ( 1.07 ) }
. out { margin-top : 10 px ; padding : 11 px 14 px ; border-radius : 10 px ; font-size : .94 rem ; background : var ( - - card - soft ) ; border : 1 px solid var ( - - border ) }
. out . ok { background : var ( - - ok - bg ) ; border-color : #86efac ; color : var ( - - ok ) }
. out . bad { background : var ( - - bad - bg ) ; border-color : #fca5a5 ; color : var ( - - bad ) }
. bd { font-family : var ( - - mono ) ; font-size : .9 rem ; line-height : 1.7 }
/* mole triangle */
. mtri { display : grid ; grid-template-columns : 170 px 1 fr ; gap : 16 px ; align-items : center }
@ media ( max-width : 560px ) { . mtri { grid-template-columns : 1 fr } }
. mtri-svg { width : 170 px ; height : 128 px ; color : var ( - - pri ) }
. mtri-fields { display : flex ; flex-direction : column ; gap : 9 px }
. mtri-f { display : flex ; flex-direction : column ; gap : 3 px }
. mtri-lab { font-size : .78 rem ; font-weight : 700 ; color : var ( - - muted ) }
. mtri-f input { width : 100 % }
. mtri-out { grid-column : 1 /- 1 ; padding : 10 px 13 px ; border-radius : 10 px ; background : var ( - - card - soft ) ; border : 1 px solid var ( - - border ) ; font-size : .92 rem }
. mtri-out . ok { background : var ( - - ok - bg ) ; border-color : #86efac ; color : var ( - - ok ) }
. mtri-out b { display : block ; font-size : 1.02 rem }
. mtri-form { display : block ; font-family : var ( - - mono ) ; font-size : .84 rem ; opacity : .85 ; margin-top : 3 px }
/* equation balancer */
. ceqb-row { display : flex ; align-items : center ; gap : 6 px ; flex-wrap : wrap ; font-size : 1.05 rem ; font-weight : 600 ; margin-bottom : 12 px }
. ceqb-sp { display : inline-flex ; align-items : center ; gap : 3 px }
. ceqb-coef { width : 46 px ; text-align : center ; padding : 6 px 4 px ; font-weight : 800 }
. ceqb-f { font-weight : 700 }
. ceqb-plus , . ceqb-arrow { color : var ( - - muted ) ; font-weight : 800 ; padding : 0 2 px }
. ceqb-arrow { color : var ( - - pri ) ; font-size : 1.2 rem }
. ceqb-actions { display : flex ; gap : 8 px ; flex-wrap : wrap }
. ceqb-out { margin-top : 10 px }
. ceqb-msg { font-weight : 700 ; margin-bottom : 6 px }
. ceqb-out . ok . ceqb-msg { color : var ( - - ok ) }
. ceqb-out . bad . ceqb-msg { color : var ( - - bad ) }
. ceqb-tab { border-collapse : collapse ; font-size : .86 rem ; font-family : var ( - - mono ) }
. ceqb-tab th , . ceqb-tab td { border : 1 px solid var ( - - border ) ; padding : 4 px 12 px ; text-align : center }
. ceqb-tab tr . ne td { background : var ( - - bad - bg ) ; color : var ( - - bad ) }
. ceqb-tab tr . eq td { background : var ( - - ok - bg ) ; color : var ( - - ok ) }
/* element lookup */
. el-grid { display : grid ; grid-template-columns : repeat ( auto - fill , minmax ( 54 px , 1 fr ) ) ; gap : 6 px ; margin-top : 8 px }
. el-cell { aspect-ratio : 1 ; border : 1 px solid var ( - - border ) ; border-radius : 8 px ; background : var ( - - card ) ; display : flex ; flex-direction : column ; align-items : center ; justify-content : center ; cursor : pointer ; transition : .12 s ; padding : 2 px }
. el-cell : hover , . el-cell . on { background : var ( - - pri - soft ) ; border-color : var ( - - pri ) ; transform : translateY ( -2 px ) }
. el-cell . z { font-size : .6 rem ; color : var ( - - muted ) }
. el-cell . s { font-size : 1.05 rem ; font-weight : 800 ; color : var ( - - pri - d ) }
html . dark . el-cell . s { color : var ( - - pri - l ) }
. el-cell . a { font-size : .56 rem ; color : var ( - - muted ) }
. el-info { margin-top : 10 px ; padding : 12 px 14 px ; border-radius : 10 px ; background : var ( - - card - soft ) ; border : 1 px solid var ( - - border ) ; font-size : .94 rem ; min-height : 48 px }
/* trainer / proverka */
. tr { margin-top : 12 px ; background : var ( - - card - soft ) ; border : 1 px solid var ( - - border ) ; border-radius : 12 px ; padding : 13 px 15 px }
. tr-q { font-size : .94 rem ; font-weight : 600 ; margin-bottom : 9 px }
. tr-row { display : flex ; gap : 8 px ; align-items : center ; flex-wrap : wrap }
. tr-fb { margin-top : 8 px ; font-size : .88 rem ; font-weight : 600 ; display : none }
. tr-fb . show { display : block }
. tr-fb . ok { color : var ( - - ok ) }
. tr-fb . bad { color : var ( - - bad ) }
. opt { padding : 7 px 13 px ; border : 1.5 px solid var ( - - border ) ; border-radius : 9 px ; background : var ( - - card ) ; cursor : pointer ; font-size : .9 rem ; font-weight : 600 ; transition : .12 s }
. opt : hover { border-color : var ( - - pri ) }
. opt . ok { background : var ( - - ok - bg ) ; border-color : #86efac ; color : var ( - - ok ) }
. opt . bad { background : var ( - - bad - bg ) ; border-color : #fca5a5 ; color : var ( - - bad ) }
/* mark read */
. mark { margin-top : 14 px ; display : flex ; gap : 10 px ; align-items : center ; flex-wrap : wrap }
. mark . btn . primary { padding : 9 px 18 px }
. mark . done-lab { font-size : .86 rem ; font-weight : 700 ; color : var ( - - ok ) ; display : none ; align-items : center ; gap : 6 px }
. mark . done . done-lab { display : inline-flex }
. mark . done . btn { display : none }
/* boss */
. boss { background : var ( - - card ) ; border : 2 px solid var ( - - pri - soft ) ; border-radius : 16 px ; padding : 18 px ; box-shadow : var ( - - sh2 ) }
. boss-h { font-family : 'Outfit' ; font-size : 1.1 rem ; font-weight : 800 ; color : var ( - - pri - d ) ; display : flex ; align-items : center ; gap : 9 px ; margin-bottom : 6 px }
html . dark . boss-h { color : var ( - - pri - l ) }
. boss-sub { font-size : .88 rem ; color : var ( - - muted ) ; margin-bottom : 12 px }
. bossbar { display : flex ; gap : 12 px ; align-items : center ; margin-bottom : 14 px ; flex-wrap : wrap }
. bossbar . lab { font-weight : 700 ; font-size : .9 rem }
. bossbar . bar { flex : 1 ; min-width : 140 px ; height : 8 px ; background : var ( - - pri - soft ) ; border-radius : 5 px ; overflow : hidden }
. bossbar . fill { height : 100 % ; background : linear-gradient ( 90 deg , var ( - - pri ) , var ( - - pri - l ) ) ; width : 0 ; transition : width .5 s }
. bcard { border : 1.5 px solid var ( - - border ) ; border-radius : 12 px ; padding : 14 px ; margin-bottom : 11 px ; transition : .3 s }
. bcard . solved { border-color : #86efac ; box-shadow : 0 0 0 3 px var ( - - ok - bg ) }
. bcard . q { font-size : .95 rem ; margin-bottom : 10 px }
. bcard . q b { color : var ( - - pri - d ) }
html . dark . bcard . q b { color : var ( - - pri - l ) }
. bcard . row { display : flex ; gap : 8 px ; align-items : center ; flex-wrap : wrap }
. bcard . fb { margin-top : 8 px ; font-size : .88 rem ; font-weight : 600 ; display : none }
. bcard . fb . show { display : block }
. bcard . fb . ok { color : var ( - - ok ) } . bcard . fb . bad { color : var ( - - bad ) }
. hintbox { margin-top : 8 px ; padding : 9 px 12 px ; background : var ( - - warn - bg ) ; border-left : 3 px solid var ( - - pri ) ; border-radius : 7 px ; font-size : .86 rem ; display : none }
. hintbox . show { display : block }
. final-cta { margin-top : 14 px ; padding : 15 px 18 px ; border-radius : 13 px ; background : linear-gradient ( 135 deg , #fef3c7 , #fde68a ) ; border : 1.5 px solid var ( - - pri - l ) ; display : none ; gap : 13 px ; align-items : center ; flex-wrap : wrap }
. final-cta . show { display : flex }
html . dark . final-cta { background : linear-gradient ( 135 deg , rgba ( 217 , 119 , 6 , .2 ) , rgba ( 180 , 83 , 9 , .18 ) ) }
. final-cta b { color : #92400e ; font-family : 'Outfit' }
html . dark . final-cta b { color : #fde68a }
. final-cta a { margin-left : auto ; padding : 9 px 16 px ; border-radius : 9 px ; background : var ( - - pri ) ; color : #fff ; text-decoration : none ; font-weight : 700 ; font-size : .88 rem }
. foot { text-align : center ; padding : 26 px 16 px ; color : var ( - - muted ) ; font-size : .78 rem ; border-top : 1 px solid var ( - - border ) ; margin-top : 10 px }
. popup { position : fixed ; bottom : 20 px ; left : 50 % ; transform : translateX ( -50 % ) translateY ( 120 px ) ; background : linear-gradient ( 135 deg , var ( - - pri ) , var ( - - pri - l ) ) ; color : #fff ; padding : 12 px 22 px ; border-radius : 12 px ; font-weight : 700 ; box-shadow : var ( - - sh2 ) ; z-index : 50 ; transition : transform .35 s ; font-size : .92 rem }
. popup . show { transform : translateX ( -50 % ) translateY ( 0 ) }
< / style >
< / head >
< body >
@@ -73,7 +212,7 @@ html.dark .ol-note span:last-child{color:var(--pri-l)}
К разделам
< / a >
< div >
< div class = "hdr-kicker" > Вводный раздел · § 1– 9< / div >
< div class = "hdr-kicker" > Вводный раздел · § 1– 9 · ПР 1 < / div >
< h1 > Количественные понятия в химии< / h1 >
< / div >
< div class = "hdr-side" >
@@ -85,42 +224,263 @@ html.dark .ol-note span:last-child{color:var(--pri-l)}
< / div >
< / header >
< main >
< section class = "wip " >
< div class = "wip -ic" >
< svg viewBox = "0 0 24 24" > < path d = "M14.7 6.3a4 4 0 0 0-5.4 5.4l-6.3 6.3a1 1 0 0 0 0 1.4l1.6 1.6a1 1 0 0 0 1.4 0l6.3-6.3a4 4 0 0 0 5.4-5.4l-2.6 2.6-2-2 2.6-2.6z" / > < / svg >
< div class = "hero" >
< div class = "hero-card " >
< div class = "hero -ic" > n < / div >
< div class = "hero-t" >
< div class = "hero-lab" > Прогресс раздела< / div >
< div id = "prog-text" style = "font-weight:700" > 0 из 9 параграфов · 0%< / div >
< div class = "hero-bar" > < div class = "hero-fill" id = "prog-fill" > < / div > < / div >
< / div >
< div >
< h2 > Раздел в разработке< / h2 >
< p > Интерактивное наглядное наполнение этого раздела (теория, модели, симуляторы, тренажёры и боссы) добавляется поэтапно. Ниже — план параграфов раздела согласно учебнику.< / p >
< / div >
< / section >
< div class = "ol-title" >
< svg viewBox = "0 0 24 24" > < path d = "M4 6h16M4 12h16M4 18h10" / > < / svg >
Содержание раздела
< div class = "hero-xp" id = "xp-badge" > 0 XP < / div >
< / div >
< ul class = "ol-list" >
< li class = "ol-para" > < span class = "ol-num" > § 1< / span > < span class = "ol-name" > Атомы. Химические элементы. Относительная атомная масса< / span > < / li >
< li class = "ol-para" > < span class = "ol-num" > § 2< / span > < span class = "ol-name" > Молекулы. Простые и сложные вещества. Химические формулы. Относительная молекулярная масса< / span > < / li >
< li class = "ol-para" > < span class = "ol-num" > § 3< / span > < span class = "ol-name" > Химическое количество вещества< / span > < / li >
< li class = "ol-para" > < span class = "ol-num" > § 4< / span > < span class = "ol-name" > Моль — единица химического количества вещества. Постоянная Авогадро< / span > < / li >
< li class = "ol-para" > < span class = "ol-num" > § 5< / span > < span class = "ol-name" > Молярная масса. Молярный объём газов< / span > < / li >
< li class = "ol-para" > < span class = "ol-num" > § 6< / span > < span class = "ol-name" > Вычисление химического количества вещества по его массе и массы вещества по его химическому количеству< / span > < / li >
< li class = "ol-para" > < span class = "ol-num" > § 7< / span > < span class = "ol-name" > Вычисление химического количества газа по его объёму и объёма газа по его химическому количеству< / span > < / li >
< li class = "ol-note" > < span class = "ol-note-ic" > < svg viewBox = "0 0 24 24" > < path d = "M9 11l3 3L22 4" / > < path d = "M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11" / > < / svg > < / span > < span > Практическая работа 1. Химическое количество вещества< / span > < / li >
< li class = "ol-para" > < span class = "ol-num" > § 8< / span > < span class = "ol-name" > Химические реакции< / span > < / li >
< li class = "ol-para" > < span class = "ol-num" > § 9< / span > < span class = "ol-name" > Количественные расчёты по уравнениям химических реакций< / span > < / li >
< / ul >
< / main >
< / div >
< footer class = "foot " >
Интерактивный учебник «Химия — 8 класс» · Вводный раздел · LearnSpace
< / footer >
< div class = "wrap " >
< nav class = "side" id = "side" >
< div class = "side-h" > Содержание < / div >
< / nav >
< main class = "col" id = "col" >
< section class = "sec" id = "p1" >
< div class = "ph" > < div class = "ph-num" > § 1< / div > < h2 > Атомы. Химические элементы. Относительная атомная масса< / h2 > < span class = "ph-key" > $A_r(\text{O}) = 16$< / span > < / div >
< div class = "card" >
< h3 > < span class = "tg" > теория< / span > Атом и химический элемент< / h3 >
< p > < b > Атом< / b > — мельчайшая химически неделимая частица вещества. < b > Химический элемент< / b > — вид атомов с одинаковым зарядом ядра. Каждый элемент имеет символ (например, < b > H< / b > , < b > O< / b > , < b > Fe< / b > ) и порядковый номер $Z$ в периодической системе.< / p >
< div class = "def" > < b > Относительная атомная масса< / b > $A_r$ показывает, во сколько раз масса атома больше $\tfrac{1}{12}$ массы атома углерода-12. Величина безразмерная: $A_r(\text{H})=1$, $A_r(\text{O})=16$, $A_r(\text{Fe})=56$.< / div >
< / div >
< div class = "wgt" >
< div class = "wgt-h" > < svg class = "ic" viewBox = "0 0 24 24" > < circle cx = "12" cy = "12" r = "9" / > < path d = "M12 7v10M7 12h10" / > < / svg > Карта элементов: клик → $Z$, название, $A_r$< / div >
< div class = "el-grid" id = "el-grid" > < / div >
< div class = "el-info" id = "el-info" > Выберите элемент, чтобы увидеть его характеристики.< / div >
< / div >
< div class = "tr" data-tr = "t1" >
< div class = "tr-q" > Во сколько раз атом серы ($A_r=32$) тяжелее атома кислорода ($A_r=16$)?< / div >
< div class = "tr-row" data-opts > < button class = "opt" > в 2 раза< / button > < button class = "opt" > в 16 раз< / button > < button class = "opt" > в 48 раз< / button > < / div >
< div class = "tr-fb" > < / div >
< / div >
< div class = "mark" data-mark = "p1" > < button class = "btn primary" > Отметить изученным (+5 XP)< / button > < span class = "done-lab" > < svg class = "ic" viewBox = "0 0 24 24" > < polyline points = "20 6 9 17 4 12" / > < / svg > Изучено< / span > < / div >
< / section >
< section class = "sec" id = "p2" >
< div class = "ph" > < div class = "ph-num" > § 2< / div > < h2 > Молекулы. Простые и сложные вещества. Химические формулы. $M_r$< / h2 > < span class = "ph-key" > $M_r=\sum A_r$< / span > < / div >
< div class = "card" >
< h3 > < span class = "tg" > теория< / span > Вещества и формулы< / h3 >
< p > < b > Простое вещество< / b > образовано атомами одного элемента ($\text{O}_2$, $\text{Fe}$), < b > сложное< / b > — разных ($\text{H}_2\text{O}$, $\text{CaCO}_3$). < b > Химическая формула< / b > показывает качественный и количественный состав: индекс — число атомов элемента.< / p >
< div class = "def" > < b > Относительная молекулярная масса< / b > $M_r$ равна сумме относительных атомных масс всех атомов в формуле. Например, $M_r(\text{H}_2\text{O}) = 2\cdot1 + 16 = 18$.< / div >
< / div >
< div class = "wgt" >
< div class = "wgt-h" > < svg class = "ic" viewBox = "0 0 24 24" > < path d = "M4 7h16M4 12h16M4 17h10" / > < / svg > Калькулятор $M_r$ по формуле< / div >
< div class = "fld" >
< label > Формула< / label >
< input type = "text" id = "mr-in" value = "CaCO3" style = "width:160px;font-family:var(--mono)" >
< button class = "btn primary" id = "mr-go" > Вычислить< / button >
< / div >
< div class = "fld" style = "gap:6px" >
< button class = "btn mr-ex" data-f = "H2O" > H₂ O< / button >
< button class = "btn mr-ex" data-f = "H2SO4" > H₂ SO₄ < / button >
< button class = "btn mr-ex" data-f = "Ca(OH)2" > Ca(OH)₂ < / button >
< button class = "btn mr-ex" data-f = "Al2(SO4)3" > Al₂ (SO₄ )₃ < / button >
< / div >
< div class = "out" id = "mr-out" > Введите формулу и нажмите «Вычислить».< / div >
< / div >
< div class = "mark" data-mark = "p2" > < button class = "btn primary" > Отметить изученным (+5 XP)< / button > < span class = "done-lab" > < svg class = "ic" viewBox = "0 0 24 24" > < polyline points = "20 6 9 17 4 12" / > < / svg > Изучено< / span > < / div >
< / section >
< section class = "sec" id = "p3" >
< div class = "ph" > < div class = "ph-num" > § 3< / div > < h2 > Химическое количество вещества< / h2 > < span class = "ph-key" > $n$, моль< / span > < / div >
< div class = "card" >
< h3 > < span class = "tg" > теория< / span > Порция вещества< / h3 >
< p > Считать атомы и молекулы поштучно невозможно — их слишком много. Поэтому ввели специальную «порцию» — < b > химическое количество вещества< / b > $n$, единица — < b > моль< / b > . Одна и та же порция ($1$ моль) любого вещества содержит одинаковое число частиц.< / p >
< div class = "def" > Химическое количество $n$ связывает массу $m$, число частиц $N$ и объём газа $V$. Это «мост» между миром атомов и граммами на весах.< / div >
< / div >
< div class = "wgt" >
< div class = "wgt-h" > < svg class = "ic" viewBox = "0 0 24 24" > < circle cx = "6" cy = "6" r = "2" / > < circle cx = "12" cy = "6" r = "2" / > < circle cx = "18" cy = "6" r = "2" / > < circle cx = "9" cy = "12" r = "2" / > < circle cx = "15" cy = "12" r = "2" / > < circle cx = "12" cy = "18" r = "2" / > < / svg > Порция вещества: $n \Rightarrow N$ и $m$< / div >
< div class = "fld" >
< label > Вещество< / label >
< select id = "port-sub" > < option value = "H2O" > вода H₂ O (M=18)< / option > < option value = "O2" > кислород O₂ (M=32)< / option > < option value = "CO2" > углекислый газ CO₂ (M=44)< / option > < option value = "NaCl" > соль NaCl (M=58,5)< / option > < / select >
< label > n, моль< / label >
< input type = "range" id = "port-n" min = "0.1" max = "5" step = "0.1" value = "1" style = "vertical-align:middle" >
< span class = "bd" id = "port-nv" > 1,0< / span >
< / div >
< div class = "out" id = "port-out" > < / div >
< / div >
< div class = "mark" data-mark = "p3" > < button class = "btn primary" > Отметить изученным (+5 XP)< / button > < span class = "done-lab" > < svg class = "ic" viewBox = "0 0 24 24" > < polyline points = "20 6 9 17 4 12" / > < / svg > Изучено< / span > < / div >
< / section >
< section class = "sec" id = "p4" >
< div class = "ph" > < div class = "ph-num" > § 4< / div > < h2 > Моль — единица химического количества. Постоянная Авогадро< / h2 > < span class = "ph-key" > $N = n\cdot N_A$< / span > < / div >
< div class = "card" >
< h3 > < span class = "tg" > теория< / span > Постоянная Авогадро< / h3 >
< div class = "def" > < b > 1 моль< / b > — это химическое количество вещества, содержащее столько же частиц, сколько атомов в $12$ г углерода-$12$, а именно $N_A = 6{,}02\cdot10^{23}$ частиц/моль — < b > постоянная Авогадро< / b > .< / div >
< p > Число частиц: $N = n\cdot N_A$. Отсюда $n = \dfrac{N}{N_A}$.< / p >
< / div >
< div class = "wgt" >
< div class = "wgt-h" > < svg class = "ic" viewBox = "0 0 24 24" > < path d = "M12 2v20M2 12h20" / > < / svg > Счётчик частиц $N = n\cdot N_A$< / div >
< div class = "fld" > < label > n, моль< / label > < input type = "range" id = "av-n" min = "0.25" max = "10" step = "0.25" value = "2" > < span class = "bd" id = "av-nv" > 2,0< / span > < / div >
< div class = "out" id = "av-out" > < / div >
< / div >
< div class = "tr" data-tr = "t4" >
< div class = "tr-q" > Сколько молекул содержится в $0{,}5$ моль воды? ($N_A=6{,}02\cdot10^{23}$)< / div >
< div class = "tr-row" data-opts > < button class = "opt" data-ok > $3{,}01\cdot10^{23}$< / button > < button class = "opt" > $6{,}02\cdot10^{23}$< / button > < button class = "opt" > $12{,}04\cdot10^{23}$< / button > < / div >
< div class = "tr-fb" > < / div >
< / div >
< div class = "mark" data-mark = "p4" > < button class = "btn primary" > Отметить изученным (+5 XP)< / button > < span class = "done-lab" > < svg class = "ic" viewBox = "0 0 24 24" > < polyline points = "20 6 9 17 4 12" / > < / svg > Изучено< / span > < / div >
< / section >
< section class = "sec" id = "p5" >
< div class = "ph" > < div class = "ph-num" > § 5< / div > < h2 > Молярная масса. Молярный объём газов< / h2 > < span class = "ph-key" > $V_m=22{,}4$ л/моль< / span > < / div >
< div class = "card" >
< h3 > < span class = "tg" > теория< / span > M и Vm< / h3 >
< div class = "def" > < b > Молярная масса< / b > $M$ — масса $1$ моль вещества (г/моль). Численно $M$ равна $M_r$: $M(\text{H}_2\text{O})=18$ г /моль.< / div >
< div class = "def" > < b > Молярный объём< / b > $V_m$ — объём $1$ моль газа. При нормальных условиях (н.у.) $V_m = 22{,}4$ л/моль для любого газа (закон Авогадро).< / div >
< / div >
< div class = "wgt" >
< div class = "wgt-h" > < svg class = "ic" viewBox = "0 0 24 24" > < path d = "M4 7h16M4 12h16M4 17h16" / > < / svg > M по формуле и объём 1 моль газа< / div >
< div class = "fld" > < label > Формула газа< / label > < input type = "text" id = "m5-in" value = "CO2" style = "width:140px;font-family:var(--mono)" > < button class = "btn primary" id = "m5-go" > Найти M< / button > < / div >
< div class = "out" id = "m5-out" > M(CO₂ ) и объём при н.у. появятся здесь.< / div >
< / div >
< div class = "mark" data-mark = "p5" > < button class = "btn primary" > Отметить изученным (+5 XP)< / button > < span class = "done-lab" > < svg class = "ic" viewBox = "0 0 24 24" > < polyline points = "20 6 9 17 4 12" / > < / svg > Изучено< / span > < / div >
< / section >
< section class = "sec" id = "p6" >
< div class = "ph" > < div class = "ph-num" > § 6 · звёздный виджет< / div > < h2 > Вычисление $n$ по массе и массы по $n$< / h2 > < span class = "ph-key" > $n = \dfrac{m}{M}$< / span > < / div >
< div class = "card" >
< h3 > < span class = "tg" > правило< / span > Треугольник n– m– M< / h3 >
< p > Три величины связаны формулой $m = n\cdot M$. Закрой искомую — получишь формулу: $n=\dfrac{m}{M}$, $m=n\cdot M$, $M=\dfrac{m}{n}$.< / p >
< div class = "exa" >
< div class = "step" > Дано: $m=36$ г воды, $M=18$ г /моль.< / div >
< div class = "step" > $n = \dfrac{m}{M} = \dfrac{36}{18} = 2$ моль.< / div >
< / div >
< / div >
< div class = "wgt" >
< div class = "wgt-h" > < svg class = "ic" viewBox = "0 0 24 24" > < path d = "M12 3 2 21h20z" / > < / svg > Интерактивный треугольник n–m–M< / div >
< div class = "fld" > < label > Подставить M вещества< / label > < select id = "mt-sub" > < option value = "" > — вручную —< / option > < option value = "H2O" > H₂ O · 18< / option > < option value = "CO2" > CO₂ · 44< / option > < option value = "NaOH" > NaOH · 40< / option > < option value = "CaCO3" > CaCO₃ · 100< / option > < option value = "H2SO4" > H₂ SO₄ · 98< / option > < / select > < / div >
< div id = "mt-mount" > < / div >
< / div >
< div class = "mark" data-mark = "p6" > < button class = "btn primary" > Отметить изученным (+5 XP)< / button > < span class = "done-lab" > < svg class = "ic" viewBox = "0 0 24 24" > < polyline points = "20 6 9 17 4 12" / > < / svg > Изучено< / span > < / div >
< / section >
< section class = "sec" id = "p7" >
< div class = "ph" > < div class = "ph-num" > § 7< / div > < h2 > Вычисление $n$ газа по объёму и объёма по $n$< / h2 > < span class = "ph-key" > $n = \dfrac{V}{V_m}$< / span > < / div >
< div class = "card" >
< h3 > < span class = "tg" > правило< / span > Связка m – n – V – N< / h3 >
< p > Для газа при н.у .: $n=\dfrac{V}{V_m}$, $V=n\cdot V_m$ ($V_m=22{,}4$ л/моль). Вместе с $n=\dfrac{m}{M}$ и $N=n\cdot N_A$ это единая система: зная одно — найдёшь всё.< / p >
< / div >
< div class = "wgt" >
< div class = "wgt-h" > < svg class = "ic" viewBox = "0 0 24 24" > < path d = "M3 12h18M12 3v18" / > < / svg > Универсальный калькулятор газа< / div >
< div class = "fld" > < label > Газ< / label > < select id = "g7-sub" > < option value = "O2" > O₂ · M=32< / option > < option value = "CO2" > CO₂ · M=44< / option > < option value = "H2" > H₂ · M=2< / option > < option value = "N2" > N₂ · M=28< / option > < / select > < / div >
< div class = "fld" > < label > Известно< / label >
< select id = "g7-key" > < option value = "n" > n, моль< / option > < option value = "m" > m, г < / option > < option value = "V" > V, л (н.у .)< / option > < option value = "N" > N, частиц< / option > < / select >
< input type = "text" id = "g7-val" value = "1" style = "width:120px;font-family:var(--mono)" >
< button class = "btn primary" id = "g7-go" > Рассчитать< / button >
< / div >
< div class = "out" id = "g7-out" > < / div >
< / div >
< div class = "mark" data-mark = "p7" > < button class = "btn primary" > Отметить изученным (+5 XP)< / button > < span class = "done-lab" > < svg class = "ic" viewBox = "0 0 24 24" > < polyline points = "20 6 9 17 4 12" / > < / svg > Изучено< / span > < / div >
< / section >
< section class = "sec" id = "pr1" >
< div class = "ph" style = "background:linear-gradient(135deg,#7c2d12,#ea580c 60%,#fb923c)" > < div class = "ph-num" > Практическая работа 1< / div > < h2 > Химическое количество вещества< / h2 > < span class = "ph-key" > $n=\dfrac{m}{M}$< / span > < / div >
< div class = "card" >
< h3 > < span class = "tg" > практика< / span > Порядок работы< / h3 >
< ul >
< li > Взвесь на весах образцы веществ (например, $\text{NaCl}$, $\text{CuSO}_4$).< / li >
< li > Запиши массу $m$ и определи молярную массу $M$ по формуле.< / li >
< li > Вычисли химическое количество $n=\dfrac{m}{M}$ и число частиц $N=n\cdot N_A$.< / li >
< li > Оформи вывод: какому числу частиц соответствует взятая масса.< / li >
< / ul >
< div class = "note-safe" > < svg class = "ic" viewBox = "0 0 24 24" > < path d = "M12 9v4M12 17h.01" / > < path d = "M10.3 3.9 1.8 18a2 2 0 0 0 1.7 3h17a2 2 0 0 0 1.7-3L13.7 3.9a2 2 0 0 0-3.4 0z" / > < / svg > Работай аккуратно с реактивами и весами; не пробуй вещества на вкус.< / div >
< / div >
< div class = "mark" data-mark = "pr1" > < button class = "btn primary" > Отметить выполненной (+5 XP)< / button > < span class = "done-lab" > < svg class = "ic" viewBox = "0 0 24 24" > < polyline points = "20 6 9 17 4 12" / > < / svg > Выполнено< / span > < / div >
< / section >
< section class = "sec" id = "p8" >
< div class = "ph" > < div class = "ph-num" > § 8 · звёздный виджет< / div > < h2 > Химические реакции< / h2 > < span class = "ph-key" > закон сохранения массы< / span > < / div >
< div class = "card" >
< h3 > < span class = "tg" > теория< / span > Уравнение реакции< / h3 >
< p > В химической реакции одни вещества превращаются в другие, но < b > атомы не исчезают и не появляются< / b > (закон сохранения массы М. В. Ломоносова, А. Лавуазье). Поэтому уравнение реакции < b > уравнивают коэффициентами< / b > — число атомов каждого элемента слева и справа равно.< / p >
< p > Типы реакций: < b > соединения< / b > ($A+B\to AB$), < b > разложения< / b > ($AB\to A+B$), < b > замещения< / b > ($A+BC\to AC+B$), < b > обмена< / b > ($AB+CD\to AD+CB$).< / p >
< / div >
< div class = "wgt" >
< div class = "wgt-h" > < svg class = "ic" viewBox = "0 0 24 24" > < path d = "M3 12h18M14 6l6 6-6 6" / > < / svg > Балансировщик: расставь коэффициенты< / div >
< div class = "fld" style = "gap:6px" > < label > Реакция< / label >
< select id = "bal-pick" >
< option value = "H2 + O2 -> H2O|2,1,2" > H₂ + O₂ → H₂ O< / option >
< option value = "Fe + O2 -> Fe2O3|4,3,2" > Fe + O₂ → Fe₂ O₃ < / option >
< option value = "Al + HCl -> AlCl3 + H2|2,6,2,3" > Al + HCl → AlCl₃ + H₂ < / option >
< option value = "CH4 + O2 -> CO2 + H2O|1,2,1,2" > CH₄ + O₂ → CO₂ + H₂ O< / option >
< / select >
< / div >
< div id = "bal-mount" > < / div >
< / div >
< div class = "mark" data-mark = "p8" > < button class = "btn primary" > Отметить изученным (+5 XP)< / button > < span class = "done-lab" > < svg class = "ic" viewBox = "0 0 24 24" > < polyline points = "20 6 9 17 4 12" / > < / svg > Изучено< / span > < / div >
< / section >
< section class = "sec" id = "p9" >
< div class = "ph" > < div class = "ph-num" > § 9 · звёздный виджет< / div > < h2 > Количественные расчёты по уравнениям реакций< / h2 > < span class = "ph-key" > по мольным отношениям< / span > < / div >
< div class = "card" >
< h3 > < span class = "tg" > правило< / span > Алгоритм расчёта< / h3 >
< ul >
< li > Записать и уравнять уравнение реакции.< / li >
< li > Найти $n$ известного вещества: $n=\dfrac{m}{M}$ (или $\dfrac{V}{V_m}$).< / li >
< li > По коэффициентам найти $n$ искомого (мольное отношение).< / li >
< li > Перейти к массе/объёму: $m=n\cdot M$ ($V=n\cdot V_m$).< / li >
< / ul >
< / div >
< div class = "wgt" >
< div class = "wgt-h" > < svg class = "ic" viewBox = "0 0 24 24" > < path d = "M9 11l3 3L22 4" / > < path d = "M21 12v7a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h11" / > < / svg > Пошаговый решатель по уравнению< / div >
< div class = "fld" > < label > Задача< / label > < select id = "st-pick" > < / select > < / div >
< div class = "out" id = "st-out" > < / div >
< div class = "fld" > < button class = "btn" id = "st-step" > Следующий шаг ▸< / button > < button class = "btn" id = "st-all" > Показать всё решение< / button > < / div >
< / div >
< div class = "mark" data-mark = "p9" > < button class = "btn primary" > Отметить изученным (+5 XP)< / button > < span class = "done-lab" > < svg class = "ic" viewBox = "0 0 24 24" > < polyline points = "20 6 9 17 4 12" / > < / svg > Изучено< / span > < / div >
< / section >
< section class = "sec" id = "boss" >
< div class = "boss" >
< div class = "boss-h" > < svg class = "ic" viewBox = "0 0 24 24" style = "width:22px;height:22px" > < path d = "M7 4h10v6a5 5 0 0 1-10 0V4z" / > < path d = "M9 20h6M12 15v5" / > < / svg > Босс раздела: количественные понятия< / div >
< div class = "boss-sub" > 4 задачи на всё, что изучено. За каждую — +10 XP. Победишь всех — ачивка «Счёт в химии» и +30 XP.< / div >
< div class = "bossbar" > < span class = "lab" id = "boss-lab" > Решено: 0 / 4< / span > < div class = "bar" > < div class = "fill" id = "boss-fill" > < / div > < / div > < / div >
< div id = "boss-cont" > < / div >
< div class = "final-cta" id = "boss-cta" >
< svg class = "ic" viewBox = "0 0 24 24" style = "width:26px;height:26px;stroke:var(--pri-d)" > < path d = "M6 9H4l-1-3h18l-1 3h-2M6 9l1 6h10l1-6" / > < / svg >
< b > Вводный раздел пройден! Ачивка «Счёт в химии» получена.< / b >
< a href = "/textbook/chemistry-8" > К разделам →< / a >
< / div >
< / div >
< / section >
< / main >
< / div >
< div class = "popup" id = "popup" > < / div >
< footer class = "foot" > Интерактивный учебник «Химия — 8 класс» · Вводный раздел · LearnSpace< / footer >
< script >
'use strict' ;
/* Инициализация — после DOMContentLoaded: к этому моменту отложенные (defer)
chem8_svg.js / biochem-core.js / api.js / xp.js уже выполнены и доступны. */
document . addEventListener ( 'DOMContentLoaded' , function ( ) {
const _TB _SLUG = 'chemistry-8-intro' ;
const C = window . Chem8 || { } ;
const Nav = [
{ id : 'p1' , n : '§ 1' , t : 'Атомы. Относительная атомная масса' } ,
{ id : 'p2' , n : '§ 2' , t : 'Формулы. Молекулярная масса' } ,
{ id : 'p3' , n : '§ 3' , t : 'Химическое количество' } ,
{ id : 'p4' , n : '§ 4' , t : 'Моль. Постоянная Авогадро' } ,
{ id : 'p5' , n : '§ 5' , t : 'Молярная масса и объём' } ,
{ id : 'p6' , n : '§ 6' , t : 'Треугольник n–m–M' } ,
{ id : 'p7' , n : '§ 7' , t : 'Расчёты для газов' } ,
{ id : 'pr1' , n : 'ПР' , t : 'Практическая работа 1' , note : true } ,
{ id : 'p8' , n : '§ 8' , t : 'Химические реакции' } ,
{ id : 'p9' , n : '§ 9' , t : 'Расчёты по уравнениям' } ,
{ id : 'boss' , n : '★' , t : 'Босс раздела' , note : true }
] ;
const READ _IDS = [ 'p1' , 'p2' , 'p3' , 'p4' , 'p5' , 'p6' , 'p7' , 'p8' , 'p9' ] ;
/* theme */
( function ( ) {
var saved = localStorage . getItem ( 'chemistry8_theme' ) || localStorage . getItem ( 'theme' ) || 'light' ;
if ( saved === 'dark' ) document . documentElement . classList . add ( 'dark' ) ;
@@ -128,12 +488,294 @@ const _TB_SLUG = 'chemistry-8-intro';
if ( lab ) lab . textContent = saved === 'dark' ? 'Светлая' : 'Тёмная' ;
document . getElementById ( 'theme-btn' ) . addEventListener ( 'click' , function ( ) {
document . documentElement . classList . toggle ( 'dark' ) ;
var dark = document . documentElement . classList . contains ( 'dark' ) ;
localStorage . setItem ( 'chemistry8_theme' , dark ? 'dark' : 'light' ) ;
localStorage . setItem ( 'theme' , dark ? 'dark' : 'light' ) ;
if ( lab ) lab . textContent = dark ? 'Светлая' : 'Тёмная' ;
var d = document . documentElement . classList . contains ( 'dark' ) ;
localStorage . setItem ( 'chemistry8_theme' , d ? 'dark' : 'light' ) ;
localStorage . setItem ( 'theme' , d ? 'dark' : 'light' ) ;
if ( lab ) lab . textContent = d ? 'Светлая' : 'Тёмная' ;
} ) ;
} ) ( ) ;
/* sidebar */
( function ( ) {
var side = document . getElementById ( 'side' ) ;
Nav . forEach ( function ( it ) {
var a = document . createElement ( 'a' ) ;
a . href = '#' + it . id ; a . id = 'nav-' + it . id ;
if ( it . note ) a . className = 'note' ;
a . innerHTML = '<span class="side-num">' + it . n + '</span><span>' + it . t + '</span>' ;
side . appendChild ( a ) ;
} ) ;
} ) ( ) ;
/* progress / XP */
var PROG = { } ;
function popup ( msg ) { var p = document . getElementById ( 'popup' ) ; p . textContent = msg ; p . classList . add ( 'show' ) ; setTimeout ( function ( ) { p . classList . remove ( 'show' ) ; } , 2200 ) ; }
function loadLocal ( ) { try { var s = localStorage . getItem ( 'chemistry8_intro_read' ) ; if ( s ) PROG = JSON . parse ( s ) || { } ; } catch ( e ) { } }
function saveLocal ( ) { try { localStorage . setItem ( 'chemistry8_intro_read' , JSON . stringify ( PROG ) ) ; } catch ( e ) { } }
function getXp ( ) { return parseInt ( localStorage . getItem ( 'chemistry8_xp' ) || '0' , 10 ) || 0 ; }
function addXp ( n , src ) {
localStorage . setItem ( 'chemistry8_xp' , String ( getXp ( ) + n ) ) ;
try { if ( window . LS && LS . xp && LS . xp . add ) LS . xp . add ( n , 'chem8-intro-' + ( src || 'x' ) ) ; } catch ( e ) { }
refreshXp ( ) ;
}
function refreshXp ( ) { var b = document . getElementById ( 'xp-badge' ) ; if ( b ) b . textContent = getXp ( ) + ' XP' ; }
var _marked = new Set ( ) , _pending = null , _timer = null ;
function _flush ( ) { var body = _pending ; _pending = null ; if ( ! body ) return ; var 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 ( ) { } ) ; }
function _queue ( p ) { _pending = Object . assign ( _pending || { } , p ) ; if ( _timer ) clearTimeout ( _timer ) ; _timer = setTimeout ( _flush , 600 ) ; }
function markServerRead ( id ) { if ( _marked . has ( id ) ) return ; _marked . add ( id ) ; _queue ( { mark _read : id } ) ; }
window . addEventListener ( 'beforeunload' , _flush ) ;
function refreshProgress ( ) {
var read = READ _IDS . filter ( function ( id ) { return PROG [ id ] ; } ) . length ;
var pct = Math . round ( read * 100 / READ _IDS . length ) ;
var t = document . getElementById ( 'prog-text' ) ; if ( t ) t . textContent = read + ' из ' + READ _IDS . length + ' параграфов · ' + pct + '%' ;
var f = document . getElementById ( 'prog-fill' ) ; if ( f ) f . style . width = pct + '%' ;
Nav . forEach ( function ( it ) { var a = document . getElementById ( 'nav-' + it . id ) ; if ( a ) { if ( PROG [ it . id ] ) a . classList . add ( 'done' ) ; else a . classList . remove ( 'done' ) ; } } ) ;
document . querySelectorAll ( '[data-mark]' ) . forEach ( function ( m ) { if ( PROG [ m . getAttribute ( 'data-mark' ) ] ) m . classList . add ( 'done' ) ; } ) ;
}
function markRead ( id ) {
if ( PROG [ id ] ) return ;
PROG [ id ] = true ; saveLocal ( ) ;
if ( READ _IDS . indexOf ( id ) !== - 1 ) markServerRead ( id ) ;
addXp ( 5 , id ) ; refreshProgress ( ) ; popup ( '+5 XP · отмечено' ) ;
}
document . querySelectorAll ( '[data-mark]' ) . forEach ( function ( m ) {
m . querySelector ( '.btn' ) . addEventListener ( 'click' , function ( ) { markRead ( m . getAttribute ( 'data-mark' ) ) ; } ) ;
} ) ;
function loadServer ( ) {
var tok = ( window . LS && LS . getToken ) ? LS . getToken ( ) : '' ; if ( ! tok ) { refreshProgress ( ) ; return ; }
fetch ( '/api/textbooks/' + _TB _SLUG , { headers : { 'Authorization' : 'Bearer ' + tok } } ) . then ( function ( r ) { return r . ok ? r . json ( ) : null ; } ) . then ( function ( d ) {
if ( d && d . progress && d . progress . read ) { d . progress . read . forEach ( function ( k ) { _marked . add ( k ) ; PROG [ k ] = true ; } ) ; saveLocal ( ) ; }
refreshProgress ( ) ;
} ) . catch ( function ( ) { refreshProgress ( ) ; } ) ;
}
/* scrollspy */
( function ( ) {
var secs = Nav . map ( function ( it ) { return document . getElementById ( it . id ) ; } ) . filter ( Boolean ) ;
function onScroll ( ) {
var y = window . scrollY + 90 , cur = secs [ 0 ] ;
secs . forEach ( function ( s ) { if ( s . offsetTop <= y ) cur = s ; } ) ;
Nav . forEach ( function ( it ) { var a = document . getElementById ( 'nav-' + it . id ) ; if ( a ) a . classList . toggle ( 'active' , cur && cur . id === it . id ) ; } ) ;
}
window . addEventListener ( 'scroll' , onScroll , { passive : true } ) ; onScroll ( ) ;
} ) ( ) ;
/* ===== WIDGETS ===== */
/* §1 element lookup */
var EL = {
H : [ 1 , 'Водород' ] , He : [ 2 , 'Гелий' ] , Li : [ 3 , 'Литий' ] , Be : [ 4 , 'Бериллий' ] , B : [ 5 , 'Бор' ] , C : [ 6 , 'Углерод' ] ,
N : [ 7 , 'Азот' ] , O : [ 8 , 'Кислород' ] , F : [ 9 , 'Фтор' ] , Ne : [ 10 , 'Неон' ] , Na : [ 11 , 'Натрий' ] , Mg : [ 12 , 'Магний' ] ,
Al : [ 13 , 'Алюминий' ] , Si : [ 14 , 'Кремний' ] , P : [ 15 , 'Фосфор' ] , S : [ 16 , 'Сера' ] , Cl : [ 17 , 'Хлор' ] , Ar : [ 18 , 'Аргон' ] ,
K : [ 19 , 'Калий' ] , Ca : [ 20 , 'Кальций' ] , Fe : [ 26 , 'Железо' ] , Cu : [ 29 , 'Медь' ] , Zn : [ 30 , 'Цинк' ] , Ag : [ 47 , 'Серебро' ] , Ba : [ 56 , 'Барий' ]
} ;
( function ( ) {
var grid = document . getElementById ( 'el-grid' ) , info = document . getElementById ( 'el-info' ) ; if ( ! grid ) return ;
Object . keys ( EL ) . forEach ( function ( s ) {
var ar = C . arOf ? C . arOf ( s ) : '' ;
var c = document . createElement ( 'div' ) ; c . className = 'el-cell' ; c . dataset . s = s ;
c . innerHTML = '<span class="z">' + EL [ s ] [ 0 ] + '</span><span class="s">' + s + '</span><span class="a">' + ar + '</span>' ;
c . addEventListener ( 'click' , function ( ) {
grid . querySelectorAll ( '.el-cell' ) . forEach ( function ( x ) { x . classList . remove ( 'on' ) ; } ) ; c . classList . add ( 'on' ) ;
info . innerHTML = '<b>' + EL [ s ] [ 1 ] + '</b> (' + s + ') · порядковый номер Z = ' + EL [ s ] [ 0 ] + ' · A_r = ' + ar ;
} ) ;
grid . appendChild ( c ) ;
} ) ;
} ) ( ) ;
function bindOpts ( trId , okText ) {
var tr = document . querySelector ( '[data-tr="' + trId + '"]' ) ; if ( ! tr ) return ;
var fb = tr . querySelector ( '.tr-fb' ) ;
tr . querySelectorAll ( '.opt' ) . forEach ( function ( o ) {
o . addEventListener ( 'click' , function ( ) {
var ok = o . hasAttribute ( 'data-ok' ) || ( okText && o . textContent . trim ( ) === okText ) ;
tr . querySelectorAll ( '.opt' ) . forEach ( function ( x ) { x . style . pointerEvents = 'none' ; } ) ;
o . classList . add ( ok ? 'ok' : 'bad' ) ; fb . className = 'tr-fb show ' + ( ok ? 'ok' : 'bad' ) ;
fb . textContent = ok ? 'Верно! +3 XP' : 'Неверно.' ;
if ( ! ok ) { tr . querySelectorAll ( '.opt' ) . forEach ( function ( x ) { if ( x . hasAttribute ( 'data-ok' ) || ( okText && x . textContent . trim ( ) === okText ) ) x . classList . add ( 'ok' ) ; } ) ; }
else addXp ( 3 , trId ) ;
} ) ;
} ) ;
}
bindOpts ( 't1' , 'в 2 раза' ) ;
bindOpts ( 't4' , null ) ;
/* §2 Mr calculator */
( function ( ) {
var inp = document . getElementById ( 'mr-in' ) , out = document . getElementById ( 'mr-out' ) , go = document . getElementById ( 'mr-go' ) ; if ( ! inp ) return ;
function calc ( ) {
var f = inp . value . trim ( ) ; var cnt = C . elementCounts ? C . elementCounts ( f ) : null ; var mr = C . molarMass ? C . molarMass ( f ) : NaN ;
if ( ! cnt || isNaN ( mr ) ) { out . className = 'out bad' ; out . textContent = 'Не удалось разобрать формулу. Проверьте символы элементов.' ; return ; }
var terms = Object . keys ( cnt ) . map ( function ( e ) { return ( C . arOf ? C . arOf ( e ) : '?' ) + '·' + cnt [ e ] ; } ) ;
out . className = 'out ok' ;
out . innerHTML = '<b>M_r(' + f + ') = ' + C . fmt ( mr ) + '</b><br><span class="bd">' +
Object . keys ( cnt ) . map ( function ( e ) { return e + ': A_r=' + ( C . arOf ? C . arOf ( e ) : '?' ) + ' × ' + cnt [ e ] ; } ) . join ( ' | ' ) +
'<br>Σ = ' + terms . join ( ' + ' ) + ' = ' + C . fmt ( mr ) + '</span>' ;
}
go . addEventListener ( 'click' , calc ) ;
inp . addEventListener ( 'keydown' , function ( e ) { if ( e . key === 'Enter' ) calc ( ) ; } ) ;
document . querySelectorAll ( '.mr-ex' ) . forEach ( function ( b ) { b . addEventListener ( 'click' , function ( ) { inp . value = b . dataset . f ; calc ( ) ; } ) ; } ) ;
calc ( ) ;
} ) ( ) ;
/* §3 portion */
( function ( ) {
var sub = document . getElementById ( 'port-sub' ) , rng = document . getElementById ( 'port-n' ) , nv = document . getElementById ( 'port-nv' ) , out = document . getElementById ( 'port-out' ) ; if ( ! sub ) return ;
var M = { H2O : 18 , O2 : 32 , CO2 : 44 , NaCl : 58.5 } ;
function rr ( v ) { return ( Math . round ( v * 100 ) / 100 ) . toString ( ) . replace ( '.' , ',' ) ; }
function upd ( ) {
var n = parseFloat ( rng . value ) , s = sub . value , m = n * M [ s ] , N = n * 6.02 ;
nv . textContent = n . toFixed ( 1 ) . replace ( '.' , ',' ) ;
out . innerHTML = '<span class="bd">n = ' + n . toFixed ( 1 ) . replace ( '.' , ',' ) + ' моль<br>' +
'm = n·M = ' + n . toFixed ( 1 ) . replace ( '.' , ',' ) + ' · ' + String ( M [ s ] ) . replace ( '.' , ',' ) + ' = <b>' + rr ( m ) + ' г </b><br>' +
'N = n·N_A = ' + rr ( N ) + '·10²³ ≈ <b>' + rr ( N ) + '·10²³ частиц</b></span>' ;
}
sub . addEventListener ( 'change' , upd ) ; rng . addEventListener ( 'input' , upd ) ; upd ( ) ;
} ) ( ) ;
/* §4 avogadro */
( function ( ) {
var rng = document . getElementById ( 'av-n' ) , nv = document . getElementById ( 'av-nv' ) , out = document . getElementById ( 'av-out' ) ; if ( ! rng ) return ;
function upd ( ) { var n = parseFloat ( rng . value ) , N = n * 6.02 ; nv . textContent = n . toFixed ( 2 ) . replace ( '.' , ',' ) ;
out . innerHTML = '<span class="bd">N = n · N_A = ' + n . toFixed ( 2 ) . replace ( '.' , ',' ) + ' · 6,02·10²³ = <b>' + ( Math . round ( N * 100 ) / 100 ) . toString ( ) . replace ( '.' , ',' ) + '·10²³ частиц</b></span>' ;
}
rng . addEventListener ( 'input' , upd ) ; upd ( ) ;
} ) ( ) ;
/* §5 M + gas volume */
( function ( ) {
var inp = document . getElementById ( 'm5-in' ) , out = document . getElementById ( 'm5-out' ) , go = document . getElementById ( 'm5-go' ) ; if ( ! inp ) return ;
function calc ( ) { var f = inp . value . trim ( ) , mr = C . molarMass ? C . molarMass ( f ) : NaN ;
if ( isNaN ( mr ) ) { out . className = 'out bad' ; out . textContent = 'Не удалось разобрать формулу.' ; return ; }
out . className = 'out ok' ;
out . innerHTML = '<span class="bd">M(' + f + ') = <b>' + C . fmt ( mr ) + ' г /моль</b><br>1 моль газа при н.у. занимает <b>22,4 л</b>.<br>Плотность газа ≈ M/22,4 = ' + ( Math . round ( mr / 22.4 * 1000 ) / 1000 ) . toString ( ) . replace ( '.' , ',' ) + ' г /л</span>' ;
}
go . addEventListener ( 'click' , calc ) ; inp . addEventListener ( 'keydown' , function ( e ) { if ( e . key === 'Enter' ) calc ( ) ; } ) ; calc ( ) ;
} ) ( ) ;
/* §6 mole triangle */
( function ( ) {
var mount = document . getElementById ( 'mt-mount' ) , sub = document . getElementById ( 'mt-sub' ) ; if ( ! mount || ! C . moleTriangle ) return ;
var api = C . moleTriangle ( mount , { } ) ;
if ( sub ) sub . addEventListener ( 'change' , function ( ) {
var f = sub . value ; if ( ! f ) return ;
var m = C . molarMass ( f ) ; if ( ! isNaN ( m ) && api && api . set ) api . set ( 'M' , m ) ;
} ) ;
} ) ( ) ;
/* §7 universal gas calc */
( function ( ) {
var sub = document . getElementById ( 'g7-sub' ) , key = document . getElementById ( 'g7-key' ) , val = document . getElementById ( 'g7-val' ) , go = document . getElementById ( 'g7-go' ) , out = document . getElementById ( 'g7-out' ) ; if ( ! sub ) return ;
var Vm = 22.4 , NA = 6.02 ;
function calc ( ) {
var f = sub . value , M = C . molarMass ( f ) , k = key . value , x = parseFloat ( val . value . replace ( ',' , '.' ) ) ;
if ( isNaN ( x ) ) { out . className = 'out bad' ; out . textContent = 'Введите число.' ; return ; }
var n ;
if ( k === 'n' ) n = x ; else if ( k === 'm' ) n = x / M ; else if ( k === 'V' ) n = x / Vm ; else n = x / NA ;
var m = n * M , V = n * Vm , N = n * NA ;
function r ( v ) { return ( Math . round ( v * 1000 ) / 1000 ) . toString ( ) . replace ( '.' , ',' ) ; }
out . className = 'out ok' ;
out . innerHTML = '<span class="bd">M(' + f + ')=' + M + ' г /моль<br>n = <b>' + r ( n ) + ' моль</b><br>m = <b>' + r ( m ) + ' г </b><br>V(н.у .) = <b>' + r ( V ) + ' л</b><br>N = <b>' + r ( N ) + '·10²³ частиц</b></span>' ;
}
go . addEventListener ( 'click' , calc ) ; val . addEventListener ( 'keydown' , function ( e ) { if ( e . key === 'Enter' ) calc ( ) ; } ) ; calc ( ) ;
} ) ( ) ;
/* §8 balancer */
( function ( ) {
var pick = document . getElementById ( 'bal-pick' ) , mount = document . getElementById ( 'bal-mount' ) ; if ( ! pick || ! C . equationBalancer ) return ;
function build ( ) {
var parts = pick . value . split ( '|' ) ; var skel = parts [ 0 ] ; var sol = parts [ 1 ] . split ( ',' ) . map ( Number ) ;
C . equationBalancer ( mount , { skeleton : skel , solution : sol } ) ;
}
pick . addEventListener ( 'change' , build ) ; build ( ) ;
} ) ( ) ;
/* §9 stoichiometry step solver */
var ST = [
{ eq : '2H₂ + O₂ → 2H₂O' , given : 'Дано: m(H₂) = 4 г . Найти m(H₂O).' ,
steps : [ 'M(H₂)=2 г /моль, M(H₂O)=18 г /моль.' , 'n(H₂) = m/M = 4/2 = 2 моль.' , 'По уравнению n(H₂):n(H₂O) = 2:2 = 1:1 → n(H₂O)=2 моль.' , 'm(H₂O) = n·M = 2·18 = 36 г . Ответ: 36 г .' ] } ,
{ eq : 'CaCO₃ → CaO + CO₂↑' , given : 'Дано: m(CaCO₃) = 100 г . Найти V(CO₂) при н.у .' ,
steps : [ 'M(CaCO₃)=100 г /моль.' , 'n(CaCO₃) = 100/100 = 1 моль.' , 'n(CaCO₃):n(CO₂) = 1:1 → n(CO₂)=1 моль.' , 'V(CO₂) = n·Vm = 1·22,4 = 22,4 л. Ответ: 22,4 л.' ] } ,
{ eq : 'Zn + 2HCl → ZnCl₂ + H₂↑' , given : 'Дано: n(Zn) = 0,5 моль. Найти V(H₂) при н.у .' ,
steps : [ 'n(Zn):n(H₂) = 1:1 → n(H₂)=0,5 моль.' , 'V(H₂) = n·Vm = 0,5·22,4 = 11,2 л. Ответ: 11,2 л.' ] }
] ;
( function ( ) {
var pick = document . getElementById ( 'st-pick' ) , out = document . getElementById ( 'st-out' ) , bStep = document . getElementById ( 'st-step' ) , bAll = document . getElementById ( 'st-all' ) ; if ( ! pick ) return ;
ST . forEach ( function ( p , i ) { var o = document . createElement ( 'option' ) ; o . value = i ; o . textContent = p . eq ; pick . appendChild ( o ) ; } ) ;
var cur = 0 , shown = 0 ;
function render ( ) {
var p = ST [ cur ] ;
var html = '<b>' + p . eq + '</b><br><span style="color:var(--muted)">' + p . given + '</span><div style="margin-top:8px">' ;
for ( var i = 0 ; i < shown ; i ++ ) html += '<div class="exa" style="margin-top:6px"><div class="step">' + p . steps [ i ] + '</div></div>' ;
if ( shown === 0 ) html += '<span style="color:var(--muted)">Нажмите «Следующий шаг», чтобы решать пошагово.</span>' ;
html += '</div>' ; out . className = 'out' ; out . innerHTML = html ;
if ( shown >= p . steps . length ) { out . className = 'out ok' ; }
}
pick . addEventListener ( 'change' , function ( ) { cur = + pick . value ; shown = 0 ; render ( ) ; } ) ;
bStep . addEventListener ( 'click' , function ( ) { if ( shown < ST [ cur ] . steps . length ) { shown ++ ; render ( ) ; } } ) ;
bAll . addEventListener ( 'click' , function ( ) { shown = ST [ cur ] . steps . length ; render ( ) ; } ) ;
render ( ) ;
} ) ( ) ;
/* ===== BOSS ===== */
var BOSS = [
{ q : 'Чему равна молярная масса серной кислоты $\\text{H}_2\\text{SO}_4$? (г /моль)' , ans : 98 , tol : 0.5 , hint : 'M = 2·1 + 32 + 4·16 = 98.' } ,
{ q : 'Сколько моль вещества в $80$ г $\\text{NaOH}$ ($M=40$ г /моль)?' , ans : 2 , tol : 0.05 , hint : 'n = m/M = 80/40 = 2.' } ,
{ q : 'Какой объём (л, н.у.) занимают $3$ моль кислорода $\\text{O}_2$?' , ans : 67.2 , tol : 0.3 , hint : 'V = n·Vm = 3·22,4 = 67,2.' } ,
{ q : 'Сколько молекул в $2$ моль воды? Ответ в виде коэффициента при ·10²³.' , ans : 12.04 , tol : 0.1 , hint : 'N = n·N_A = 2·6,02 = 12,04 (·10²³).' }
] ;
var BKEY = 'chemistry8_intro_boss' , BACH = 'chemistry8_intro_ach' ;
function bossState ( ) { try { return JSON . parse ( localStorage . getItem ( BKEY ) || '{}' ) || { } ; } catch ( e ) { return { } ; } }
function bossSave ( s ) { try { localStorage . setItem ( BKEY , JSON . stringify ( s ) ) ; } catch ( e ) { } }
( function ( ) {
var cont = document . getElementById ( 'boss-cont' ) ; if ( ! cont ) return ;
var st = bossState ( ) ;
BOSS . forEach ( function ( b , i ) {
var solved = ! ! st [ i ] ;
var d = document . createElement ( 'div' ) ; d . className = 'bcard' + ( solved ? ' solved' : '' ) ; d . id = 'b' + i ;
d . innerHTML = '<div class="q"><b>Задача ' + ( i + 1 ) + '.</b> ' + b . q + '</div>' +
'<div class="row"><input type="text" inputmode="decimal" placeholder="ответ" style="width:120px;font-family:var(--mono)"' + ( solved ? ' value="' + b . ans + '" disabled' : '' ) + '>' +
'<button class="btn primary go"' + ( solved ? ' disabled' : '' ) + '>Ответить</button><button class="btn hint">Подсказка</button></div>' +
'<div class="hintbox">' + b . hint + '</div>' +
'<div class="fb' + ( solved ? ' show ok' : '' ) + '">' + ( solved ? 'Решено! +10 XP' : '' ) + '</div>' ;
cont . appendChild ( d ) ;
var inp = d . querySelector ( 'input' ) , go = d . querySelector ( '.go' ) , hint = d . querySelector ( '.hint' ) , hb = d . querySelector ( '.hintbox' ) , fb = d . querySelector ( '.fb' ) ;
hint . addEventListener ( 'click' , function ( ) { hb . classList . toggle ( 'show' ) ; } ) ;
if ( ! solved ) go . addEventListener ( 'click' , function ( ) {
var v = parseFloat ( ( inp . value || '' ) . replace ( ',' , '.' ) ) ;
if ( isNaN ( v ) ) { fb . className = 'fb show bad' ; fb . textContent = 'Введите число.' ; return ; }
if ( Math . abs ( v - b . ans ) <= b . tol ) {
fb . className = 'fb show ok' ; fb . textContent = 'Верно! +10 XP' ; d . classList . add ( 'solved' ) ; go . disabled = true ; inp . disabled = true ;
var s = bossState ( ) ; if ( ! s [ i ] ) { s [ i ] = true ; bossSave ( s ) ; addXp ( 10 , 'boss' + i ) ; updateBoss ( s ) ; }
} else { fb . className = 'fb show bad' ; fb . textContent = 'Не то. Проверь расчёт и попробуй снова.' ; }
} ) ;
inp . addEventListener ( 'keydown' , function ( e ) { if ( e . key === 'Enter' && ! go . disabled ) go . click ( ) ; } ) ;
} ) ;
updateBoss ( st ) ;
if ( window . renderMathInElement ) try { renderMathInElement ( cont ) ; } catch ( e ) { }
} ) ( ) ;
function updateBoss ( s ) {
var won = 0 ; for ( var k in s ) if ( s [ k ] ) won ++ ;
var lab = document . getElementById ( 'boss-lab' ) , fill = document . getElementById ( 'boss-fill' ) ;
if ( lab ) lab . textContent = 'Решено: ' + won + ' / ' + BOSS . length ;
if ( fill ) fill . style . width = Math . round ( won * 100 / BOSS . length ) + '%' ;
if ( won >= BOSS . length && localStorage . getItem ( BACH ) !== '1' ) {
localStorage . setItem ( BACH , '1' ) ; addXp ( 30 , 'ach' ) ;
var cta = document . getElementById ( 'boss-cta' ) ; if ( cta ) cta . classList . add ( 'show' ) ;
popup ( 'Ачивка «Счёт в химии»! +30 XP' ) ;
try { if ( window . confetti ) confetti ( { particleCount : 180 , spread : 100 , origin : { y : . 6 } } ) ; } catch ( e ) { }
} else if ( won >= BOSS . length ) { var c = document . getElementById ( 'boss-cta' ) ; if ( c ) c . classList . add ( 'show' ) ; }
}
/* init */
loadLocal ( ) ; refreshXp ( ) ; refreshProgress ( ) ; loadServer ( ) ;
window . addEventListener ( 'focus' , loadServer ) ;
} ) ; /* end DOMContentLoaded */
< / script >
< / body >