diff --git a/public/app.js b/public/app.js index 6fc3c98..8281dc0 100644 --- a/public/app.js +++ b/public/app.js @@ -403,9 +403,9 @@ class ParticleField { constructor(canvas) { this.canvas = canvas; this.ctx = canvas.getContext('2d'); - this.COLS = 100; + this.COLS = 0; this.ROWS = 4; - this.N = this.COLS * this.ROWS; + this.N = 0; this.targetProgress = 0; this.currentProgress = 0; this.cardState = 'active'; @@ -420,6 +420,16 @@ class ParticleField { this.canvas.height = rect.height * devicePixelRatio; this.canvas.style.width = rect.width + 'px'; this.canvas.style.height = rect.height + 'px'; + + const dpr = devicePixelRatio; + const radius = 4 * dpr; + const gap = 2 * dpr; + const step = radius * 2 + gap; + + // Calculate how many columns fit with 32px padding on each side + const padding = 32 * dpr; + this.COLS = Math.max(1, Math.floor((this.canvas.width - padding * 2) / step)); + this.N = this.COLS * this.ROWS; } update(progress, cardState) { @@ -429,6 +439,7 @@ class ParticleField { draw(_ts) { const { ctx, canvas, COLS, ROWS, N } = this; + if (N === 0) return; const w = canvas.width; const h = canvas.height; ctx.clearRect(0, 0, w, h); @@ -639,11 +650,6 @@ function rebuildCards() { const id = card.id.replace('card-', ''); const canvas = card.querySelector('canvas'); if (!canvas) return; - const rect = card.getBoundingClientRect(); - canvas.width = rect.width * devicePixelRatio; - canvas.height = rect.height * devicePixelRatio; - canvas.style.width = rect.width + 'px'; - canvas.style.height = rect.height + 'px'; particleFields[id] = new ParticleField(canvas); }); diff --git a/public/index.html b/public/index.html index 3c4d93e..30d3926 100644 --- a/public/index.html +++ b/public/index.html @@ -16,12 +16,12 @@ diff --git a/public/style.css b/public/style.css index cbc95d8..b22381e 100644 --- a/public/style.css +++ b/public/style.css @@ -20,7 +20,6 @@ html, body { font-family: 'Inter', system-ui, sans-serif; font-weight: 300; color: var(--text); - overflow: hidden; } /* ── Header ─────────────────────────────────────── */ @@ -31,6 +30,16 @@ header { justify-content: space-between; padding: 0 32px; border-bottom: 1px solid var(--border); + position: sticky; + top: 0; + background: var(--bg); + z-index: 10; +} + +@media (max-width: 600px) { + header { + padding: 0 16px; + } } #week-number { @@ -45,6 +54,12 @@ header { align-items: center; } +@media (max-width: 600px) { + .toggles { + gap: 12px; + } +} + .toggle { display: flex; align-items: center; @@ -92,6 +107,18 @@ header { font-size: 12px; letter-spacing: 0.04em; color: var(--text-secondary); + display: flex; + align-items: center; + gap: 4px; +} + +#workModeLabel .toggle-label::before { content: '⚒'; filter: grayscale(100%); opacity: 0.7; } +#bridgeDaysLabel .toggle-label::before { content: '🏖'; filter: grayscale(100%); opacity: 0.7; } + +@media (max-width: 600px) { + .toggle-label span { + display: none; + } } #bridgeDaysLabel { @@ -107,7 +134,7 @@ header { /* ── Cards ───────────────────────────────────────── */ #cards { - height: calc(100vh - var(--header-height)); + min-height: calc(100vh - var(--header-height)); display: flex; flex-direction: column; } @@ -115,6 +142,7 @@ header { .card { position: relative; flex: 1; + min-height: 80px; border-bottom: 1px solid rgba(0, 0, 0, 0.12); overflow: hidden; } @@ -140,6 +168,12 @@ header { pointer-events: none; } +@media (max-width: 600px) { + .card-content { + padding: 8px 12px 0; + } +} + .card-name { font-size: 11px; font-weight: 400; @@ -149,6 +183,12 @@ header { transition: color 0.4s ease; } +@media (max-width: 600px) { + .card-name, .card-ends, .card-remaining { + font-size: 10px; + } +} + .card-ends { font-size: 11px; font-weight: 300; @@ -279,6 +319,19 @@ header { gap: 10px; } +@media (max-width: 600px) { + .add-timer-form { + flex-direction: column; + padding: 20px 0; + gap: 12px; + } + + .add-timer-form input[type="text"], + .add-timer-form input[type="date"] { + width: 200px !important; + } +} + .add-timer-form input[type="text"], .add-timer-form input[type="date"] { background: none;