Skip to main content

max / goingson

Prune dead CSS and collapse duplicated primitives Three passes over the frontend, no behaviour change intended. Dead: 90 classes across 107 rules that no markup ever referenced, including a whole abandoned plugin UI. Verified by set diff that only the targeted names disappeared. Layout: 17 domain-named classes retired onto the primitives this stylesheet's own header already documents (.stack/.stack-peer, .row-flex/.row-flex-peer, .row). They were re-implementing it longhand. Type: 201 font-size declarations snapped onto the 10-step scale defined at the top of the file. 137 were already on-scale and only changed to token form; 63 shifted, none by more than 0.05rem. Dropped 9 var() fallbacks, five of which contradicted the scale they fell back from. Then 18 more classes retired onto composable text utilities, which the snapping made possible by turning near-identical rules into identical ones. Classes 1094 -> 972, rules 1651 -> 1512.
Author: Max Johnson <me@maxj.phd> · 2026-07-28 20:46 UTC
Signed with PGP, not checked
Commit: 4e46874585aaf8f0e2f93ab8a859c39b1d2832ca
Parent: e15363e
26 files changed, +87 insertions, -203 deletions
@@ -179,7 +179,7 @@
179 179 <div id="projects-view" class="subview hidden" role="tabpanel" aria-labelledby="projects-tab">
180 180 <div class="page-header">
181 181 <h2 class="page-title">Projects</h2>
182 - <div class="page-header-actions">
182 + <div class="row-flex row-flex-peer">
183 183 <span id="projects-filter-slot"></span>
184 184 <button class="btn btn-primary" data-act="projects.openNew" title="New project (n)">+ New Project <kbd class="kbd-hint">n</kbd></button>
185 185 </div>
@@ -197,7 +197,7 @@
197 197 <div id="problems-view" class="subview hidden" role="tabpanel" aria-labelledby="problems-tab">
198 198 <div class="page-header">
199 199 <h2 class="page-title">Problems</h2>
200 - <div class="page-header-actions">
200 + <div class="row-flex row-flex-peer">
201 201 <select id="problems-source-filter" class="form-select form-select--compact" aria-label="Filter by source" data-change="problems.setSourceFilter">
202 202 <option value="">All sources</option>
203 203 </select>
@@ -213,7 +213,7 @@
213 213 </div>
214 214 </div>
215 215 <p class="view-hint">Candidates, not work. Ranked by painhours, so anything left untriaged climbs on its own. Promote the ones worth doing.</p>
216 - <div id="problems-list" class="problems-list">
216 + <div id="problems-list" class="stack stack-peer">
217 217 <div class="skeleton-shimmer" aria-label="Loading problems">
218 218 <div class="skeleton-row"><div class="skeleton-lines"><div class="skeleton-line long"></div><div class="skeleton-line short"></div></div></div>
219 219 <div class="skeleton-row"><div class="skeleton-lines"><div class="skeleton-line medium"></div><div class="skeleton-line long"></div></div></div>
@@ -356,19 +356,15 @@
356 356 .row for raw flex with no align. .flex-1 to grow a child. */
357 357 .flex-1 { flex: 1; }
358 358 .row { display: flex; }
359 - .text-sm-secondary { font-size: 0.875rem; color: var(--content-secondary); }
360 - .text-xs-secondary { font-size: 0.75rem; color: var(--content-secondary); }
359 + .text-sm-secondary { font-size: var(--font-size-base); color: var(--content-secondary); }
361 360 .text-accent-red { color: var(--danger); }
362 361 .mb-section { margin-bottom: var(--gap-section); }
363 362 .mb-peer { margin-bottom: var(--gap-peer); }
364 363
365 364 /* --- FORM SIZING UTILITIES */
366 365 .form-select--compact { width: auto; min-width: 120px; }
367 -
368 - /* --- SETTINGS UTILITIES */
369 - .settings-divider { margin-top: var(--gap-pane); padding-top: var(--gap-pane); border-top: var(--border-width) solid var(--border); }
370 366 .settings-heading { margin-bottom: var(--gap-section); font-family: var(--font-heading); }
371 - .settings-desc { font-size: 0.875rem; color: var(--content-secondary); margin-bottom: var(--gap-section); }
367 + .settings-desc { font-size: var(--font-size-base); color: var(--content-secondary); margin-bottom: var(--gap-section); }
372 368
373 369 /* --- SUBTASK UTILITIES */
374 370 /* .subtask-item is the base row; --linked is a modifier for subtasks that
@@ -411,7 +407,6 @@
411 407 .btn,
412 408 .tab,
413 409 .tab-label,
414 - .tab-icon,
415 410 .tab-navigation,
416 411 .mobile-tab,
417 412 .mobile-tab-bar,
@@ -427,8 +422,7 @@
427 422
428 423 input,
429 424 textarea,
430 - [contenteditable="true"],
431 - .selectable {
425 + [contenteditable="true"]{
432 426 -webkit-user-select: text;
433 427 user-select: text;
434 428 }
@@ -466,7 +460,7 @@
466 460 }
467 461
468 462 .ui-mode-desktop .app-subtitle {
469 - font-size: 0.875rem;
463 + font-size: var(--font-size-base);
470 464 color: var(--content-muted);
471 465 font-weight: 500;
472 466 line-height: 1;
@@ -509,17 +503,9 @@
509 503 box-shadow: var(--bevel-inset);
510 504 }
511 505
512 - .ui-mode-desktop .tab-icon {
513 - font-size: var(--font-size-xl);
514 - }
515 -
516 506 .ui-mode-desktop .tab-label {
517 507 font-weight: 600;
518 - font-size: 0.9rem;
519 - }
520 -
521 - .ui-mode-desktop .tab.tab-right {
522 - margin-left: auto;
508 + font-size: var(--font-size-base);
523 509 }
524 510
525 511 /* --- Tab Groups & Sub-Views (shared) --- */
@@ -586,11 +572,6 @@
586 572 }
587 573
588 574 /* Right-aligned cluster of header controls (e.g. a filter toggle + primary action). */
589 - .page-header-actions {
590 - display: flex;
591 - align-items: center;
592 - gap: var(--gap-peer);
593 - }
594 575
595 576 /* --- Merged pill + page-header toolbar --- */
596 577 /* Tab groups use pills as view labels; page-header buttons float right on the same row */
@@ -630,7 +611,7 @@
630 611 padding: var(--gap-peer) var(--gap-section);
631 612 border: var(--border-width) solid var(--border);
632 613 border-radius: var(--radius-sm);
633 - font-size: 0.9rem;
614 + font-size: var(--font-size-base);
634 615 font-weight: 600;
635 616 cursor: pointer;
636 617 text-decoration: none;
@@ -699,7 +680,7 @@
699 680
700 681 .btn-sm {
701 682 padding: var(--gap-bound) var(--gap-group);
702 - font-size: 0.8rem;
683 + font-size: var(--font-size-md);
703 684 }
704 685
705 686 /* 10. (vacated: Quick Add Input was deleted 2026-05-24; the modal at
@@ -829,13 +810,13 @@
829 810 }
830 811
831 812 .card-description {
832 - font-size: 0.9rem;
813 + font-size: var(--font-size-base);
833 814 color: var(--content-secondary);
834 815 margin-bottom: var(--gap-section);
835 816 }
836 817
837 818 .markdown-content {
838 - font-size: 0.9rem;
819 + font-size: var(--font-size-base);
839 820 color: var(--content-secondary);
840 821 line-height: 1.5;
841 822 }
@@ -866,7 +847,7 @@
866 847 padding: var(--step-tight) var(--step-roomy);
867 848 border: var(--border-width-sm) solid var(--border);
868 849 border-radius: var(--radius-sm);
869 - font-size: 0.8125rem;
850 + font-size: var(--font-size-md);
870 851 font-weight: 600;
871 852 background: var(--surface-raised);
872 853 color: var(--content);
@@ -904,8 +885,7 @@
904 885 }
905 886
906 887 /* Size modifiers */
907 - .badge--xs { padding: var(--step-hair) var(--step-base); font-size: 0.75rem; }
908 - .badge--sm { padding: var(--step-hair) var(--step-roomy); font-size: 0.78rem; }
888 + .badge--xs { padding: var(--step-hair) var(--step-base); font-size: var(--font-size-sm); }
909 889
910 890 /* Filled intent, solid accent fill, no border. Flat by intent, so no bevel
911 891 either: the fill is the signal and an edge would fight it. Pairs with
@@ -924,7 +904,7 @@
924 904 .kbd-hint {
925 905 display: inline-block;
926 906 font-family: var(--font-mono, monospace);
927 - font-size: 0.65rem;
907 + font-size: var(--font-size-xxs);
928 908 font-weight: 600;
929 909 padding: var(--step-hair) var(--step-snug);
930 910 margin-left: var(--gap-bound);
@@ -995,7 +975,7 @@
995 975
996 976 .data-table th {
997 977 background-color: var(--surface-overlay);
998 - font-size: 0.8rem;
978 + font-size: var(--font-size-md);
999 979 font-weight: 700;
1000 980 text-transform: uppercase;
1001 981 letter-spacing: 0.08em;
@@ -1050,7 +1030,7 @@
1050 1030
1051 1031 .task-header-row .task-cell {
1052 1032 padding: var(--gap-peer) 0;
1053 - font-size: 0.8rem;
1033 + font-size: var(--font-size-md);
1054 1034 font-weight: 700;
1055 1035 text-transform: uppercase;
1056 1036 letter-spacing: 0.05em;
@@ -1122,7 +1102,7 @@
1122 1102 }
1123 1103
1124 1104 .task-project {
1125 - font-size: 0.85rem;
1105 + font-size: var(--font-size-base);
1126 1106 color: var(--content-secondary);
1127 1107 white-space: nowrap;
1128 1108 }
@@ -1211,38 +1191,11 @@
1211 1191 color: var(--content);
1212 1192 padding: var(--step-hair) var(--step-base);
1213 1193 border-radius: var(--radius-xs);
1214 - font-size: 0.75rem;
1194 + font-size: var(--font-size-sm);
1215 1195 font-weight: 600;
1216 1196 border: var(--border-width) solid var(--border);
1217 1197 }
1218 1198
1219 - .recurrence-icon {
1220 - color: var(--category-five);
1221 - font-size: 0.85rem;
1222 - font-weight: 700;
1223 - }
1224 -
1225 - .annotation-badge {
1226 - background-color: var(--warning);
1227 - color: var(--content);
1228 - padding: var(--step-hair) var(--step-base);
1229 - border-radius: var(--radius-xs);
1230 - font-size: 0.7rem;
1231 - font-weight: 700;
1232 - border: var(--border-width-sm) solid var(--border);
1233 - }
1234 -
1235 - .subtask-badge {
1236 - background-color: var(--surface-overlay);
1237 - color: var(--content);
1238 - padding: var(--step-hair) var(--step-base);
1239 - border-radius: var(--radius-xs);
1240 - font-size: 0.7rem;
1241 - font-weight: 700;
1242 - border: var(--border-width-sm) solid var(--border);
1243 - margin-left: var(--gap-bound);
1244 - }
1245 -
1246 1199 .task-pending { }
1247 1200 .task-started {
1248 1201 border-left: 4px solid var(--success);
@@ -1253,26 +1206,6 @@
1253 1206 }
1254 1207 .task-deleted { display: none; }
1255 1208
1256 - /* 15. Due Date Badges */
1257 - .due-overdue {
1258 - color: var(--danger);
1259 - font-weight: 700;
1260 - background: #fde8ea; /* Fallback for browsers without color-mix() */
1261 - background: color-mix(in srgb, var(--danger) 15%, var(--surface-raised));
1262 - padding: var(--step-tight) var(--step-base);
1263 - border-radius: var(--radius-xs);
1264 - }
1265 - .due-today {
1266 - color: var(--warning);
1267 - font-weight: 700;
1268 - background: #fef8e6; /* Fallback for browsers without color-mix() */
1269 - background: color-mix(in srgb, var(--warning) 15%, var(--surface-raised));
1270 - padding: var(--step-tight) var(--step-base);
1271 - border-radius: var(--radius-xs);
1272 - }
1273 - .due-soon { color: var(--content-secondary); }
1274 - .due-future { color: var(--content-muted); }
1275 -
1276 1209 /* 16. Events */
1277 1210 .events-list {
1278 1211 display: flex;
@@ -1314,7 +1247,7 @@
1314 1247
1315 1248 .event-header-row .event-cell {
1316 1249 padding: var(--gap-peer) var(--gap-section);
1317 - font-size: 0.8rem;
1250 + font-size: var(--font-size-md);
1318 1251 font-weight: 700;
1319 1252 text-transform: uppercase;
1320 1253 letter-spacing: 0.05em;
@@ -1354,18 +1287,13 @@
1354 1287 text-overflow: ellipsis;
1355 1288 }
1356 1289
1357 - /* Event Table row styles - base in .data-table */
1358 - .event-row {
1359 - cursor: pointer;
1360 - }
1361 -
1362 1290 .event-cell-date {
1363 1291 white-space: nowrap;
1364 1292 }
1365 1293
1366 1294 .event-cell-date .event-date-num {
1367 1295 font-weight: 700;
1368 - font-size: 0.9rem;
1296 + font-size: var(--font-size-base);
1369 1297 color: var(--content);
1370 1298 margin-right: var(--gap-bound);
1371 1299 }
@@ -1375,7 +1303,7 @@
1375 1303 padding: var(--step-hair) var(--step-snug);
1376 1304 background: var(--success);
1377 1305 color: var(--content-on-action);
1378 - font-size: 0.7rem;
1306 + font-size: var(--font-size-xs);
1379 1307 font-weight: 700;
1380 1308 text-transform: uppercase;
1381 1309 border-radius: var(--radius-xs);
@@ -1385,7 +1313,7 @@
1385 1313
1386 1314 .event-cell-time {
1387 1315 font-family: var(--font-mono);
1388 - font-size: 0.85rem;
1316 + font-size: var(--font-size-base);
1389 1317 color: var(--content-secondary);
1390 1318 }
1391 1319
@@ -1395,7 +1323,7 @@
1395 1323
1396 1324 .event-cell-location {
1397 1325 color: var(--content-secondary);
1398 - font-size: 0.875rem;
1326 + font-size: var(--font-size-base);
1399 1327 }
1400 1328
1401 1329 /* Event proximity-based badge colors */
@@ -1416,18 +1344,6 @@
1416 1344 background: var(--content-muted);
1417 1345 }
1418 1346
1419 - /* Past events - muted appearance */
1420 - .event-row.event-past {
1421 - opacity: 0.7;
1422 - }
1423 -
1424 - .no-upcoming-events {
1425 - text-align: center;
1426 - padding: var(--gap-pane);
1427 - color: var(--content-secondary);
1428 - font-style: italic;
1429 - }
1430 -
1431 1347 /* Past Events Collapsible Section */
1432 1348 .past-events-section {
1433 1349 margin-top: var(--gap-section);
@@ -1453,7 +1369,7 @@
1453 1369
1454 1370 .past-events-toggle::before {
1455 1371 content: '▶';
1456 - font-size: 0.7rem;
1372 + font-size: var(--font-size-xs);
1457 1373 }
1458 1374
1459 1375 .past-events-section[open] .past-events-toggle::before {
@@ -1472,7 +1388,7 @@
1472 1388 .past-events-count {
1473 1389 background: var(--content-muted);
1474 1390 color: var(--content-on-action);
1475 - font-size: 0.75rem;
1391 + font-size: var(--font-size-sm);
1476 1392 padding: var(--step-hair) var(--step-base);
1477 1393 border-radius: var(--radius-sm);
1478 1394 }
@@ -1504,7 +1420,7 @@
1504 1420 /* Plain heading above the Upcoming list */
1505 1421 .events-section-heading {
1506 1422 margin: var(--gap-section) 0 var(--gap-peer);
1507 - font-size: 0.95rem;
1423 + font-size: var(--font-size-lg);
1508 1424 font-weight: 700;
1509 1425 color: var(--content-secondary);
1510 1426 text-transform: uppercase;
@@ -1526,32 +1442,11 @@
1526 1442 background-color: var(--surface-overlay);
1527 1443 }
1528 1444
1529 - .event-date {
1530 - flex-shrink: 0;
1531 - width: 80px;
1532 - text-align: center;
1533 - padding: var(--gap-peer);
1534 - background-color: var(--success);
1535 - border-radius: var(--radius-sm);
1536 - color: var(--content-on-action);
1537 - }
1538 -
1539 - .event-date-day {
1540 - font-size: 0.7rem;
1541 - font-weight: 700;
1542 - text-transform: uppercase;
1543 - letter-spacing: 0.05em;
1544 - }
1545 -
1546 1445 .event-date-num {
1547 1446 font-size: var(--font-size-3xl);
1548 1447 font-weight: 700;
1549 1448 }
1550 1449
1551 - .event-content {
1552 - flex: 1;
1553 - }
1554 -
1555 1450 .event-title {
1556 1451 font-family: var(--font-heading);
1557 1452 font-weight: 700;
@@ -1560,23 +1455,12 @@
1560 1455 margin-bottom: var(--gap-bound);
1561 1456 }
1562 1457
1563 - .event-details {
1564 - font-size: 0.875rem;
1565 - color: var(--content-secondary);
1566 - display: flex;
1567 - gap: var(--gap-section);
1568 - }
1569 -
1570 - .event-time, .event-location {
1458 + .event-time{
1571 1459 display: flex;
1572 1460 align-items: center;
1573 1461 gap: var(--gap-bound);
1574 1462 }
1575 1463
1576 - .event-project {
1577 - margin-top: var(--gap-peer);
1578 - }
1579 -
1580 1464 /* 17. Email List */
1581 1465 /* Base chrome from .card.card--shell; only the flex sizing for the
1582 1466 list panel remains here. */
@@ -1626,11 +1510,6 @@
1626 1510 border-left: 4px solid var(--success);
1627 1511 }
1628 1512
1629 - .email-checkbox {
1630 - flex-shrink: 0;
1631 - margin-top: var(--gap-bound);
1632 - }
1633 -
1634 1513 .email-content {
1635 1514 flex: 1;
1636 1515 min-width: 0;
@@ -1650,7 +1529,7 @@
1650 1529 .thread-badge {
1651 1530 background-color: var(--surface-sunken);
1652 1531 color: var(--content-secondary);
1653 - font-size: 0.7rem;
1532 + font-size: var(--font-size-xs);
1654 1533 font-weight: 600;
1655 1534 padding: var(--step-hair) var(--step-snug);
1656 1535 border-radius: var(--radius-md);
@@ -1660,20 +1539,20 @@
1660 1539
1661 1540 .email-from {
1662 1541 color: var(--content);
1663 - font-size: 0.9rem;
1542 + font-size: var(--font-size-base);
1664 1543 font-weight: 600;
1665 1544 }
1666 1545
1667 1546 .email-date {
1668 1547 color: var(--content-muted);
1669 - font-size: 0.8rem;
1548 + font-size: var(--font-size-md);
1670 1549 flex-shrink: 0;
1671 1550 font-weight: 600;
1672 1551 }
1673 1552
1674 1553 .email-subject {
1675 1554 color: var(--content);
1676 - font-size: 0.95rem;
1555 + font-size: var(--font-size-lg);
1677 1556 margin-bottom: var(--gap-bound);
1678 1557 white-space: nowrap;
1679 1558 overflow: hidden;
@@ -1682,7 +1561,7 @@
1682 1561
1683 1562 .email-preview {
1684 1563 color: var(--content-muted);
1685 - font-size: 0.85rem;
1564 + font-size: var(--font-size-base);
1686 1565 white-space: nowrap;
1687 1566 overflow: hidden;
1688 1567 text-overflow: ellipsis;
@@ -1866,7 +1745,7 @@
1866 1745 background: none;
1867 1746 border: none;
1868 1747 cursor: pointer;
1869 - font-size: 0.85rem;
1748 + font-size: var(--font-size-base);
1870 1749 font-weight: 600;
1871 1750 color: var(--action);
1872 1751 padding: var(--gap-bound) 0;
@@ -1890,7 +1769,7 @@
1890 1769 align-items: center;
1891 1770 gap: var(--gap-bound);
1892 1771 padding: var(--step-tight) var(--step-base);
1893 - font-size: 0.8rem;
1772 + font-size: var(--font-size-md);
1894 1773 border: var(--border-width) solid var(--border);
1895 1774 border-radius: var(--radius-sm);
1896 1775 background: var(--surface-raised);
@@ -1912,13 +1791,13 @@
1912 1791
1913 1792 .recurrence-config .form-input,
1914 1793 .recurrence-config .form-select {
1915 - font-size: 0.85rem;
1794 + font-size: var(--font-size-base);
1916 1795 padding: var(--gap-bound) var(--gap-peer);
1917 1796 }
1918 1797
1919 1798 .form-label {
1920 1799 display: block;
1921 - font-size: 0.9rem;
1800 + font-size: var(--font-size-base);
1922 1801 font-weight: 700;
1923 1802 color: var(--content);
1924 1803 margin-bottom: var(--gap-bound);
@@ -1933,7 +1812,7 @@
1933 1812 border-radius: var(--radius-sm);
1934 1813 background-color: var(--surface-raised);
1935 1814 color: var(--content);
1936 - font-size: 1rem;
1815 + font-size: var(--font-size-lg);
1937 1816 box-shadow: var(--bevel-inset);
1938 1817 }
1939 1818
@@ -1958,13 +1837,13 @@
1958 1837 border: none;
1959 1838 box-shadow: none;
1960 1839 padding: var(--gap-peer);
1961 - font-size: 0.875rem;
1840 + font-size: var(--font-size-base);
1962 1841 }
1963 1842 .form-input--ghost:focus { background: transparent; box-shadow: none; }
1964 1843 .form-select--ghost {
1965 1844 background: var(--surface-page);
1966 1845 padding: var(--gap-peer);
1967 - font-size: 0.875rem;
1846 + font-size: var(--font-size-base);
1968 1847 }
1969 1848
1970 1849 .form-actions {
@@ -1990,7 +1869,7 @@
1990 1869
1991 1870 .form-error {
1992 1871 color: var(--danger);
1993 - font-size: 0.8rem;
1872 + font-size: var(--font-size-md);
1994 1873 font-weight: 600;
1995 1874 margin-top: var(--gap-bound);
1996 1875 display: none;
@@ -2044,8 +1923,13 @@
2044 1923 .text-muted { color: var(--content-muted); }
2045 1924 .text-secondary { color: var(--content-secondary); }
2046 1925 .text-danger { color: var(--danger); }
1926 +
1927 + /* Size and colour compose: `text-sm text-muted`, not a class per pairing.
1928 + Named for the scale step, so a size never disagrees with its name the way
1929 + .text-sm-secondary does (it resolves to --font-size-base). */
1930 + .text-xs { font-size: var(--font-size-xs); }
2047 1931 .text-sm { font-size: var(--font-size-sm); }
2048 - .text-xs { font-size: var(--font-size-xs, 0.75rem); }
1932 + .text-base { font-size: var(--font-size-base); }
2049 1933
2050 1934 .section-divider {
2051 1935 border-top: var(--border-width-sm) solid var(--border);
@@ -2084,7 +1968,7 @@
2084 1968 margin-bottom: var(--gap-bound);
2085 1969 }
2086 1970 .sync-stat-value {
2087 - font-size: var(--font-size-md, 0.9rem);
1971 + font-size: var(--font-size-md);
2088 1972 }
Lines truncated
@@ -330,7 +330,7 @@
330 330 <div class="about-panel">
331 331 <h2 class="about-title">GoingsOn</h2>
332 332 <p class="about-tagline">Tasks, email, calendar, contacts.</p>
333 - <p class="about-version">Version ${appVersion}</p>
333 + <p class="text-sm text-muted">Version ${appVersion}</p>
334 334 <dl class="about-info-list">
335 335 <dt>Publisher</dt><dd>Make Creative, LLC</dd>
336 336 <dt>License</dt><dd>PolyForm Noncommercial 1.0.0</dd>
@@ -367,7 +367,7 @@
367 367 </p>
368 368 <div class="welcome-section">
369 369 <h3 class="welcome-subhead">Get Started</h3>
370 - <div class="welcome-step-stack">
370 + <div class="stack stack-peer">
371 371 <button class="btn btn-secondary text-left" data-act="ui.markSeenThen" data-a1="go-welcomed" data-a2="keyboard.openQuickAdd">${step1}</button>
372 372 <button class="btn btn-secondary text-left" data-act="ui.markSeenThen" data-a1="go-welcomed" data-a2="navigation.switchView" data-a3="day-plan">${step2}</button>
373 373 <button class="btn btn-secondary text-left" data-act="ui.markSeenThen" data-a1="go-welcomed" data-a2="emails.openAccountsModal"><strong>3.</strong> Add an email account</button>
@@ -63,14 +63,14 @@
63 63 <div class="attachment-info">
64 64 <div class="attachment-filename"
65 65 title="${escAttrVal(a.filename)}">${esc(a.filename)}</div>
66 - <div class="attachment-meta">${esc(a.fileSizeFormatted)}</div>
66 + <div class="text-sm text-muted">${esc(a.fileSizeFormatted)}</div>
67 67 </div>
68 - <div class="attachment-actions">
68 + <div class="row row-flex-peer">
69 69 ${a.hasLocalBlob ? `
70 70 <button class="btn btn-sm btn-secondary" data-act="attachments.open" data-a1="${escAttr(a.id)}" title="Open">Open</button>
71 71 <button class="btn btn-sm btn-secondary" data-act="attachments.saveAs" data-a1="${escAttr(a.id)}" data-a2="${escAttr(a.filename)}" title="Save As">Save</button>
72 72 ` : `
73 - <span class="attachment-sync-warning">Sync needed</span>
73 + <span class="text-sm text-muted">Sync needed</span>
74 74 `}
75 75 <button class="btn btn-sm btn-secondary text-accent-red"
76 76 data-act="attachments.remove" data-a1="${escAttr(a.id)}" data-a2="${escAttr(tid)}" data-a3="${escAttr(pid)}" title="Delete">×</button>
@@ -63,7 +63,7 @@
63 63 `;
64 64 }
65 65
66 - const content = `<div class="snooze-options">${optionsHtml}</div>`;
66 + const content = `<div class="stack stack-peer">${optionsHtml}</div>`;
67 67
68 68 // Store the callback for when user clicks an option
69 69 GoingsOn.bulk._snoozeCallback = snoozeCallback;
@@ -211,7 +211,7 @@
211 211
212 212 const content = `
213 213 <p class="bulk-modal-prompt bulk-modal-prompt--wide">Set priority for ${selectedTaskIds.size} tasks:</p>
214 - <div class="bulk-priority-row">
214 + <div class="row row-flex-peer">
215 215 <button class="btn btn-sm" data-act="bulk._applyPriority" data-a1="High">High</button>
216 216 <button class="btn btn-sm" data-act="bulk._applyPriority" data-a1="Medium">Medium</button>
217 217 <button class="btn btn-sm" data-act="bulk._applyPriority" data-a1="Low">Low</button>
@@ -139,7 +139,7 @@
139 139 const showAll = totalActivity > timelineItems.length
140 140 ? `<button class="btn btn-link" data-act="contactDashboard.showAllActivity">Show all ${totalActivity} interactions</button>`
141 141 : '';
142 - timelineHtml = `<div class="contact-timeline" id="contact-timeline">${items}</div>${showAll}`;
142 + timelineHtml = `<div class="stack stack-peer" id="contact-timeline">${items}</div>${showAll}`;
143 143 }
144 144
145 145 // Notes section
@@ -150,7 +150,7 @@
150 150 data-keydown="dayPlan.handleUnscheduledTaskKeydown" data-a1="@event" data-a2="${escAttr(task.id)}"
151 151 tabindex="0" role="listitem" aria-label="Unscheduled task: ${esc(task.title)} (Press S to schedule)">
152 152 <div class="unscheduled-task-title">${esc(task.title)}</div>
153 - <div class="unscheduled-task-meta">
153 + <div class="text-sm text-secondary">
154 154 ${task.projectName ? esc(task.projectName) + ' - ' : ''}${task.priority}
155 155 </div>
156 156 <div class="unscheduled-task-actions" data-act="ui.noop">
@@ -263,8 +263,8 @@
263 263 <div class="account-row-actions">
264 264 <div class="account-row-info">
265 265 <div class="account-row-name">${esc(a.account_name)}${oauthBadge}</div>
266 - <div class="account-row-meta">${esc(a.email_address)}</div>
267 - <div class="account-row-sync">Last sync: ${a.lastSyncFormatted}</div>
266 + <div class="text-sm text-secondary">${esc(a.email_address)}</div>
267 + <div class="text-sm text-secondary">Last sync: ${a.lastSyncFormatted}</div>
268 268 </div>
269 269 ${editBtn}
270 270 <button class="btn btn-sm btn-danger" data-act="emails.deleteAccount" data-a1="${escAttr(a.id)}" aria-label="Delete account">&times;</button>
@@ -232,7 +232,7 @@
232 232 <div class="email-item email-draft-item"
233 233 data-act="emails.openDraft" data-a1="${escAttr(d.id)}">
234 234 <div class="email-draft-subject">${esc(subject)}</div>
235 - <div class="email-draft-meta">To: ${esc(to)} · ${d.receivedFormatted}</div>
235 + <div class="text-sm text-secondary">To: ${esc(to)} · ${d.receivedFormatted}</div>
236 236 </div>
237 237 `;
238 238 }).join('');
@@ -30,7 +30,7 @@
30 30 <div class="avatar avatar--sm">${initials}</div>
31 31 <div class="email-sender-info">
32 32 <span class="email-sender-name">${esc(name)}</span>
33 - ${company ? `<span class="email-sender-company">${company}</span>` : ''}
33 + ${company ? `<span class="text-sm text-secondary">${company}</span>` : ''}
34 34 </div>
35 35 <button class="btn btn-sm btn-secondary" data-act="ui.closeModalThen" data-a1="contacts.open" data-a2="${escAttr(contact.id)}">View Contact</button>
36 36 </div>
@@ -168,7 +168,7 @@
168 168 */
169 169 function renderHeader(email, latest, messageCount, senderCardHtml) {
170 170 const subjectLine = messageCount > 1
171 - ? `${esc(email.subject)} <span class="email-thread-count">(${messageCount} messages)</span>`
171 + ? `${esc(email.subject)} <span class="text-sm text-muted">(${messageCount} messages)</span>`
172 172 : esc(email.subject);
173 173 const labels = (email.labels || []).length > 0
174 174 ? ' · ' + email.labels.map(l => `<span class="badge badge--xs badge--filled" data-color="blue">${esc(l)}</span>`).join(' ')
@@ -116,7 +116,7 @@
116 116 <div class="backup-item">
117 117 <div>
118 118 <div class="backup-item-name">${esc(backup.fileName)}</div>
119 - <div class="backup-item-meta">${dateStr} - ${sizeStr}</div>
119 + <div class="text-sm text-secondary">${dateStr} - ${sizeStr}</div>
120 120 </div>
121 121 <div class="backup-item-actions">
122 122 <button class="btn btn-sm btn-secondary" data-act="export.restoreFromBackup" data-a1="${escAttr(backup.filePath)}">Restore</button>
@@ -32,7 +32,7 @@
32 32 <div class="focus-overlay-content">
33 33 <div class="focus-header">
34 34 <span class="focus-label">Focus Mode</span>
35 - <div class="focus-presets">
35 + <div class="row row-flex-peer">
36 36 <button class="btn btn-sm focus-preset-btn" data-preset="standard">25/5</button>
37 37 <button class="btn btn-sm focus-preset-btn" data-preset="deep">50/10</button>
38 38 </div>
@@ -18,12 +18,12 @@
18 18 <button class="import-type-card" data-act="importExternal.startContactImport">
19 19 <span class="import-type-icon">&#128101;</span>
20 20 <span class="import-type-label">Import Contacts</span>
21 - <span class="import-type-desc">Import from a vCard (.vcf) file</span>
21 + <span class="text-sm text-muted">Import from a vCard (.vcf) file</span>
22 22 </button>
23 23 <button class="import-type-card" data-act="importExternal.startCalendarImport">
24 24 <span class="import-type-icon">&#128197;</span>
25 25 <span class="import-type-label">Import Calendar</span>
26 - <span class="import-type-desc">Import from an iCalendar (.ics) file</span>
26 + <span class="text-sm text-muted">Import from an iCalendar (.ics) file</span>
27 27 </button>
28 28 </div>
29 29 <div class="form-actions">
@@ -31,7 +31,7 @@
31 31 <div class="import-step" id="import-step-file">
32 32 <h3>1. Choose a CSV or TSV file</h3>
33 33 <p class="import-hint">Columns are matched by name (description, due, priority, project, tags for tasks; start/end for events; name/type for projects).</p>
34 - <div class="file-selector">
34 + <div class="row-flex row-flex-peer">
35 35 <button class="btn btn-primary" id="select-file-btn" data-act="import.selectFile">
36 36 Choose File...
37 37 </button>
@@ -159,14 +159,14 @@
159 159
160 160 let html = '<div class="card card--static review-card month-pulse-card">';
161 161 html += '<h3 class="review-card-title">Project Health</h3>';
162 - html += '<div class="month-pulse-list">';
162 + html += '<div class="stack stack-peer">';
163 163
164 164 for (const p of r.projectPulse) {
165 165 const dirClass = p.direction === 'shrinking' ? 'positive' : p.direction === 'growing' ? 'negative' : 'neutral';
166 166 const arrow = p.direction === 'shrinking' ? '&#x2193;' : p.direction === 'growing' ? '&#x2191;' : '&#x2194;';
167 167 html += `<div class="month-pulse-item ${dirClass}">`;
168 168 html += `<span class="pulse-name">${esc(truncate(p.name, 24))}</span>`;
169 - html += `<span class="pulse-stats">+${p.completed} done / +${p.created} new</span>`;
169 + html += `<span class="text-sm text-secondary">+${p.completed} done / +${p.created} new</span>`;
170 170 html += `<span class="pulse-arrow">${arrow}</span>`;
171 171 html += '</div>';
172 172 }
@@ -78,7 +78,7 @@
78 78 <div class="progress-bar" style="width: ${progress}%"></div>
79 79 </div>
80 80 ` : ''}
81 - <div class="dashboard-item-meta">
81 + <div class="text-sm text-secondary">
82 82 <span class="priority-${t.priority.toLowerCase()}" aria-label="Priority ${t.priority}">${t.priority}</span>
83 83 ${t.dueFormatted ? `&bull; ${t.dueFormatted}` : ''}
84 84 </div>
@@ -94,7 +94,7 @@
94 94 <div class="card card--list-item" data-act="events.open" data-a1="${escAttr(e.id)}"
95 95 tabindex="0" role="button" aria-label="Open event ${esc(e.title)}">
96 96 <div class="dashboard-item-title">${esc(e.title)}</div>
97 - <div class="dashboard-item-meta">
97 + <div class="text-sm text-secondary">
98 98 ${e.dateFormatted} &bull; ${e.timeFormatted}
99 99 </div>
100 100 </div>
@@ -109,7 +109,7 @@
109 109 <div class="card card--list-item ${e.is_read ? '' : 'unread'}" data-act="emails.open" data-a1="${escAttr(e.id)}"
110 110 tabindex="0" role="button" aria-label="Open email ${esc(e.subject)}">
111 111 <div class="dashboard-item-title">${esc(e.subject)}</div>
112 - <div class="dashboard-item-meta">
112 + <div class="text-sm text-secondary">
113 113 ${esc(e.from)} &bull; ${e.receivedFormatted}
114 114 </div>
115 115 </div>
@@ -129,7 +129,7 @@
129 129 <div class="card card--list-item" data-act="attachments.openPanel" data-a1="${escAttr(projectId)}" data-args='[null, "@a1"]'
130 130 tabindex="0" role="button" aria-label="View attachment ${esc(a.filename)}">
131 131 <div class="dashboard-item-title">${esc(a.filename)}</div>
132 - <div class="dashboard-item-meta">${esc(a.fileSizeFormatted)}</div>
132 + <div class="text-sm text-secondary">${esc(a.fileSizeFormatted)}</div>
133 133 </div>
134 134 `).join('');
135 135 }