zero-query 0.2.9 → 0.4.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,96 @@
1
+ // scripts/store.js — global state management
2
+ //
3
+ // $.store() creates a centralized store with state, actions, and getters.
4
+ // Components can dispatch actions and subscribe to changes.
5
+ // The store is accessible anywhere via $.getStore('main').
6
+
7
+ export const store = $.store('main', {
8
+ state: {
9
+ todos: [],
10
+ visits: 0,
11
+
12
+ // Contacts
13
+ contacts: [
14
+ { id: 1, name: 'Alice Johnson', email: 'alice@example.com', role: 'Designer', status: 'online', favorite: true },
15
+ { id: 2, name: 'Bob Martinez', email: 'bob@example.com', role: 'Developer', status: 'offline', favorite: false },
16
+ { id: 3, name: 'Carol White', email: 'carol@example.com', role: 'Manager', status: 'online', favorite: true },
17
+ { id: 4, name: 'Dave Kim', email: 'dave@example.com', role: 'Designer', status: 'away', favorite: false },
18
+ { id: 5, name: 'Eve Torres', email: 'eve@example.com', role: 'Developer', status: 'online', favorite: false },
19
+ ],
20
+ contactsAdded: 0,
21
+ },
22
+
23
+ actions: {
24
+ // Increment the global visit counter
25
+ incrementVisits(state) {
26
+ state.visits++;
27
+ },
28
+
29
+ // Add a new todo item using $.uuid() for unique IDs
30
+ addTodo(state, text) {
31
+ state.todos.push({
32
+ id: $.uuid(),
33
+ text: text.trim(),
34
+ done: false,
35
+ createdAt: Date.now(),
36
+ });
37
+ },
38
+
39
+ // Toggle a todo's completion status
40
+ toggleTodo(state, id) {
41
+ const todo = state.todos.find(t => t.id === id);
42
+ if (todo) todo.done = !todo.done;
43
+ },
44
+
45
+ // Remove a todo by ID
46
+ removeTodo(state, id) {
47
+ state.todos = state.todos.filter(t => t.id !== id);
48
+ },
49
+
50
+ // Clear all completed todos
51
+ clearCompleted(state) {
52
+ state.todos = state.todos.filter(t => !t.done);
53
+ },
54
+
55
+ // -- Contact actions --
56
+
57
+ addContact(state, { name, email, role }) {
58
+ state.contacts.push({
59
+ id: Date.now(),
60
+ name,
61
+ email,
62
+ role,
63
+ status: 'offline',
64
+ favorite: false,
65
+ });
66
+ state.contactsAdded++;
67
+ },
68
+
69
+ deleteContact(state, id) {
70
+ state.contacts = state.contacts.filter(c => c.id !== id);
71
+ },
72
+
73
+ toggleFavorite(state, id) {
74
+ const c = state.contacts.find(c => c.id === id);
75
+ if (c) c.favorite = !c.favorite;
76
+ },
77
+
78
+ cycleContactStatus(state, id) {
79
+ const c = state.contacts.find(c => c.id === id);
80
+ if (!c) return;
81
+ const order = ['online', 'away', 'offline'];
82
+ c.status = order[(order.indexOf(c.status) + 1) % 3];
83
+ },
84
+ },
85
+
86
+ getters: {
87
+ todoCount: (state) => state.todos.length,
88
+ doneCount: (state) => state.todos.filter(t => t.done).length,
89
+ pendingCount: (state) => state.todos.filter(t => !t.done).length,
90
+
91
+ contactCount: (state) => state.contacts.length,
92
+ favoriteCount: (state) => state.contacts.filter(c => c.favorite).length,
93
+ },
94
+
95
+ debug: true, // logs dispatches to console in development
96
+ });
@@ -0,0 +1,556 @@
1
+ /* styles/styles.css — responsive scaffold styles
2
+ *
3
+ * Uses CSS custom properties for easy theming.
4
+ * Dark theme by default, light theme via [data-theme="light"].
5
+ * Mobile-first: sidebar collapses to a hamburger nav below 768px.
6
+ */
7
+
8
+ /* -- Theme Variables -- */
9
+ :root {
10
+ --bg: #13141f;
11
+ --bg-surface: #1b1d2e;
12
+ --bg-card: #222438;
13
+ --bg-hover: #2a2d45;
14
+ --border: #2e3150;
15
+ --text: #e2e4f0;
16
+ --text-muted: #8b8ea8;
17
+ --accent: #7c5cfc;
18
+ --accent-hover:#9b7eff;
19
+ --accent-soft: rgba(124, 92, 252, 0.12);
20
+ --success: #34d399;
21
+ --danger: #f87171;
22
+ --info: #60a5fa;
23
+ --radius: 8px;
24
+ --radius-lg: 12px;
25
+ --sidebar-w: 240px;
26
+ --topbar-h: 56px;
27
+ --font: 'Inter', system-ui, -apple-system, 'Segoe UI', Roboto, sans-serif;
28
+ }
29
+
30
+ [data-theme="light"] {
31
+ --bg: #f5f5f9;
32
+ --bg-surface: #ffffff;
33
+ --bg-card: #ffffff;
34
+ --bg-hover: #f0f0f5;
35
+ --border: #e0e0e8;
36
+ --text: #1e1e2e;
37
+ --text-muted: #6b6e8a;
38
+ --accent: #6244e0;
39
+ --accent-hover:#7c5cfc;
40
+ --accent-soft: rgba(98, 68, 224, 0.08);
41
+ --success: #16a34a;
42
+ --danger: #dc2626;
43
+ --info: #2563eb;
44
+ }
45
+
46
+ /* -- Reset -- */
47
+ *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; }
48
+
49
+ html { scroll-behavior: smooth; }
50
+
51
+ body {
52
+ font-family: var(--font);
53
+ font-size: 15px;
54
+ line-height: 1.6;
55
+ color: var(--text);
56
+ background: var(--bg);
57
+ overflow-x: hidden;
58
+ }
59
+
60
+ a { color: var(--accent); text-decoration: none; }
61
+ a:hover { color: var(--accent-hover); }
62
+ code { background: var(--bg-hover); padding: 2px 6px; border-radius: 4px; font-size: 0.85em; }
63
+
64
+ /* -- Sidebar -- */
65
+ .sidebar {
66
+ position: fixed;
67
+ top: 0; left: 0; bottom: 0;
68
+ width: var(--sidebar-w);
69
+ background: var(--bg-surface);
70
+ border-right: 1px solid var(--border);
71
+ display: flex;
72
+ flex-direction: column;
73
+ z-index: 100;
74
+ transition: transform 0.3s ease;
75
+ }
76
+
77
+ .sidebar-header {
78
+ padding: 1.25rem 1.25rem 1rem;
79
+ border-bottom: 1px solid var(--border);
80
+ }
81
+
82
+ .brand {
83
+ font-size: 1.15rem;
84
+ font-weight: 700;
85
+ color: var(--text);
86
+ letter-spacing: -0.02em;
87
+ }
88
+
89
+ .sidebar-nav {
90
+ flex: 1;
91
+ padding: 0.75rem 0;
92
+ overflow-y: auto;
93
+ }
94
+
95
+ .nav-link {
96
+ display: flex;
97
+ align-items: center;
98
+ gap: 0.6rem;
99
+ padding: 0.6rem 1.25rem;
100
+ color: var(--text-muted);
101
+ font-size: 0.9rem;
102
+ font-weight: 500;
103
+ transition: all 0.15s ease;
104
+ border-left: 3px solid transparent;
105
+ cursor: pointer;
106
+ }
107
+
108
+ .nav-link:hover {
109
+ color: var(--text);
110
+ background: var(--bg-hover);
111
+ }
112
+
113
+ .nav-link.active {
114
+ color: var(--accent);
115
+ background: var(--accent-soft);
116
+ border-left-color: var(--accent);
117
+ }
118
+
119
+ .nav-icon { font-size: 1.05rem; width: 1.4rem; text-align: center; }
120
+
121
+ .sidebar-footer {
122
+ padding: 0.75rem 1.25rem;
123
+ border-top: 1px solid var(--border);
124
+ color: var(--text-muted);
125
+ font-size: 0.8rem;
126
+ }
127
+
128
+ /* -- Mobile Top Bar -- */
129
+ .topbar {
130
+ display: none;
131
+ position: fixed;
132
+ top: 0; left: 0; right: 0;
133
+ height: var(--topbar-h);
134
+ background: var(--bg-surface);
135
+ border-bottom: 1px solid var(--border);
136
+ align-items: center;
137
+ padding: 0 1rem;
138
+ z-index: 101;
139
+ }
140
+
141
+ .topbar-brand {
142
+ font-weight: 700;
143
+ font-size: 1.05rem;
144
+ margin-left: 0.75rem;
145
+ }
146
+
147
+ /* Hamburger */
148
+ .hamburger {
149
+ display: flex;
150
+ flex-direction: column;
151
+ justify-content: center;
152
+ gap: 5px;
153
+ width: 32px;
154
+ height: 32px;
155
+ background: none;
156
+ border: none;
157
+ cursor: pointer;
158
+ padding: 4px;
159
+ }
160
+
161
+ .hamburger span {
162
+ display: block;
163
+ height: 2px;
164
+ background: var(--text);
165
+ border-radius: 2px;
166
+ transition: all 0.25s ease;
167
+ }
168
+
169
+ .hamburger.active span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
170
+ .hamburger.active span:nth-child(2) { opacity: 0; }
171
+ .hamburger.active span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }
172
+
173
+ /* Overlay */
174
+ .overlay {
175
+ display: none;
176
+ position: fixed;
177
+ inset: 0;
178
+ background: rgba(0, 0, 0, 0.5);
179
+ z-index: 99;
180
+ opacity: 0;
181
+ transition: opacity 0.3s ease;
182
+ }
183
+
184
+ .overlay.visible { display: block; opacity: 1; }
185
+
186
+ /* -- Main Content -- */
187
+ .content {
188
+ margin-left: var(--sidebar-w);
189
+ padding: 2rem 2.5rem;
190
+ min-height: 100vh;
191
+ max-width: 900px;
192
+ }
193
+
194
+ /* -- Page Header -- */
195
+ .page-header { margin-bottom: 1.75rem; }
196
+ .page-header h1 { font-size: 1.75rem; font-weight: 700; margin-bottom: 0.25rem; }
197
+ .page-header.center { text-align: center; padding: 4rem 1rem; }
198
+ .subtitle { color: var(--text-muted); font-size: 0.95rem; }
199
+
200
+ /* -- Cards -- */
201
+ .card {
202
+ background: var(--bg-card);
203
+ border: 1px solid var(--border);
204
+ border-radius: var(--radius-lg);
205
+ padding: 1.5rem;
206
+ margin-bottom: 1.25rem;
207
+ }
208
+
209
+ .card h3 { font-size: 1.1rem; margin-bottom: 0.5rem; }
210
+ .card p { color: var(--text-muted); font-size: 0.9rem; margin-bottom: 0.75rem; }
211
+ .card-accent { border-left: 3px solid var(--accent); }
212
+ .card-muted { background: var(--bg-surface); }
213
+ .card-error { background: rgba(248,113,113,0.08); border-color: var(--danger); color: var(--danger); }
214
+
215
+ .card-grid {
216
+ display: grid;
217
+ grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
218
+ gap: 1rem;
219
+ margin-bottom: 1.25rem;
220
+ }
221
+
222
+ .card-grid > .card {
223
+ display: flex;
224
+ flex-direction: column;
225
+ }
226
+
227
+ .card-grid > .card > .btn,
228
+ .card-grid > .card > a.btn {
229
+ margin-top: auto;
230
+ align-self: flex-end;
231
+ }
232
+
233
+ /* -- Buttons -- */
234
+ .btn {
235
+ display: inline-flex;
236
+ align-items: center;
237
+ gap: 0.4rem;
238
+ padding: 0.5rem 1.1rem;
239
+ font-size: 0.9rem;
240
+ font-weight: 500;
241
+ border: none;
242
+ border-radius: var(--radius);
243
+ cursor: pointer;
244
+ transition: all 0.15s ease;
245
+ text-decoration: none;
246
+ }
247
+
248
+ .btn-primary { background: var(--accent); color: #fff; }
249
+ .btn-primary:hover { background: var(--accent-hover); color: #fff; }
250
+
251
+ .btn-outline { background: transparent; border: 1px solid var(--border); color: var(--accent); }
252
+ .btn-outline:hover { border-color: var(--accent); background: var(--accent-soft); }
253
+
254
+ .btn-ghost { background: transparent; color: var(--text-muted); }
255
+ .btn-ghost:hover { color: var(--text); background: var(--bg-hover); }
256
+
257
+ .btn-danger { background: var(--danger); color: #fff; }
258
+ .btn-danger:hover { opacity: 0.85; }
259
+
260
+ .btn-sm { padding: 0.35rem 0.75rem; font-size: 0.82rem; }
261
+
262
+ /* -- Inputs -- */
263
+ .input {
264
+ width: 100%;
265
+ padding: 0.55rem 0.85rem;
266
+ background: var(--bg);
267
+ border: 1px solid var(--border);
268
+ border-radius: var(--radius);
269
+ color: var(--text);
270
+ font-size: 0.9rem;
271
+ font-family: inherit;
272
+ outline: none;
273
+ transition: border-color 0.15s ease;
274
+ }
275
+
276
+ .input:focus { border-color: var(--accent); }
277
+ .input-sm { width: auto; padding: 0.35rem 0.6rem; font-size: 0.82rem; }
278
+
279
+ /* -- Counter -- */
280
+ .counter-card { text-align: center; }
281
+ .counter-display { padding: 1.5rem 0 1rem; }
282
+ .counter-value { font-size: 3.5rem; font-weight: 700; font-variant-numeric: tabular-nums; }
283
+ .counter-value.negative { color: var(--danger); }
284
+ .counter-controls { display: flex; justify-content: center; gap: 0.75rem; margin-bottom: 1.25rem; }
285
+ .counter-step { display: flex; align-items: center; justify-content: center; gap: 1rem; }
286
+ .counter-step label { display: flex; align-items: center; gap: 0.5rem; color: var(--text-muted); font-size: 0.9rem; }
287
+ .counter-step .input-sm { width: 70px; text-align: center; }
288
+
289
+ .history-list { display: flex; flex-wrap: wrap; gap: 0.5rem; }
290
+ .history-item {
291
+ background: var(--bg-hover);
292
+ padding: 0.3rem 0.65rem;
293
+ border-radius: var(--radius);
294
+ font-size: 0.82rem;
295
+ font-variant-numeric: tabular-nums;
296
+ }
297
+
298
+ /* -- Todos -- */
299
+ .todo-form { display: flex; gap: 0.75rem; }
300
+ .todo-form .input { flex: 1; }
301
+
302
+ .todo-toolbar { display: flex; justify-content: space-between; align-items: center; gap: 0.75rem; margin-bottom: 1rem; flex-wrap: wrap; }
303
+ .todo-filters { display: flex; gap: 0.35rem; }
304
+
305
+ .todo-list { list-style: none; }
306
+ .todo-item {
307
+ display: flex;
308
+ align-items: center;
309
+ gap: 0.6rem;
310
+ padding: 0.6rem 0;
311
+ border-bottom: 1px solid var(--border);
312
+ }
313
+ .todo-item:last-child { border-bottom: none; }
314
+ .todo-item.done .todo-text { text-decoration: line-through; color: var(--text-muted); }
315
+
316
+ .todo-check {
317
+ width: 22px;
318
+ height: 22px;
319
+ border-radius: 50%;
320
+ border: 2px solid var(--border);
321
+ background: transparent;
322
+ cursor: pointer;
323
+ display: flex;
324
+ align-items: center;
325
+ justify-content: center;
326
+ flex-shrink: 0;
327
+ font-size: 0;
328
+ transition: all 0.15s ease;
329
+ padding: 0;
330
+ }
331
+ .todo-check:hover {
332
+ border-color: var(--accent);
333
+ }
334
+ .todo-item.done .todo-check {
335
+ background: var(--accent);
336
+ border-color: var(--accent);
337
+ }
338
+ .todo-item.done .todo-check::after {
339
+ content: '✓';
340
+ font-size: 13px;
341
+ color: #fff;
342
+ line-height: 1;
343
+ }
344
+
345
+ .todo-text { flex: 1; font-size: 0.9rem; }
346
+
347
+ .todo-remove {
348
+ background: none;
349
+ border: none;
350
+ color: var(--text-muted);
351
+ cursor: pointer;
352
+ font-size: 0.9rem;
353
+ padding: 0.2rem 0.4rem;
354
+ border-radius: 4px;
355
+ }
356
+ .todo-remove:hover { color: var(--danger); background: rgba(248,113,113,0.1); }
357
+
358
+ .todo-footer { padding-top: 0.75rem; border-top: 1px solid var(--border); margin-top: 0.75rem; }
359
+ .empty-state { text-align: center; padding: 2rem 0; color: var(--text-muted); }
360
+
361
+ /* -- Signal Demo -- */
362
+ .signal-demo { display: flex; align-items: center; gap: 1rem; margin-top: 0.75rem; }
363
+ .signal-value { font-weight: 600; font-size: 1.1rem; color: var(--accent); font-variant-numeric: tabular-nums; }
364
+ .signal-demo .btn-sm {
365
+ background: var(--accent);
366
+ color: #fff;
367
+ border: none;
368
+ padding: 0.4rem 1rem;
369
+ border-radius: var(--radius);
370
+ font-weight: 500;
371
+ cursor: pointer;
372
+ transition: opacity 0.15s ease, transform 0.1s ease;
373
+ }
374
+ .signal-demo .btn-sm:hover { opacity: 0.85; }
375
+ .signal-demo .btn-sm:active { transform: scale(0.96); }
376
+
377
+ /* -- Stats -- */
378
+ .stats-row { display: flex; gap: 2rem; margin: 0.75rem 0; }
379
+ .stat { display: flex; flex-direction: column; }
380
+ .stat-value { font-size: 1.5rem; font-weight: 700; color: var(--accent); font-variant-numeric: tabular-nums; }
381
+ .stat-label { font-size: 0.8rem; color: var(--text-muted); }
382
+
383
+ /* -- Stats Groups -- */
384
+ .stats-grid {
385
+ display: grid;
386
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
387
+ gap: 1rem;
388
+ margin: 1rem 0 0.75rem;
389
+ }
390
+
391
+ .stat-group {
392
+ background: var(--bg-card);
393
+ border: 1px solid var(--border);
394
+ border-radius: var(--radius-lg);
395
+ padding: 1.15rem 1.25rem;
396
+ display: flex;
397
+ flex-direction: column;
398
+ gap: 0.65rem;
399
+ }
400
+
401
+ .stat-group-title {
402
+ font-size: 0.78rem;
403
+ font-weight: 600;
404
+ text-transform: uppercase;
405
+ letter-spacing: 0.04em;
406
+ color: var(--text-muted);
407
+ }
408
+
409
+ .stat-group-values {
410
+ display: flex;
411
+ gap: 1.5rem;
412
+ justify-content: center;
413
+ }
414
+
415
+ .stat-group-values .stat {
416
+ align-items: center;
417
+ }
418
+
419
+ .stat-group .stat-value {
420
+ font-size: 1.35rem;
421
+ }
422
+
423
+ .stat-group .stat-label {
424
+ font-size: 0.75rem;
425
+ }
426
+
427
+ /* -- API Demo -- */
428
+ .user-grid {
429
+ display: grid;
430
+ grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
431
+ gap: 0.75rem;
432
+ margin-top: 0.75rem;
433
+ }
434
+
435
+ .user-card {
436
+ display: flex;
437
+ flex-direction: column;
438
+ gap: 0.15rem;
439
+ padding: 0.85rem;
440
+ background: var(--bg-hover);
441
+ border: 1px solid var(--border);
442
+ border-radius: var(--radius);
443
+ cursor: pointer;
444
+ text-align: left;
445
+ font-family: inherit;
446
+ color: var(--text);
447
+ transition: all 0.15s ease;
448
+ }
449
+
450
+ .user-card:hover { border-color: var(--accent); background: var(--accent-soft); }
451
+ .user-card strong { font-size: 0.9rem; }
452
+ .user-card small { font-size: 0.8rem; color: var(--text-muted); }
453
+
454
+ .user-detail-header { display: flex; justify-content: space-between; align-items: flex-start; gap: 1rem; }
455
+
456
+ .posts-list { display: flex; flex-direction: column; gap: 1rem; margin-top: 0.75rem; }
457
+ .post-item { border-left: 3px solid var(--border); padding-left: 1rem; }
458
+ .post-item h4 { font-size: 0.95rem; margin-bottom: 0.25rem; text-transform: capitalize; }
459
+ .post-item p { color: var(--text-muted); font-size: 0.85rem; }
460
+
461
+ /* Loading bar */
462
+ .loading-bar {
463
+ height: 3px;
464
+ background: linear-gradient(90deg, transparent, var(--accent), transparent);
465
+ border-radius: 2px;
466
+ margin-bottom: 1rem;
467
+ animation: loading 1.2s ease-in-out infinite;
468
+ }
469
+ @keyframes loading { 0%,100% { opacity: 0.3; } 50% { opacity: 1; } }
470
+
471
+ /* -- About -- */
472
+ .theme-toggle { display: flex; align-items: center; gap: 1rem; }
473
+ .feature-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 0.5rem; }
474
+ .feature-item {
475
+ display: flex;
476
+ flex-direction: column;
477
+ padding: 0.65rem 0.85rem;
478
+ background: var(--bg-hover);
479
+ border-radius: var(--radius);
480
+ font-size: 0.85rem;
481
+ }
482
+ .feature-item strong { color: var(--accent); font-size: 0.82rem; margin-bottom: 0.1rem; }
483
+ .feature-item span { color: var(--text-muted); font-size: 0.8rem; }
484
+
485
+ .next-steps { padding-left: 1.25rem; }
486
+ .next-steps li { margin-bottom: 0.4rem; font-size: 0.9rem; color: var(--text-muted); }
487
+ .next-steps a { color: var(--accent); }
488
+
489
+ .muted { color: var(--text-muted); }
490
+
491
+ /* -- Toast Notifications -- */
492
+ .toast-container {
493
+ position: fixed;
494
+ bottom: 1.5rem;
495
+ right: 1.5rem;
496
+ display: flex;
497
+ flex-direction: column;
498
+ gap: 0.5rem;
499
+ z-index: 200;
500
+ pointer-events: none;
501
+ }
502
+
503
+ .toast {
504
+ padding: 0.65rem 1.1rem;
505
+ border-radius: var(--radius);
506
+ font-size: 0.85rem;
507
+ font-weight: 500;
508
+ color: #fff;
509
+ animation: toast-in 0.3s ease;
510
+ pointer-events: auto;
511
+ }
512
+
513
+ .toast-success { background: var(--success); }
514
+ .toast-error { background: var(--danger); }
515
+ .toast-info { background: var(--info); }
516
+
517
+ .toast-exit { animation: toast-out 0.3s ease forwards; }
518
+
519
+ @keyframes toast-in { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: translateY(0); } }
520
+ @keyframes toast-out { from { opacity: 1; transform: translateY(0); } to { opacity: 0; transform: translateY(-10px); } }
521
+
522
+ /* -- Route Transition -- */
523
+ #app { animation: fade-in 0.25s ease; }
524
+ @keyframes fade-in { from { opacity: 0; transform: translateY(6px); } to { opacity: 1; transform: translateY(0); } }
525
+
526
+ /* -- Responsive: Mobile -- */
527
+ @media (max-width: 768px) {
528
+ .sidebar {
529
+ transform: translateX(-100%);
530
+ width: 260px;
531
+ }
532
+ .sidebar.open { transform: translateX(0); }
533
+
534
+ .topbar { display: flex; }
535
+
536
+ .content {
537
+ margin-left: 0;
538
+ padding: 1.25rem;
539
+ padding-top: calc(var(--topbar-h) + 1.25rem);
540
+ }
541
+
542
+ .card-grid { grid-template-columns: 1fr; }
543
+ .user-grid { grid-template-columns: 1fr 1fr; }
544
+ .feature-grid { grid-template-columns: 1fr; }
545
+ .stats-row { gap: 1.25rem; }
546
+
547
+ .page-header h1 { font-size: 1.4rem; }
548
+ .counter-value { font-size: 2.75rem; }
549
+ }
550
+
551
+ @media (max-width: 480px) {
552
+ .content { padding: 1rem; padding-top: calc(var(--topbar-h) + 1rem); }
553
+ .user-grid { grid-template-columns: 1fr; }
554
+ .todo-toolbar { flex-direction: column; align-items: stretch; }
555
+ .todo-toolbar .input-sm { width: 100%; }
556
+ }