feat(geom9): полировка — анимации появления, bump-эффекты, hover, плавные переходы

This commit is contained in:
Maxim Dolgolyov
2026-05-29 11:13:39 +03:00
parent 294b3622b5
commit a4be2ecba0
5 changed files with 717 additions and 0 deletions
+158
View File
@@ -184,6 +184,38 @@ a{color:inherit;text-decoration:none}
.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}
/* === GEOM9 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}
.psel-card{transition:transform .2s,box-shadow .2s,border-color .2s,background .25s}
.sec{transition:opacity .25s}
</style>
</head>
<body>
@@ -2679,6 +2711,132 @@ function init(){
}
}
document.addEventListener('DOMContentLoaded', init);
/* === GEOM9 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.__geom9BumpScore = bumpScore;
/* Observe .score-display b text changes — auto bump */
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();
/* Re-scan on DOM additions (lazy-built sections) */
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){}
/* psel-card 'done' marker — based on STATE.progress >= 50 */
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){}
}
/* Wrap refreshProgressUI if present */
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); });
/* Smooth-scroll on psel-card click */
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);
});
/* Boss-card glow when defeated */
function observeBossGlow(){
var cards = document.querySelectorAll('.boss-card');
cards.forEach(function(card){
if(card.__bossObs) return;
card.__bossObs = true;
try{
var mo = new MutationObserver(function(){
if(card.__glowed) return;
var go = card.querySelector('button[id*="-go"]');
var fb = card.querySelector('.feedback.ok, .boss-fb.ok');
var defeatedByBtn = go && go.disabled;
var defeatedByFb = fb && fb.textContent && /Побед|повержен/i.test(fb.textContent);
if(defeatedByBtn || defeatedByFb){
card.__glowed = true;
card.classList.add('glow','pulse');
setTimeout(function(){ try{ card.classList.remove('pulse'); }catch(e){} }, 900);
setTimeout(function(){ try{ card.classList.remove('glow'); }catch(e){} }, 1400);
}
});
mo.observe(card, {childList:true, subtree:true, attributes:true, attributeFilter:['class','style','disabled']});
/* Initial: if already defeated (loaded from state) — skip glow (no animation on page load) */
var goNow = card.querySelector('button[id*="-go"]');
if(goNow && goNow.disabled) card.__glowed = true;
}catch(e){}
});
}
var bossRescan = function(){ try{ observeBossGlow(); }catch(e){} };
setTimeout(bossRescan, 800);
var bossPoll = setInterval(bossRescan, 2500);
setTimeout(function(){ try{ clearInterval(bossPoll); }catch(e){} }, 90000);
})();
</script>
</body>
+151
View File
@@ -165,6 +165,38 @@ a{color:inherit;text-decoration:none}
.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}
/* === GEOM9 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}
.psel-card{transition:transform .2s,box-shadow .2s,border-color .2s,background .25s}
.sec{transition:opacity .25s}
</style>
</head>
<body>
@@ -1615,6 +1647,125 @@ function init(){
}
}
document.addEventListener('DOMContentLoaded', init);
/* === GEOM9 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.__geom9BumpScore = 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);
});
function observeBossGlow(){
var cards = document.querySelectorAll('.boss-card');
cards.forEach(function(card){
if(card.__bossObs) return;
card.__bossObs = true;
try{
var mo = new MutationObserver(function(){
if(card.__glowed) return;
var go = card.querySelector('button[id*="-go"]');
var fb = card.querySelector('.feedback.ok, .boss-fb.ok');
var defeatedByBtn = go && go.disabled;
var defeatedByFb = fb && fb.textContent && /Побед|повержен/i.test(fb.textContent);
if(defeatedByBtn || defeatedByFb){
card.__glowed = true;
card.classList.add('glow','pulse');
setTimeout(function(){ try{ card.classList.remove('pulse'); }catch(e){} }, 900);
setTimeout(function(){ try{ card.classList.remove('glow'); }catch(e){} }, 1400);
}
});
mo.observe(card, {childList:true, subtree:true, attributes:true, attributeFilter:['class','style','disabled']});
var goNow = card.querySelector('button[id*="-go"]');
if(goNow && goNow.disabled) card.__glowed = true;
}catch(e){}
});
}
var bossRescan = function(){ try{ observeBossGlow(); }catch(e){} };
setTimeout(bossRescan, 800);
var bossPoll = setInterval(bossRescan, 2500);
setTimeout(function(){ try{ clearInterval(bossPoll); }catch(e){} }, 90000);
})();
</script>
</body>
+151
View File
@@ -165,6 +165,38 @@ a{color:inherit;text-decoration:none}
.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}
/* === GEOM9 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}
.psel-card{transition:transform .2s,box-shadow .2s,border-color .2s,background .25s}
.sec{transition:opacity .25s}
</style>
</head>
<body>
@@ -1612,6 +1644,125 @@ function init(){
}
}
document.addEventListener('DOMContentLoaded', init);
/* === GEOM9 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.__geom9BumpScore = 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);
});
function observeBossGlow(){
var cards = document.querySelectorAll('.boss-card');
cards.forEach(function(card){
if(card.__bossObs) return;
card.__bossObs = true;
try{
var mo = new MutationObserver(function(){
if(card.__glowed) return;
var go = card.querySelector('button[id*="-go"]');
var fb = card.querySelector('.feedback.ok, .boss-fb.ok');
var defeatedByBtn = go && go.disabled;
var defeatedByFb = fb && fb.textContent && /Побед|повержен/i.test(fb.textContent);
if(defeatedByBtn || defeatedByFb){
card.__glowed = true;
card.classList.add('glow','pulse');
setTimeout(function(){ try{ card.classList.remove('pulse'); }catch(e){} }, 900);
setTimeout(function(){ try{ card.classList.remove('glow'); }catch(e){} }, 1400);
}
});
mo.observe(card, {childList:true, subtree:true, attributes:true, attributeFilter:['class','style','disabled']});
var goNow = card.querySelector('button[id*="-go"]');
if(goNow && goNow.disabled) card.__glowed = true;
}catch(e){}
});
}
var bossRescan = function(){ try{ observeBossGlow(); }catch(e){} };
setTimeout(bossRescan, 800);
var bossPoll = setInterval(bossRescan, 2500);
setTimeout(function(){ try{ clearInterval(bossPoll); }catch(e){} }, 90000);
})();
</script>
</body>
+151
View File
@@ -180,6 +180,38 @@ a{color:inherit;text-decoration:none}
.drop-box:hover{border-color:var(--pri);background:var(--pri-soft)}
.drop-box h5{font-family:'Unbounded',sans-serif;font-size:.78rem;color:var(--pri2);margin-bottom:8px;text-transform:uppercase;letter-spacing:.05em}
.drop-box.over{border-color:var(--pri);background:var(--pri-soft);border-style:solid;transform:scale(1.015)}
/* === GEOM9 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}
.psel-card{transition:transform .2s,box-shadow .2s,border-color .2s,background .25s}
.sec{transition:opacity .25s}
</style>
</head>
<body>
@@ -1925,6 +1957,125 @@ function init(){
}
}
document.addEventListener('DOMContentLoaded', init);
/* === GEOM9 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.__geom9BumpScore = 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);
});
function observeBossGlow(){
var cards = document.querySelectorAll('.boss-card');
cards.forEach(function(card){
if(card.__bossObs) return;
card.__bossObs = true;
try{
var mo = new MutationObserver(function(){
if(card.__glowed) return;
var go = card.querySelector('button[id*="-go"]');
var fb = card.querySelector('.feedback.ok, .boss-fb.ok');
var defeatedByBtn = go && go.disabled;
var defeatedByFb = fb && fb.textContent && /Побед|повержен/i.test(fb.textContent);
if(defeatedByBtn || defeatedByFb){
card.__glowed = true;
card.classList.add('glow','pulse');
setTimeout(function(){ try{ card.classList.remove('pulse'); }catch(e){} }, 900);
setTimeout(function(){ try{ card.classList.remove('glow'); }catch(e){} }, 1400);
}
});
mo.observe(card, {childList:true, subtree:true, attributes:true, attributeFilter:['class','style','disabled']});
var goNow = card.querySelector('button[id*="-go"]');
if(goNow && goNow.disabled) card.__glowed = true;
}catch(e){}
});
}
var bossRescan = function(){ try{ observeBossGlow(); }catch(e){} };
setTimeout(bossRescan, 800);
var bossPoll = setInterval(bossRescan, 2500);
setTimeout(function(){ try{ clearInterval(bossPoll); }catch(e){} }, 90000);
})();
</script>
</body>
+106
View File
@@ -199,6 +199,34 @@ html.dark .final-cta-sub{color:#fcd34d}
.final-cta-btn{padding:10px 18px;border-radius:10px;background:linear-gradient(135deg,var(--pri),#fb7185);color:#fff;text-decoration:none;font-weight:800;font-size:.9rem;display:inline-flex;align-items:center;gap:7px;transition:filter .15s}
.final-cta-btn:hover{filter:brightness(1.1)}
.final-cta-btn svg{width:16px;height:16px;stroke:currentColor;fill:none;stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
/* === GEOM9 POLISH (hub) === */
@keyframes chCardIn{from{opacity:0;transform:translateY(10px) scale(.985)}to{opacity:1;transform:none}}
.ch-card{animation:chCardIn .4s cubic-bezier(.16,1,.3,1) backwards}
.ch-card:nth-child(1){animation-delay:.04s}
.ch-card:nth-child(2){animation-delay:.10s}
.ch-card:nth-child(3){animation-delay:.16s}
.ch-card:nth-child(4){animation-delay:.22s}
@keyframes bossCardIn{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:none}}
.final-wrap.open #fin-bosses-container .boss-card{animation:bossCardIn .35s cubic-bezier(.16,1,.3,1) backwards}
.final-wrap.open #fin-bosses-container .boss-card:nth-child(1){animation-delay:.06s}
.final-wrap.open #fin-bosses-container .boss-card:nth-child(2){animation-delay:.12s}
.final-wrap.open #fin-bosses-container .boss-card:nth-child(3){animation-delay:.18s}
.final-wrap.open #fin-bosses-container .boss-card:nth-child(4){animation-delay:.24s}
.final-wrap.open #fin-bosses-container .boss-card:nth-child(5){animation-delay:.30s}
.final-wrap.open #fin-bosses-container .boss-card:nth-child(6){animation-delay:.36s}
.final-wrap.open #fin-bosses-container .boss-card:nth-child(7){animation-delay:.42s}
.po-fill,.ch-prog-fill,.boss-overall-bar .fill{transition:width .6s cubic-bezier(.16,1,.3,1)!important}
.boss-btn,.ch-action,.final-cta-btn{position:relative;overflow:hidden}
.boss-btn::after,.ch-action::after,.final-cta-btn::after{content:'';position:absolute;inset:0;background:radial-gradient(circle at center,rgba(255,255,255,.45) 0%,transparent 60%);opacity:0;transition:opacity .25s;pointer-events:none}
.boss-btn:hover::after,.ch-action:hover::after,.final-cta-btn:hover::after{opacity:1}
.cheat-list li .katex{transition:color .2s}
.cheat-list li .katex:hover{color:var(--pri-d);cursor:help}
.boss-card.glow-once{box-shadow:0 0 24px rgba(16,185,129,.6),0 0 0 2px rgba(16,185,129,.45)!important}
@keyframes bossPulseH{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-once{animation:bossPulseH .85s ease-out}
.fin-boss-bar-bump b{transition:transform .2s,color .2s;display:inline-block}
.fin-boss-bar-bump b.bump{transform:scale(1.18);color:#10b981}
</style>
</head>
<body>
@@ -836,6 +864,84 @@ if (document.readyState === 'loading') {
loadProgress();
}
window.addEventListener('focus', loadProgress);
/* === GEOM9 HUB POLISH JS === */
(function(){
/* Smooth scroll into view when final opens */
try{
var finalHead = document.getElementById('final-head');
var finalWrap = document.getElementById('course-final');
if(finalHead && finalWrap){
finalHead.addEventListener('click', function(){
setTimeout(function(){
if(finalWrap.classList.contains('open')){
try{ finalWrap.scrollIntoView({behavior:'smooth', block:'start'}); }catch(e){}
}
}, 80);
});
}
}catch(e){}
/* Boss-card glow on defeat (one-shot, only on user action, not page-load state) */
function observeFinBosses(){
var cards = document.querySelectorAll('#fin-bosses-container .boss-card');
cards.forEach(function(card){
if(card.__finObs) return;
card.__finObs = true;
try{
var go = card.querySelector('button[id*="-go"]');
/* If already defeated at observation start — suppress glow */
if(go && go.disabled) card.__glowed = true;
var mo = new MutationObserver(function(){
if(card.__glowed) return;
var g = card.querySelector('button[id*="-go"]');
if(g && g.disabled){
card.__glowed = true;
card.classList.add('glow-once','pulse-once');
setTimeout(function(){ try{ card.classList.remove('pulse-once'); }catch(e){} }, 900);
setTimeout(function(){ try{ card.classList.remove('glow-once'); }catch(e){} }, 1500);
}
});
mo.observe(card, {childList:true, subtree:true, attributes:true, attributeFilter:['class','disabled']});
}catch(e){}
});
}
/* Bump for boss overall counter */
var lastLab = '';
function bumpLab(){
try{
var lab = document.getElementById('fin-boss-lab');
if(!lab) return;
var nv = lab.textContent;
if(nv === lastLab){ return; }
lastLab = nv;
if(!lab.parentElement.classList.contains('fin-boss-bar-bump')){
lab.parentElement.classList.add('fin-boss-bar-bump');
}
/* wrap digits in a <b> if not already — simplest: bump the whole label briefly */
lab.style.transition = 'transform .25s ease, color .25s';
lab.style.transform = 'scale(1.04)';
lab.style.color = '#10b981';
setTimeout(function(){
try{ lab.style.transform=''; lab.style.color=''; }catch(e){}
}, 320);
}catch(e){}
}
try{
var labEl = document.getElementById('fin-boss-lab');
if(labEl){
lastLab = labEl.textContent;
var labObs = new MutationObserver(bumpLab);
labObs.observe(labEl, {childList:true, characterData:true, subtree:true});
}
}catch(e){}
/* Rescan periodically (final-body uses lazy render) */
setTimeout(observeFinBosses, 600);
var pollT = setInterval(observeFinBosses, 1500);
setTimeout(function(){ try{ clearInterval(pollT); }catch(e){} }, 90000);
})();
</script>
</body>