diff --git a/src/components/PerfectCelebration.js b/src/components/PerfectCelebration.js
new file mode 100644
index 0000000..83c0abb
--- /dev/null
+++ b/src/components/PerfectCelebration.js
@@ -0,0 +1,94 @@
+import { getRankInfo } from '../engine/constants.js';
+
+const PRAISE = [
+ 'Every. Single. Word. You absolute wordsmith.',
+ 'Not a fragment left behind. Flawless.',
+ 'You squeezed every last word out of this board.',
+ 'A clean sweep. The dictionary bows to you.',
+ 'Nothing escaped you. That was masterful.',
+ 'Perfection achieved. Take a bow.'
+];
+
+const HEADLINES = ['Perfect!', 'Flawless!', 'Clean Sweep!', 'Masterful!'];
+
+function pick(list) {
+ return list[Math.floor(Math.random() * list.length)];
+}
+
+/**
+ * Celebration card shown when the player finds every word on the board.
+ */
+export function createPerfectCelebration({ onPlayAnother }) {
+ const element = document.createElement('div');
+ element.className = 'perfect-overlay';
+ element.id = 'perfect-overlay';
+ element.hidden = true;
+
+ function close() {
+ element.classList.remove('visible');
+ setTimeout(() => { element.hidden = true; }, 300);
+ }
+
+ function show(state) {
+ const puzzle = state.currentPuzzle;
+ const stats = state.getStats();
+ const wordsFound = state.foundWords.size;
+ const totalWords = puzzle?.wordCount || wordsFound;
+ const rank = getRankInfo(state.score, totalWords, totalWords);
+ const isFirstPerfect = stats.perfectCount <= 1;
+
+ element.innerHTML = `
+
+
+
${rank.current.badge}
+
${pick(HEADLINES)}
+
${pick(PRAISE)}
+
+
+
+
${state.score}
+
Points
+
+
+
${wordsFound}
+
Words
+
+
+
+
+
+ ${isFirstPerfect
+ ? '🎖️ Your first perfect board!'
+ : `🎖️ Perfect board #${stats.perfectCount}. You're on a roll.`}
+
+
+
+
+
+
+
+ `;
+
+ element.hidden = false;
+ requestAnimationFrame(() => element.classList.add('visible'));
+
+ element.querySelector('#btn-perfect-close').addEventListener('click', close);
+ element.querySelector('#btn-perfect-play-another').addEventListener('click', () => {
+ close();
+ onPlayAnother?.();
+ });
+ }
+
+ // Click outside the card or press Escape to close
+ element.addEventListener('click', e => {
+ if (e.target === element) close();
+ });
+ document.addEventListener('keydown', e => {
+ if (e.key === 'Escape' && !element.hidden) close();
+ });
+
+ return { element, show, close, isOpen: () => !element.hidden };
+}
diff --git a/src/engine/audio.js b/src/engine/audio.js
index 2ae4e0d..6209214 100644
--- a/src/engine/audio.js
+++ b/src/engine/audio.js
@@ -260,6 +260,38 @@ class SoundEngine {
osc.stop(now + delay + 0.36);
});
}
+
+ // Triumphant fanfare for a perfect board: rising arpeggio, big chord, sparkles
+ playPerfect() {
+ if (this.muted) return;
+ this.initContext();
+ if (!this.ctx) return;
+
+ const now = this.ctx.currentTime;
+ const tone = (freq, start, dur, type = 'triangle', vol = 0.12) => {
+ const osc = this.ctx.createOscillator();
+ const gain = this.ctx.createGain();
+ osc.type = type;
+ osc.frequency.setValueAtTime(freq, now + start);
+ gain.gain.setValueAtTime(0.0001, now + start);
+ gain.gain.exponentialRampToValueAtTime(vol, now + start + 0.02);
+ gain.gain.exponentialRampToValueAtTime(0.001, now + start + dur);
+ osc.connect(gain);
+ gain.connect(this.ctx.destination);
+ osc.start(now + start);
+ osc.stop(now + start + dur + 0.05);
+ };
+
+ // C major arpeggio climbing two octaves
+ [523.25, 659.25, 783.99, 1046.5, 1318.51, 1567.98].forEach((f, i) => tone(f, i * 0.09, 0.3));
+
+ // Sustained final chord (C - E - G - C)
+ [523.25, 659.25, 783.99, 1046.5].forEach(f => tone(f, 0.6, 1.6, 'triangle', 0.1));
+ tone(261.63, 0.6, 1.6, 'sine', 0.12);
+
+ // High sparkle shimmer
+ [2093, 2637, 3136, 2637, 3520, 4186].forEach((f, i) => tone(f, 0.75 + i * 0.11, 0.25, 'sine', 0.04));
+ }
}
export const sound = new SoundEngine();
diff --git a/src/engine/confetti.js b/src/engine/confetti.js
index e605182..18fa7fe 100644
--- a/src/engine/confetti.js
+++ b/src/engine/confetti.js
@@ -1,107 +1,189 @@
/**
- * Lightweight canvas confetti system with multi-colored ribbons, stars and sparkles
+ * Lightweight canvas confetti system with multi-colored ribbons, stars and sparkles.
+ * All bursts share one canvas and one animation loop, so overlapping bursts
+ * (e.g. a multi-cannon celebration) don't clobber each other.
+ */
+const DEFAULT_COLORS = ['#f59e0b', '#10b981', '#6366f1', '#ec4899', '#3b82f6', '#fbbf24'];
+
+let canvas = null;
+let ctx = null;
+let particles = [];
+let animationFrame = null;
+
+function ensureCanvas() {
+ if (!canvas || !canvas.isConnected) {
+ canvas = document.getElementById('quartiles-confetti-canvas');
+ if (!canvas) {
+ canvas = document.createElement('canvas');
+ canvas.id = 'quartiles-confetti-canvas';
+ canvas.style.position = 'fixed';
+ canvas.style.top = '0';
+ canvas.style.left = '0';
+ canvas.style.width = '100vw';
+ canvas.style.height = '100vh';
+ canvas.style.pointerEvents = 'none';
+ canvas.style.zIndex = '9999';
+ document.body.appendChild(canvas);
+ }
+ ctx = canvas.getContext('2d');
+ }
+ // Resize (also resets the transform) only when the viewport changed
+ const dpr = window.devicePixelRatio || 1;
+ const w = Math.round(window.innerWidth * dpr);
+ const h = Math.round(window.innerHeight * dpr);
+ if (canvas.width !== w || canvas.height !== h) {
+ canvas.width = w;
+ canvas.height = h;
+ }
+ ctx.setTransform(dpr, 0, 0, dpr, 0, 0);
+}
+
+function render() {
+ ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
+
+ particles = particles.filter(p => p.opacity > 0 && p.y < window.innerHeight + 50);
+
+ for (const p of particles) {
+ p.vx *= p.drag;
+ p.vy = p.vy * p.drag + p.gravity;
+ p.x += p.vx + Math.sin(p.wobble) * p.wobbleAmp;
+ p.y += p.vy;
+ p.wobble += p.wobbleSpeed;
+ p.rotation += p.vRotation;
+ p.scaleX += p.vScale;
+ if (Math.abs(p.scaleX) > 1) p.vScale *= -1;
+ p.opacity -= p.decay;
+
+ ctx.save();
+ ctx.translate(p.x, p.y);
+ ctx.rotate((p.rotation * Math.PI) / 180);
+ ctx.scale(p.scaleX, 1);
+ ctx.globalAlpha = Math.max(0, p.opacity);
+ ctx.fillStyle = p.color;
+
+ if (p.shape === 'rect') {
+ ctx.fillRect(-p.size / 2, -p.size / 3, p.size, p.size / 1.5);
+ } else if (p.shape === 'star') {
+ drawStar(p.size / 2);
+ } else {
+ ctx.beginPath();
+ ctx.arc(0, 0, p.size / 2.5, 0, Math.PI * 2);
+ ctx.fill();
+ }
+ ctx.restore();
+ }
+
+ if (particles.length > 0) {
+ animationFrame = requestAnimationFrame(render);
+ } else {
+ ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
+ animationFrame = null;
+ }
+}
+
+function drawStar(r) {
+ ctx.beginPath();
+ for (let i = 0; i < 10; i++) {
+ const radius = i % 2 === 0 ? r : r * 0.45;
+ const a = (Math.PI / 5) * i - Math.PI / 2;
+ ctx.lineTo(Math.cos(a) * radius, Math.sin(a) * radius);
+ }
+ ctx.closePath();
+ ctx.fill();
+}
+
+/**
+ * Fires one confetti burst.
+ * @param {object} opts
+ * @param {number} opts.particleCount
+ * @param {number} opts.spread Half-angle of the cone in degrees
+ * @param {number} opts.angle Launch direction in degrees (-90 = straight up)
+ * @param {{x:number,y:number}} opts.origin Fractions of the viewport
+ * @param {number} opts.velocity Base launch speed
+ * @param {number} opts.starRatio Share of particles drawn as stars (0-1)
+ * @param {number} opts.decay Base fade rate per frame (lower = lasts longer)
+ * @param {string[]} opts.colors
*/
export function fireConfetti({
particleCount = 75,
spread = 70,
+ angle = -90,
origin = { x: 0.5, y: 0.6 },
- colors = ['#f59e0b', '#10b981', '#6366f1', '#ec4899', '#3b82f6', '#fbbf24']
+ velocity = 7,
+ starRatio = 0,
+ decay = 0.012,
+ colors = DEFAULT_COLORS
} = {}) {
- let canvas = document.getElementById('quartiles-confetti-canvas');
- if (!canvas) {
- canvas = document.createElement('canvas');
- canvas.id = 'quartiles-confetti-canvas';
- canvas.style.position = 'fixed';
- canvas.style.top = '0';
- canvas.style.left = '0';
- canvas.style.width = '100vw';
- canvas.style.height = '100vh';
- canvas.style.pointerEvents = 'none';
- canvas.style.zIndex = '9999';
- document.body.appendChild(canvas);
- }
+ ensureCanvas();
- const ctx = canvas.getContext('2d');
- const dpr = window.devicePixelRatio || 1;
- canvas.width = window.innerWidth * dpr;
- canvas.height = window.innerHeight * dpr;
- ctx.scale(dpr, dpr);
-
- const particles = [];
const startX = origin.x * window.innerWidth;
const startY = origin.y * window.innerHeight;
for (let i = 0; i < particleCount; i++) {
- const angle = (Math.PI / 180) * (-90 + (Math.random() - 0.5) * spread * 2);
- const speed = 7 + Math.random() * 9;
- const color = colors[Math.floor(Math.random() * colors.length)];
- const size = Math.random() * 8 + 6;
- const shape = Math.random() > 0.4 ? 'rect' : 'circle';
+ const a = (Math.PI / 180) * (angle + (Math.random() - 0.5) * spread * 2);
+ const speed = velocity + Math.random() * 9;
+ const roll = Math.random();
+ const shape = roll < starRatio ? 'star' : roll < starRatio + (1 - starRatio) * 0.6 ? 'rect' : 'circle';
particles.push({
x: startX,
y: startY,
- vx: Math.cos(angle) * speed,
- vy: Math.sin(angle) * speed,
+ vx: Math.cos(a) * speed,
+ vy: Math.sin(a) * speed,
rotation: Math.random() * 360,
vRotation: (Math.random() - 0.5) * 12,
scaleX: 1,
vScale: (Math.random() - 0.5) * 0.1,
- color,
- size,
+ wobble: Math.random() * Math.PI * 2,
+ wobbleSpeed: 0.05 + Math.random() * 0.08,
+ wobbleAmp: Math.random() * 0.6,
+ color: colors[Math.floor(Math.random() * colors.length)],
+ size: Math.random() * 8 + 6,
shape,
gravity: 0.28,
drag: 0.96,
opacity: 1,
- decay: 0.012 + Math.random() * 0.008
+ decay: decay + Math.random() * 0.008
});
}
- let animationFrame;
-
- function render() {
- ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
-
- let activeCount = 0;
-
- for (let i = 0; i < particles.length; i++) {
- const p = particles[i];
- if (p.opacity <= 0) continue;
-
- activeCount++;
- p.vx *= p.drag;
- p.vy = p.vy * p.drag + p.gravity;
- p.x += p.vx;
- p.y += p.vy;
- p.rotation += p.vRotation;
- p.scaleX += p.vScale;
- if (Math.abs(p.scaleX) > 1) p.vScale *= -1;
- p.opacity -= p.decay;
-
- ctx.save();
- ctx.translate(p.x, p.y);
- ctx.rotate((p.rotation * Math.PI) / 180);
- ctx.scale(p.scaleX, 1);
- ctx.globalAlpha = Math.max(0, p.opacity);
- ctx.fillStyle = p.color;
-
- if (p.shape === 'rect') {
- ctx.fillRect(-p.size / 2, -p.size / 3, p.size, p.size / 1.5);
- } else {
- ctx.beginPath();
- ctx.arc(0, 0, p.size / 2.5, 0, Math.PI * 2);
- ctx.fill();
- }
- ctx.restore();
- }
-
- if (activeCount > 0) {
- animationFrame = requestAnimationFrame(render);
- } else {
- ctx.clearRect(0, 0, window.innerWidth, window.innerHeight);
- cancelAnimationFrame(animationFrame);
- }
+ if (!animationFrame) {
+ animationFrame = requestAnimationFrame(render);
+ }
+}
+
+/**
+ * Multi-stage celebration for a perfect board: a center blast, side cannons,
+ * then a slow golden rain of stars from the top of the screen.
+ */
+export function fireConfettiShow() {
+ const gold = ['#fbbf24', '#f59e0b', '#fde68a', '#ffffff', '#facc15'];
+
+ fireConfetti({ particleCount: 180, spread: 110, origin: { x: 0.5, y: 0.65 }, velocity: 9, starRatio: 0.2 });
+
+ setTimeout(() => {
+ fireConfetti({ particleCount: 110, spread: 30, angle: -60, origin: { x: 0, y: 0.8 }, velocity: 14, starRatio: 0.15 });
+ fireConfetti({ particleCount: 110, spread: 30, angle: -120, origin: { x: 1, y: 0.8 }, velocity: 14, starRatio: 0.15 });
+ }, 350);
+
+ setTimeout(() => {
+ fireConfetti({ particleCount: 90, spread: 25, angle: -70, origin: { x: 0.1, y: 0.95 }, velocity: 15, starRatio: 0.3 });
+ fireConfetti({ particleCount: 90, spread: 25, angle: -110, origin: { x: 0.9, y: 0.95 }, velocity: 15, starRatio: 0.3 });
+ }, 900);
+
+ // Golden star rain drifting down across the whole width
+ for (let i = 0; i < 6; i++) {
+ setTimeout(() => {
+ fireConfetti({
+ particleCount: 30,
+ spread: 25,
+ angle: 90,
+ origin: { x: 0.1 + Math.random() * 0.8, y: -0.05 },
+ velocity: 1,
+ starRatio: 0.6,
+ decay: 0.006,
+ colors: gold
+ });
+ }, 1300 + i * 250);
}
-
- render();
}
diff --git a/src/main.js b/src/main.js
index 57341ac..0802200 100644
--- a/src/main.js
+++ b/src/main.js
@@ -5,7 +5,7 @@ import './styles/components.css';
import { gameState } from './state/game-state.js';
import { getDailyPuzzle, resolveBuiltInPuzzle, toDateString } from './engine/daily.js';
import { sound } from './engine/audio.js';
-import { fireConfetti } from './engine/confetti.js';
+import { fireConfetti, fireConfettiShow } from './engine/confetti.js';
import { createHeader } from './components/Header.js';
import { createScoreRankBar } from './components/ScoreRankBar.js';
@@ -16,6 +16,7 @@ import { createWordsDrawer } from './components/WordsDrawer.js';
import { createModals } from './components/Modals.js';
import { createToastContainer } from './components/Toast.js';
import { createUpdateBanner } from './components/UpdateBanner.js';
+import { createPerfectCelebration } from './components/PerfectCelebration.js';
import { APP_VERSION, startVersionCheck } from './engine/version.js';
// PWA Service Worker Registration (production only: in dev it would cache
@@ -102,18 +103,7 @@ async function initApp() {
onOpenStats: () => modals.open('stats', gameState),
onOpenCustomBuilder: () => modals.open('builder', gameState),
onOpenSettings: () => modals.open('settings', gameState),
- onNewRandomPuzzle: async () => {
- try {
- const { generateRandomPuzzle } = await import('./engine/generator.js');
- const randomPuzzle = generateRandomPuzzle();
- gameState.loadPuzzle(randomPuzzle);
- gameState.notifyToast('Loaded fresh unlimited puzzle!', 'success');
- fireConfetti({ particleCount: 30, spread: 50 });
- } catch (err) {
- console.error('Failed to generate puzzle:', err);
- gameState.notifyToast('Failed to generate puzzle', 'error');
- }
- }
+ onNewRandomPuzzle: () => loadRandomPuzzle()
});
appContainer.appendChild(header.element);
@@ -197,6 +187,31 @@ async function initApp() {
document.body.appendChild(updateBanner.element);
startVersionCheck(latest => updateBanner.show(latest));
+ // Loads a fresh unlimited puzzle (header dice button + celebration card)
+ async function loadRandomPuzzle() {
+ try {
+ const { generateRandomPuzzle } = await import('./engine/generator.js');
+ const randomPuzzle = generateRandomPuzzle();
+ gameState.loadPuzzle(randomPuzzle);
+ gameState.notifyToast('Loaded fresh unlimited puzzle!', 'success');
+ fireConfetti({ particleCount: 30, spread: 50 });
+ } catch (err) {
+ console.error('Failed to generate puzzle:', err);
+ gameState.notifyToast('Failed to generate puzzle', 'error');
+ }
+ }
+
+ // Perfect board celebration (every word on the list found)
+ const perfectCelebration = createPerfectCelebration({ onPlayAnother: loadRandomPuzzle });
+ document.body.appendChild(perfectCelebration.element);
+
+ function celebratePerfect() {
+ sound.playPerfect();
+ fireConfettiShow();
+ gameState.notifyToast('👑 PERFECT! Every word found!', 'quartile', 4000);
+ setTimeout(() => perfectCelebration.show(gameState), 900);
+ }
+
// Word submission handler
function handleWordSubmission() {
const prevRank = gameState.getRank();
@@ -233,7 +248,10 @@ async function initApp() {
// Check rank progression
const currentRank = gameState.getRank();
- if (!prevRank.isExpert && currentRank.isExpert) {
+ if (!prevRank.isPerfect && currentRank.isPerfect) {
+ // Perfect outranks Expert; let the 5/5 bonus fanfare finish first if it just played
+ setTimeout(celebratePerfect, result.bonusUnlocked ? 1600 : 500);
+ } else if (!prevRank.isExpert && currentRank.isExpert) {
setTimeout(() => {
sound.playRankUp();
gameState.notifyToast('🏆 EXPERT RANK ACHIEVED! Outstanding!', 'quartile', 4000);
@@ -250,6 +268,8 @@ async function initApp() {
window.addEventListener('keydown', e => {
// If inside an input field, do not hijack
if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA') return;
+ // Celebration card handles its own keys (Escape closes it)
+ if (perfectCelebration.isOpen()) return;
if (e.key === 'Enter') {
e.preventDefault();
diff --git a/src/styles/components.css b/src/styles/components.css
index 0afd402..766ce4c 100644
--- a/src/styles/components.css
+++ b/src/styles/components.css
@@ -928,3 +928,175 @@
font-variant-numeric: tabular-nums;
letter-spacing: 0.02em;
}
+
+/* ==========================================================================
+ PERFECT BOARD CELEBRATION
+ ========================================================================== */
+
+.perfect-overlay {
+ position: fixed;
+ inset: 0;
+ z-index: 1200; /* below the confetti canvas (9999) so confetti falls over the card */
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 20px;
+ background: rgba(0, 0, 0, 0.45);
+ backdrop-filter: blur(6px);
+ -webkit-backdrop-filter: blur(6px);
+ opacity: 0;
+ transition: opacity 0.3s ease;
+}
+
+.perfect-overlay.visible {
+ opacity: 1;
+}
+
+.perfect-card {
+ position: relative;
+ overflow: hidden;
+ width: min(380px, 100%);
+ padding: 30px 24px 22px;
+ text-align: center;
+ background: var(--bg-secondary);
+ border: 1.5px solid rgba(245, 158, 11, 0.45);
+ border-radius: 24px;
+ box-shadow: 0 24px 60px rgba(0, 0, 0, 0.35), 0 0 0 6px rgba(245, 158, 11, 0.08);
+ transform: scale(0.85) translateY(20px);
+ transition: transform 0.45s var(--ease-spring);
+}
+
+.perfect-overlay.visible .perfect-card {
+ transform: scale(1) translateY(0);
+}
+
+.perfect-glow {
+ position: absolute;
+ inset: -40% -20% auto;
+ height: 70%;
+ background: radial-gradient(circle, rgba(251, 191, 36, 0.35), transparent 65%);
+ pointer-events: none;
+ animation: perfectGlow 3s ease-in-out infinite;
+}
+
+@keyframes perfectGlow {
+ 0%, 100% { opacity: 0.6; transform: scale(1); }
+ 50% { opacity: 1; transform: scale(1.08); }
+}
+
+.perfect-crown {
+ position: relative;
+ font-size: 3.6rem;
+ line-height: 1;
+ margin-bottom: 6px;
+ filter: drop-shadow(0 6px 14px rgba(245, 158, 11, 0.45));
+ animation: crownDrop 0.8s var(--ease-spring) 0.15s both, crownFloat 2.6s ease-in-out 1s infinite;
+}
+
+@keyframes crownDrop {
+ 0% { transform: translateY(-60px) rotate(-20deg) scale(0.4); opacity: 0; }
+ 100% { transform: translateY(0) rotate(0) scale(1); opacity: 1; }
+}
+
+@keyframes crownFloat {
+ 0%, 100% { transform: translateY(0) rotate(0); }
+ 50% { transform: translateY(-6px) rotate(4deg); }
+}
+
+.perfect-title {
+ position: relative;
+ margin: 0 0 6px;
+ font-size: 2.2rem;
+ font-weight: 900;
+ letter-spacing: -0.02em;
+ background: var(--accent-quartile-gradient);
+ -webkit-background-clip: text;
+ background-clip: text;
+ color: transparent;
+}
+
+.perfect-praise {
+ position: relative;
+ margin: 0 auto 18px;
+ max-width: 290px;
+ font-size: 0.95rem;
+ color: var(--text-secondary);
+}
+
+.perfect-stats {
+ position: relative;
+ display: grid;
+ grid-template-columns: repeat(3, 1fr);
+ gap: 8px;
+ margin-bottom: 14px;
+}
+
+.perfect-stat {
+ padding: 10px 4px;
+ background: var(--bg-primary);
+ border-radius: var(--radius-md);
+ animation: statPop 0.4s var(--ease-spring) both;
+}
+
+.perfect-stat:nth-child(1) { animation-delay: 0.35s; }
+.perfect-stat:nth-child(2) { animation-delay: 0.45s; }
+.perfect-stat:nth-child(3) { animation-delay: 0.55s; }
+
+@keyframes statPop {
+ 0% { transform: scale(0.6); opacity: 0; }
+ 100% { transform: scale(1); opacity: 1; }
+}
+
+.perfect-stat-value {
+ font-size: 1.5rem;
+ font-weight: 800;
+ color: var(--text-primary);
+ font-variant-numeric: tabular-nums;
+}
+
+.perfect-stat-label {
+ font-size: 0.7rem;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.06em;
+ color: var(--text-secondary);
+}
+
+.perfect-milestone {
+ position: relative;
+ margin: 0 0 18px;
+ font-size: 0.85rem;
+ font-weight: 600;
+ color: var(--accent-quartile);
+}
+
+.perfect-actions {
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+}
+
+.perfect-btn-primary {
+ height: 46px;
+ border-radius: var(--radius-md);
+ background: var(--accent-quartile-gradient);
+ color: #ffffff;
+ font-weight: 800;
+ font-size: 1rem;
+ box-shadow: 0 6px 18px rgba(245, 158, 11, 0.35);
+ transition: transform var(--transition-fast);
+}
+
+.perfect-btn-primary:hover {
+ transform: translateY(-1px);
+}
+
+.perfect-btn-secondary {
+ height: 40px;
+ border-radius: var(--radius-md);
+ background: var(--bg-tertiary);
+ color: var(--text-primary);
+ font-weight: 700;
+ font-size: 0.9rem;
+}
diff --git a/src/version.json b/src/version.json
index ca84e70..9a8bfb2 100644
--- a/src/version.json
+++ b/src/version.json
@@ -1,3 +1,3 @@
{
- "version": "1.0.1"
+ "version": "1.0.2"
}