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
+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>