391 lines
18 KiB
HTML
391 lines
18 KiB
HTML
<meta charset="utf-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
<title>PRAGATIVEER · Reveal</title>
|
|
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
<link href="https://fonts.googleapis.com/css2?family=Space+Grotesk:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
<style>
|
|
:root{
|
|
--navy-900:#081226; --navy-800:#0c1c39; --navy-700:#13294d;
|
|
--gold-200:#fdeeb8; --gold-300:#f6dd93; --gold-400:#e8c463; --gold-500:#d4af37;
|
|
--gold-600:#b1892a; --gold-700:#7d5f18;
|
|
--ink:#e9eefb; --badge:230px; --grow:1.18; --halo-w:170%;
|
|
--font:"Space Grotesk", ui-sans-serif, system-ui, -apple-system, Segoe UI, Roboto, sans-serif;
|
|
}
|
|
*{box-sizing:border-box}
|
|
html,body{height:100%}
|
|
body{
|
|
margin:0; font-family:var(--font); color:var(--ink);
|
|
background:radial-gradient(120% 90% at 50% -10%, #16305c 0%, var(--navy-800) 42%, var(--navy-900) 100%);
|
|
display:flex; align-items:center; justify-content:center; overflow:hidden;
|
|
-webkit-font-smoothing:antialiased; transition:background .6s ease;
|
|
}
|
|
/* sad sky */
|
|
body.sad{background:radial-gradient(120% 90% at 50% -10%, #22303f 0%, #131c26 46%, #0a1017 100%)}
|
|
body::after{
|
|
content:""; position:fixed; inset:0; pointer-events:none; z-index:0;
|
|
background:radial-gradient(70% 60% at 50% 44%, transparent 55%, rgba(0,0,0,.45) 100%);
|
|
mix-blend-mode:multiply;
|
|
}
|
|
|
|
#fw{position:fixed; inset:0; z-index:5; pointer-events:none}
|
|
|
|
/* euphoric gold flash */
|
|
.flash{position:fixed; inset:0; z-index:6; pointer-events:none; opacity:0;
|
|
background:radial-gradient(circle at 50% 46%, rgba(255,240,190,.6), rgba(212,175,55,.15) 45%, transparent 70%)}
|
|
.stage.play.tier3 .flash{animation:flash 760ms ease-out 2600ms}
|
|
|
|
.stage{
|
|
position:relative; z-index:10; width:min(92vw,560px); height:100dvh;
|
|
display:flex; flex-direction:column; align-items:center; justify-content:center;
|
|
gap:26px; padding:24px; text-align:center;
|
|
}
|
|
|
|
.scene{
|
|
position:relative; width:var(--badge); height:var(--badge);
|
|
perspective:1300px; perspective-origin:50% 46%;
|
|
display:flex; align-items:center; justify-content:center;
|
|
}
|
|
.stage.play.tier3 .scene{animation:shake 520ms ease-out 2620ms}
|
|
|
|
.halo{
|
|
position:absolute; top:50%; left:50%; width:var(--halo-w); aspect-ratio:1;
|
|
transform:translate(-50%,-50%) scale(.3); opacity:0; border-radius:50%; filter:blur(4px); z-index:0;
|
|
background:radial-gradient(circle,
|
|
rgba(246,221,147,.55) 0%, rgba(212,175,55,.35) 34%, rgba(212,175,55,.08) 58%, transparent 72%);
|
|
}
|
|
.stage.play.happy .halo{animation:bloom 850ms cubic-bezier(.2,.8,.3,1) 1850ms forwards, pulse 2.8s ease-in-out 2700ms infinite}
|
|
.stage.sad .halo{
|
|
background:radial-gradient(circle, rgba(130,160,200,.30) 0%, rgba(96,124,164,.14) 42%, transparent 70%);
|
|
}
|
|
.stage.play.sad .halo{animation:bloomcold 1000ms ease-out 1900ms forwards}
|
|
|
|
.scaler{position:relative; width:100%; height:100%; z-index:1; transform:scale(1)}
|
|
.stage.play.happy .scaler{animation:grow 680ms cubic-bezier(.34,1.56,.5,1) 2050ms forwards, litglow 700ms ease-out 1900ms forwards}
|
|
.stage.play.sad .scaler{animation:droop 950ms cubic-bezier(.3,.7,.4,1) 2050ms forwards, dimglow 800ms ease-out 1900ms forwards}
|
|
|
|
.spinner{position:absolute; inset:0; will-change:transform; transform:rotateY(0deg) scale(0)}
|
|
.stage.play .spinner{animation:spin 1800ms cubic-bezier(.14,.62,.2,1) 150ms forwards}
|
|
|
|
.badge{
|
|
position:absolute; inset:0; border-radius:50%;
|
|
background: url('/PragatiVeer_Logo/SVG_Logos/Square_dark_background.svg') center/cover no-repeat !important;
|
|
}
|
|
.stage.sad .badge{
|
|
position:absolute; inset:0; border-radius:50%;
|
|
background: url('/PragatiVeer_Logo/SVG_Logos/Square_dark_background.svg') center/cover no-repeat !important;
|
|
}
|
|
|
|
.count{
|
|
position:relative; z-index:1; display:flex; flex-direction:column; align-items:center; gap:6px;
|
|
opacity:0; transform:translateY(14px) scale(.9);
|
|
}
|
|
.stage.play .count{animation:reveal 620ms cubic-bezier(.34,1.4,.5,1) 2650ms forwards}
|
|
.number{
|
|
font-size:clamp(56px,17vw,88px); font-weight:700; line-height:.92; letter-spacing:-.02em;
|
|
background:linear-gradient(180deg,var(--gold-200),var(--gold-500) 55%,var(--gold-600));
|
|
-webkit-background-clip:text; background-clip:text; color:transparent;
|
|
font-variant-numeric:tabular-nums; filter:drop-shadow(0 4px 18px rgba(212,175,55,.35));
|
|
}
|
|
.stage.sad .number{
|
|
background:linear-gradient(180deg,#c7d0e0,#8b97ad 58%,#68738a);
|
|
-webkit-background-clip:text; background-clip:text; filter:drop-shadow(0 3px 12px rgba(80,105,140,.3));
|
|
}
|
|
.caption{
|
|
font-size:12px; font-weight:600; letter-spacing:.34em; text-transform:uppercase;
|
|
color:var(--gold-400); text-indent:.34em; min-height:14px; transition:color .5s ease;
|
|
}
|
|
.stage.sad .caption{color:#95a2ba}
|
|
|
|
.panel{
|
|
position:absolute; bottom:22px; left:50%; transform:translateX(-50%);
|
|
display:flex; gap:8px; align-items:center; flex-wrap:wrap; justify-content:center;
|
|
background:rgba(9,20,40,.55); border:1px solid rgba(232,196,99,.22);
|
|
padding:8px 8px 8px 14px; border-radius:14px; backdrop-filter:blur(8px); box-shadow:0 10px 30px rgba(0,0,0,.4);
|
|
}
|
|
.panel label{font-size:11px; letter-spacing:.14em; text-transform:uppercase; color:rgba(233,238,251,.55)}
|
|
.panel input{
|
|
width:110px; background:rgba(255,255,255,.06); border:1px solid rgba(232,196,99,.25);
|
|
color:var(--ink); font-family:var(--font); font-size:14px; font-weight:500; padding:8px 10px; border-radius:9px; outline:none;
|
|
}
|
|
.panel input.cap{width:104px; text-transform:uppercase; letter-spacing:.1em; font-size:12px}
|
|
.panel input:focus{border-color:var(--gold-400); box-shadow:0 0 0 3px rgba(212,175,55,.18)}
|
|
.replay{
|
|
border:none; cursor:pointer; font-family:var(--font); font-weight:600; font-size:13px; letter-spacing:.06em;
|
|
color:#20130a; padding:9px 16px; border-radius:9px;
|
|
background:linear-gradient(180deg,var(--gold-300),var(--gold-500)); box-shadow:0 4px 14px rgba(212,175,55,.35);
|
|
transition:transform .12s ease, box-shadow .12s ease;
|
|
}
|
|
.replay:hover{transform:translateY(-1px); box-shadow:0 7px 18px rgba(212,175,55,.45)}
|
|
.replay:active{transform:translateY(0)}
|
|
.replay:focus-visible, .panel input:focus-visible{outline:2px solid var(--gold-300); outline-offset:2px}
|
|
.hint{font-size:10.5px; color:rgba(233,238,251,.4); letter-spacing:.02em; margin-top:2px}
|
|
|
|
@media (max-width:460px){ :root{--badge:180px} .panel input{width:94px} .panel input.cap{width:88px} }
|
|
|
|
@keyframes spin{ 0%{transform:rotateY(0deg) scale(0)} 100%{transform:rotateY(1440deg) scale(1)} }
|
|
@keyframes bloom{
|
|
0%{opacity:0; transform:translate(-50%,-50%) scale(.3)}
|
|
60%{opacity:1; transform:translate(-50%,-50%) scale(1.06)}
|
|
100%{opacity:.9; transform:translate(-50%,-50%) scale(1)}
|
|
}
|
|
@keyframes bloomcold{
|
|
0%{opacity:0; transform:translate(-50%,-50%) scale(.3)}
|
|
100%{opacity:.5; transform:translate(-50%,-50%) scale(.82)}
|
|
}
|
|
@keyframes pulse{ 0%,100%{opacity:.75; transform:translate(-50%,-50%) scale(1)} 50%{opacity:1; transform:translate(-50%,-50%) scale(1.05)} }
|
|
@keyframes grow{ 0%{transform:scale(1)} 70%{transform:scale(calc(var(--grow) + .06))} 100%{transform:scale(var(--grow))} }
|
|
@keyframes droop{ 0%{transform:scale(1) translateY(0)} 100%{transform:scale(.93) translateY(9px)} }
|
|
@keyframes litglow{ 0%{filter:drop-shadow(0 0 0 rgba(212,175,55,0))} 100%{filter:drop-shadow(0 0 26px rgba(233,196,99,.6))} }
|
|
@keyframes dimglow{ 0%{filter:drop-shadow(0 0 0 rgba(120,150,190,0))} 100%{filter:drop-shadow(0 6px 16px rgba(20,30,45,.5))} }
|
|
@keyframes reveal{ 0%{opacity:0; transform:translateY(14px) scale(.9)} 100%{opacity:1; transform:translateY(0) scale(1)} }
|
|
@keyframes flash{ 0%{opacity:0} 18%{opacity:.85} 100%{opacity:0} }
|
|
@keyframes shake{
|
|
0%,100%{transform:translate(0,0)} 20%{transform:translate(-4px,2px)} 40%{transform:translate(4px,-2px)}
|
|
60%{transform:translate(-3px,-1px)} 80%{transform:translate(3px,1px)}
|
|
}
|
|
|
|
@media (prefers-reduced-motion:reduce){
|
|
.stage.play .spinner{animation:none; transform:rotateY(0) scale(1)}
|
|
.stage.play.happy .scaler{animation:none; transform:scale(var(--grow)); filter:drop-shadow(0 0 26px rgba(233,196,99,.6))}
|
|
.stage.play.sad .scaler{animation:none; transform:scale(.93) translateY(9px)}
|
|
.stage.play.happy .halo{animation:none; opacity:.9; transform:translate(-50%,-50%) scale(1)}
|
|
.stage.play.sad .halo{animation:none; opacity:.5; transform:translate(-50%,-50%) scale(.82)}
|
|
.stage.play .count{animation:none; opacity:1; transform:none}
|
|
.stage.play.tier3 .scene,.stage.play.tier3 .flash{animation:none}
|
|
}
|
|
|
|
body {
|
|
background: transparent !important;
|
|
}
|
|
body.sad {
|
|
background: transparent !important;
|
|
}
|
|
body::after {
|
|
display: none !important;
|
|
}
|
|
#stage, .hint {
|
|
background: transparent !important;
|
|
box-shadow: none !important;
|
|
}
|
|
.count, .panel, .hint {
|
|
display: none !important;
|
|
}
|
|
|
|
</style>
|
|
|
|
<canvas id="fw"></canvas>
|
|
<div class="flash"></div>
|
|
|
|
<div class="stage" id="stage">
|
|
<div class="scene">
|
|
<div class="halo"></div>
|
|
<div class="scaler"><div class="spinner"><div class="badge"></div></div></div>
|
|
</div>
|
|
|
|
<div class="count">
|
|
<div class="number" id="number">0</div>
|
|
<div class="caption" id="caption">GROWTH SCORE</div>
|
|
</div>
|
|
|
|
<div class="panel">
|
|
<label for="num">Number</label>
|
|
<input id="num" type="text" inputmode="numeric" value="2,026" aria-label="Target number">
|
|
<input id="cap" class="cap" type="text" value="GROWTH SCORE" aria-label="Caption" maxlength="16">
|
|
<button class="replay" id="replay">Replay</button>
|
|
</div>
|
|
</div>
|
|
<div class="hint" style="position:absolute;bottom:6px;left:50%;transform:translateX(-50%);z-index:11">
|
|
under 5 = sad · higher = bigger celebration
|
|
</div>
|
|
|
|
<script>
|
|
const stage=document.getElementById('stage');
|
|
const body=document.body;
|
|
const numEl=document.getElementById('number');
|
|
const capEl=document.getElementById('caption');
|
|
const numInput=document.getElementById('num');
|
|
const capInput=document.getElementById('cap');
|
|
const replay=document.getElementById('replay');
|
|
const reduce=window.matchMedia('(prefers-reduced-motion:reduce)');
|
|
|
|
/* ---------- particle canvas (fireworks + rain + confetti) ---------- */
|
|
const cv=document.getElementById('fw'), ctx=cv.getContext('2d');
|
|
let W,H,DPR;
|
|
function resize(){
|
|
DPR=Math.min(2,window.devicePixelRatio||1);
|
|
W=cv.width=Math.floor(innerWidth*DPR); H=cv.height=Math.floor(innerHeight*DPR);
|
|
cv.style.width=innerWidth+'px'; cv.style.height=innerHeight+'px';
|
|
}
|
|
resize(); addEventListener('resize',resize);
|
|
|
|
const SPARK=['#f6dd93','#e8c463','#d4af37','#fff4cf','#ffffff','#8fb4ff'];
|
|
const CONF =['#f6dd93','#e8c463','#d4af37','#ffffff','#ffd34d'];
|
|
let parts=[], running=false, mode='fireworks', rafFW=null, fwTimers=[], fadeCol='rgba(8,18,38,0.22)';
|
|
|
|
function burst(xr,yr,count){
|
|
const x=xr*W, y=yr*H, n=count|0;
|
|
for(let i=0;i<n;i++){
|
|
const a=Math.PI*2*(i/n)+Math.random()*0.25, sp=(2+Math.random()*4.4)*DPR;
|
|
parts.push({t:'spark', x,y, vx:Math.cos(a)*sp, vy:Math.sin(a)*sp-1.1*DPR,
|
|
life:1, decay:0.010+Math.random()*0.013, col:SPARK[(Math.random()*SPARK.length)|0], size:(1.3+Math.random()*1.9)*DPR});
|
|
}
|
|
}
|
|
function confetti(m){
|
|
for(let i=0;i<m;i++){
|
|
parts.push({t:'conf', x:(0.3+Math.random()*0.4)*W, y:-20*DPR,
|
|
vx:(-1.2+Math.random()*2.4)*DPR, vy:(1.5+Math.random()*2.5)*DPR,
|
|
rot:Math.random()*7, vr:(-0.2+Math.random()*0.4), life:1,
|
|
w:(4+Math.random()*4)*DPR, h:(7+Math.random()*6)*DPR, col:CONF[(Math.random()*CONF.length)|0]});
|
|
}
|
|
}
|
|
// s = sadness 0..1 (1 = most sad). One persistent face per side.
|
|
function drawSadFace(p){
|
|
const r=p.r, s=p.s, alpha=Math.min(1,p.fi);
|
|
const yb=Math.sin(p.phase)*3*DPR + s*3*DPR; // gentle bob + slight downward droop
|
|
ctx.save(); ctx.translate(p.x, p.y+yb); ctx.globalAlpha=alpha;
|
|
const line=Math.max(1.6,r*0.075), stroke='rgba(182,200,228,0.92)';
|
|
|
|
// face
|
|
ctx.beginPath(); ctx.arc(0,0,r,0,7);
|
|
ctx.fillStyle='rgba(150,175,210,0.13)'; ctx.fill();
|
|
ctx.lineWidth=line; ctx.strokeStyle=stroke; ctx.stroke();
|
|
|
|
const ex=r*0.42, ey=-r*0.12, er=r*0.10;
|
|
// worried brows: inner ends raised, steeper when sadder
|
|
const bl=r*0.30, tilt=(0.15+s*0.55);
|
|
ctx.lineWidth=Math.max(1.6,r*0.08); ctx.strokeStyle=stroke; ctx.lineCap='round';
|
|
ctx.beginPath(); ctx.moveTo(-ex-bl*0.5, ey-r*0.30 + bl*tilt); ctx.lineTo(-ex+bl*0.5, ey-r*0.30 - bl*tilt*0.6); ctx.stroke();
|
|
ctx.beginPath(); ctx.moveTo( ex-bl*0.5, ey-r*0.30 - bl*tilt*0.6); ctx.lineTo( ex+bl*0.5, ey-r*0.30 + bl*tilt); ctx.stroke();
|
|
|
|
// eyes (droopier when sadder: a lid line cuts the top)
|
|
ctx.fillStyle='rgba(205,218,238,0.95)';
|
|
ctx.beginPath(); ctx.arc(-ex,ey,er,0,7); ctx.fill();
|
|
ctx.beginPath(); ctx.arc( ex,ey,er,0,7); ctx.fill();
|
|
|
|
// mouth: frown depth scales with sadness (quadratic, peak above corners)
|
|
const mw=r*0.42, my=r*0.5, dip=r*(0.10 + s*0.55);
|
|
ctx.beginPath(); ctx.moveTo(-mw,my); ctx.quadraticCurveTo(0, my-dip, mw, my);
|
|
ctx.lineWidth=line; ctx.strokeStyle=stroke; ctx.stroke();
|
|
|
|
// tears: none at low sadness, one, then two — with a looping drip
|
|
const tears = s>0.66 ? 2 : s>0.3 ? 1 : 0;
|
|
if(tears){
|
|
const drop=(p.tp % 1);
|
|
ctx.fillStyle='rgba(150,205,244,0.92)';
|
|
const cheeks = tears===2 ? [-ex, ex] : [-ex];
|
|
for(const cx of cheeks){
|
|
const ty=ey+er + drop*r*0.9, tr=r*0.085*(1-drop*0.35);
|
|
ctx.globalAlpha=alpha*(1-drop*0.7);
|
|
ctx.beginPath(); ctx.arc(cx, ty, tr, 0, 7); ctx.fill();
|
|
}
|
|
ctx.globalAlpha=alpha;
|
|
}
|
|
ctx.restore();
|
|
}
|
|
|
|
function tick(){
|
|
if(mode==='faces'){ ctx.clearRect(0,0,W,H); }
|
|
else { ctx.globalCompositeOperation='source-over'; ctx.fillStyle=fadeCol; ctx.fillRect(0,0,W,H); }
|
|
for(const p of parts){
|
|
if(p.t==='spark'){
|
|
p.vx*=0.986; p.vy=p.vy*0.986+0.05*DPR; p.x+=p.vx; p.y+=p.vy; p.life-=p.decay;
|
|
if(p.life<=0) continue;
|
|
ctx.globalCompositeOperation='lighter'; ctx.globalAlpha=Math.max(0,p.life); ctx.fillStyle=p.col;
|
|
ctx.beginPath(); ctx.arc(p.x,p.y,p.size*p.life+0.4,0,7); ctx.fill();
|
|
} else if(p.t==='sadface'){
|
|
p.fi+=0.03; p.phase+=0.03; p.tp+=0.010; // persists until replay
|
|
drawSadFace(p);
|
|
} else { // conf
|
|
p.vy+=0.06*DPR; p.x+=p.vx; p.y+=p.vy; p.rot+=p.vr; p.life-=0.006;
|
|
if(p.y>H+20||p.life<=0){p.life=0; continue;}
|
|
ctx.globalCompositeOperation='source-over'; ctx.globalAlpha=Math.max(0,Math.min(1,p.life));
|
|
ctx.save(); ctx.translate(p.x,p.y); ctx.rotate(p.rot); ctx.fillStyle=p.col;
|
|
ctx.fillRect(-p.w/2,-p.h/2,p.w,p.h); ctx.restore();
|
|
}
|
|
}
|
|
parts=parts.filter(p=>p.life>0);
|
|
ctx.globalAlpha=1; ctx.globalCompositeOperation='source-over';
|
|
if(running||parts.length){ rafFW=requestAnimationFrame(tick); }
|
|
else { ctx.clearRect(0,0,W,H); rafFW=null; }
|
|
}
|
|
function stopFw(){ fwTimers.forEach(clearTimeout); fwTimers=[]; running=false; parts=[]; if(W&&H) ctx.clearRect(0,0,W,H); }
|
|
|
|
function celebrate(t){ // t: 0..1 intensity
|
|
stopFw(); parts=[]; mode='fireworks'; fadeCol='rgba(8,18,38,0.22)'; running=true;
|
|
if(!rafFW) rafFW=requestAnimationFrame(tick);
|
|
const rounds=Math.round(5+t*10); // 5..15
|
|
const gap=Math.round(345-t*155); // 345..190 ms
|
|
const base=Math.round(34+t*46); // particles per burst
|
|
for(let k=0;k<rounds;k++){
|
|
const d=k*gap;
|
|
fwTimers.push(setTimeout(()=>burst(0.10+Math.random()*0.07, 0.30+Math.random()*0.24, base), d)); // left
|
|
fwTimers.push(setTimeout(()=>burst(0.83+Math.random()*0.07, 0.30+Math.random()*0.24, base), d)); // right
|
|
if(t>0.4) fwTimers.push(setTimeout(()=>burst(0.44+Math.random()*0.12, 0.20+Math.random()*0.14, base+8), d+90)); // center-top
|
|
if(t>0.75) confetti(6);
|
|
}
|
|
fwTimers.push(setTimeout(()=>{ running=false; }, rounds*gap+400));
|
|
}
|
|
function sadFaces(s){ // s: 0..1 sadness (1 = most sad)
|
|
stopFw(); parts=[]; mode='faces'; running=true;
|
|
if(!rafFW) rafFW=requestAnimationFrame(tick);
|
|
const r=Math.min(W,H)*0.085 + s*Math.min(W,H)*0.02; // slightly bigger when sadder
|
|
const y=H*0.42;
|
|
parts.push({t:'sadface', x:W*0.17, y, r, s, phase:0, tp:Math.random(), fi:0, life:1});
|
|
parts.push({t:'sadface', x:W*0.83, y, r, s, phase:1.6, tp:Math.random()+0.4, fi:0, life:1});
|
|
// keep running so the two faces persist (with idle bob + tears) until replay
|
|
}
|
|
|
|
/* ---------- number ---------- */
|
|
let countRAF=null, countTimer=null, moodTimer=null;
|
|
|
|
const parseTarget=()=>{
|
|
const params = new URLSearchParams(window.location.search);
|
|
const s = params.get('streak');
|
|
const v=parseFloat(s);
|
|
return isFinite(v)?v:0;
|
|
};
|
|
|
|
function countUp(target){
|
|
const dur=1300, start=performance.now(), ease=t=>1-Math.pow(1-t,3);
|
|
const decimals=(String(target).split('.')[1]||'').length;
|
|
(function frame(now){
|
|
const t=Math.min(1,(now-start)/dur), val=target*ease(t);
|
|
numEl.textContent=val.toLocaleString(undefined,{minimumFractionDigits:decimals,maximumFractionDigits:decimals});
|
|
if(t<1) countRAF=requestAnimationFrame(frame);
|
|
})(start);
|
|
}
|
|
|
|
function play(){
|
|
clearTimeout(countTimer); clearTimeout(moodTimer); cancelAnimationFrame(countRAF); stopFw();
|
|
const target=parseTarget();
|
|
const sad = target<=2;
|
|
const t = sad ? 0 : Math.min(1, Math.log10(Math.max(target,3)/3)/3); // 5→0 … 5000+→1
|
|
|
|
// set mood + intensity
|
|
stage.classList.remove('play','happy','sad','tier1','tier2','tier3'); body.classList.remove('sad');
|
|
if(sad){ stage.classList.add('sad'); body.classList.add('sad'); }
|
|
else{
|
|
stage.classList.add('happy');
|
|
stage.classList.add(t<0.34?'tier1':t<0.7?'tier2':'tier3');
|
|
stage.style.setProperty('--grow',(1.12+t*0.16).toFixed(3));
|
|
stage.style.setProperty('--halo-w',(160+t*70).toFixed(0)+'%');
|
|
}
|
|
|
|
capEl.textContent=(capInput.value||'').trim();
|
|
numEl.textContent='0';
|
|
void stage.offsetWidth; stage.classList.add('play');
|
|
|
|
const sadS = Math.max(0, Math.min(1, (3 - target) / 3)); // 1→1 (most), 4→0 (least)
|
|
|
|
if(reduce.matches){ numEl.textContent=target.toLocaleString(); if(sad){ setTimeout(()=>sadFaces(sadS),0);} return; }
|
|
moodTimer=setTimeout(()=> sad ? sadFaces(sadS) : celebrate(t), 2650);
|
|
countTimer=setTimeout(()=>countUp(target), 2750);
|
|
}
|
|
|
|
replay.addEventListener('click', play);
|
|
numInput.addEventListener('keydown', e=>{ if(e.key==='Enter') play(); });
|
|
capInput.addEventListener('keydown', e=>{ if(e.key==='Enter') play(); });
|
|
window.addEventListener('load', ()=>setTimeout(play,180));
|
|
</script>
|