Add bingo celebration and stack weeks
AI Just committed 2026-08-16 15:20:49 +0000

Analyzing diff...

index.html+67 −1
index 45ed840..b8239ff 100644
--- a/index.html
+++ b/index.html
@@ -199,7 +199,7 @@
       }
       .weeks-grid {
         display: grid;
-        grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
+        grid-template-columns: minmax(0, 1fr);
         gap: 30px;
         align-items: start;
       }
@@ -444,8 +444,10 @@
         grid-template-columns: repeat(7, 1fr);
         gap: 4px;
         margin-top: 8px;
+        min-width: 0;
       }
       .dot {
+        min-width: 0;
         border: 0;
         border-radius: 8px;
         aspect-ratio: 1;
@@ -455,6 +457,14 @@
         font-weight: 800;
         cursor: pointer;
       }
+      .celebration-canvas {
+        position: fixed;
+        inset: 0;
+        width: 100%;
+        height: 100%;
+        pointer-events: none;
+        z-index: 9999;
+      }
       .dot.active {
         background: var(--yellow);
         color: var(--navy);
@@ -923,6 +933,7 @@
       ];
       const DAYS = ["Пн", "Вт", "Ср", "Чт", "Пт", "Сб", "Вс"];
       const KEY = "sekta-plan-alyona-w8-w9-v1";
+      const gameLineCounts = new WeakMap();
       let state = JSON.parse(
         localStorage.getItem(KEY) || '{"done":{},"comments":{},"practice":{}}',
       );
@@ -1011,6 +1022,7 @@
         );
         const winningCards = new Set(winningLines.flat());
         const lines = winningLines.length;
+        const previousLines = gameLineCounts.get(game);
         const count = Object.keys(state.practice).filter(
           (k) => k.startsWith(`w${week}-`) && state.practice[k],
         ).length;
@@ -1033,6 +1045,60 @@
           : "0 линий";
         game.querySelector("[data-score]").textContent =
           `${count} применений за неделю`;
+        if (previousLines !== undefined && lines > previousLines) {
+          launchCelebration();
+        }
+        gameLineCounts.set(game, lines);
+      }
+      function launchCelebration() {
+        if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
+        const canvas = document.createElement("canvas");
+        canvas.className = "celebration-canvas";
+        canvas.setAttribute("aria-hidden", "true");
+        document.body.append(canvas);
+        const ctx = canvas.getContext("2d");
+        const scale = Math.min(window.devicePixelRatio || 1, 2);
+        canvas.width = Math.round(innerWidth * scale);
+        canvas.height = Math.round(innerHeight * scale);
+        ctx.scale(scale, scale);
+        const colors = ["#ffd95a", "#ff8f70", "#4ba8e8", "#42b883", "#ffffff"];
+        const particles = Array.from({ length: 110 }, (_, i) => {
+          const side = i % 2;
+          const angle = (side ? -Math.PI * 0.82 : -Math.PI * 0.18) + (Math.random() - 0.5) * 1.2;
+          const speed = 5 + Math.random() * 8;
+          return {
+            x: side ? innerWidth * 0.82 : innerWidth * 0.18,
+            y: innerHeight * 0.62,
+            vx: Math.cos(angle) * speed,
+            vy: Math.sin(angle) * speed,
+            size: 3 + Math.random() * 5,
+            color: colors[i % colors.length],
+            rotation: Math.random() * Math.PI,
+            spin: (Math.random() - 0.5) * 0.35,
+          };
+        });
+        const started = performance.now();
+        function frame(now) {
+          const elapsed = now - started;
+          ctx.clearRect(0, 0, innerWidth, innerHeight);
+          particles.forEach((p) => {
+            p.x += p.vx;
+            p.y += p.vy;
+            p.vy += 0.16;
+            p.vx *= 0.992;
+            p.rotation += p.spin;
+            ctx.save();
+            ctx.globalAlpha = Math.max(0, 1 - elapsed / 1500);
+            ctx.translate(p.x, p.y);
+            ctx.rotate(p.rotation);
+            ctx.fillStyle = p.color;
+            ctx.fillRect(-p.size / 2, -p.size / 2, p.size, p.size * 0.58);
+            ctx.restore();
+          });
+          if (elapsed < 1500) requestAnimationFrame(frame);
+          else canvas.remove();
+        }
+        requestAnimationFrame(frame);
       }
       function updateProgress() {
         const checks = [...document.querySelectorAll(".check")],