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;