diff --git a/BUGS.md b/BUGS.md
index 2d5a7dd..6e173b1 100644
--- a/BUGS.md
+++ b/BUGS.md
@@ -520,3 +520,27 @@ table is re-read under it; terminate, on a throwaway `sleep`, asks by name and p
and the footer stops offering a process that is gone. Sampler values outside 100 ms–60 s are
refused, and a rate slower than the collector's 10 s samples at 10 s. All 14 pages at his
window size and on a phone, in both browsers: no errors, no overflow.
+
+
+# Bug bash 4, later — 2026-09-21: the SMART table outside its card
+
+Mitchell, from the Disks page of `hamtv` with SMART open: the attribute table's rows ran out
+through the right-hand side of the card they belong to, the rules and the Thresh column
+drawn over the page behind.
+
+12. ✅ **A table cannot be narrower than its widest unbreakable cell, and one of `hamtv`'s
+ was 130px wide.** `.num` sets `white-space: nowrap`, and a SMART raw value is not always a
+ number — the temperature attribute reads `34 (Min/Max 28/39)`. That one cell set the
+ table's minimum width above the card's, so it spilled. Three changes, in this order,
+ because the first two each looked worse than the bug in their own way:
+ - the raw column wraps — but `overflow-wrap: anywhere` broke `4295163906` across two
+ lines as "429516 / 3906", which reads as two numbers. It wraps at spaces only now;
+ - a disk card is given room for six columns of SMART (`minmax(420px, 1fr)` rather than
+ the shared 320px), and the cells' side padding goes 6px → 4px, which together is the
+ ~40px it was short by;
+ - if some drive ever reports a raw value long enough to still not fit, the table scrolls
+ inside its card rather than out of it.
+ Verified on all four machines at six widths from 1721px down to a phone, in Chromium and
+ Firefox: nothing leaves its card, no number is broken across lines, and the fallback
+ scrollbar is never reached. The same measurement was run over every other page's cards
+ and boxes — Dashboard, Top, Docker, Alerts, Machines and the rest — and found nothing.
diff --git a/web/src/Disks.tsx b/web/src/Disks.tsx
index 00229b2..35cc163 100644
--- a/web/src/Disks.tsx
+++ b/web/src/Disks.tsx
@@ -58,17 +58,19 @@ function SmartPanel({ dev, rotational }: { dev: string; rotational: boolean }) {
)}
{s.attrs.length > 0 && (
+
| ID | Attribute | Raw | Value | Worst | Thresh |
{s.attrs.map((a) => (
| {a.id} | {a.name.replace(/_/g, ' ')}{a.failed && {a.failed}} |
- {a.raw} | {a.value} | {a.worst} | {a.thresh} |
+ {a.raw} | {a.value} | {a.worst} | {a.thresh} |
))}
+
)}
{!s.self_test_supported && Self-tests: {s.self_test_note || 'not available on this drive'}.
}
{s.self_test_supported &&
@@ -231,7 +233,7 @@ export default function Disks() {
{!hasSmart && SMART health needs smartmontools on this host. Install it with the button, then reload.
}
{task && { toast(`${t.title}: ${t.exit_code === 0 ? 'finished' : `exited ${t.exit_code}`}`, t.exit_code === 0 ? 'ok' : 'err'); load() }} />}
-
+
{devs.map((d) => )}
diff --git a/web/src/styles.css b/web/src/styles.css
index 627979d..7c82d2e 100644
--- a/web/src/styles.css
+++ b/web/src/styles.css
@@ -262,9 +262,19 @@ h2.section { margin: 0; font-size: 13px; font-weight: 600; color: var(--muted);
.good { color: var(--ok); } .warn-text { color: var(--s2); }
.smart { display: flex; flex-direction: column; gap: 8px; padding-top: 8px; border-top: 1px solid var(--border); }
.attrs { width: 100%; border-collapse: collapse; font-size: 12px; }
-.attrs th { text-align: left; color: var(--muted); font-weight: 500; padding: 4px 6px; border-bottom: 1px solid var(--border); }
-.attrs td { padding: 3px 6px; border-bottom: 1px solid var(--border); } .attrs td.num, .attrs th:not(:nth-child(2)) { text-align: right; }
+.attrs th { text-align: left; color: var(--muted); font-weight: 500; padding: 4px 4px; border-bottom: 1px solid var(--border); }
+.attrs td { padding: 3px 4px; border-bottom: 1px solid var(--border); } .attrs td.num, .attrs th:not(:nth-child(2)) { text-align: right; }
.attrs tr.warn td { color: var(--s2); }
+/* A SMART raw value is not always a number — a temperature reads "36 (Min/Max 28/39)" — and a
+ table cannot be narrower than its widest unbreakable cell, so one of those pushed the whole
+ table out through the side of its card. The raw column wraps, but only where there is a
+ space: a number broken across two lines reads as two numbers. If some drive ever reports
+ one long enough to still not fit, the table scrolls inside its card rather than out of it. */
+.attrs td.raw { white-space: normal; }
+.attrs-wrap { overflow-x: auto; scrollbar-width: thin; }
+/* Six columns of SMART want about 350px between them; a disk card is given room for that, so
+ the fallback above stays a fallback. */
+.disk-cards { grid-template-columns: repeat(auto-fill, minmax(min(420px, 100%), 1fr)); }
.datasets { padding: 0; overflow-x: auto; }
.ds-row { display: grid; grid-template-columns: minmax(160px, 1fr) 80px 80px 80px 50px 50px minmax(120px, 1fr); gap: 8px; align-items: center; padding: 6px 12px; border-bottom: 1px solid var(--border); font-size: 13px; min-width: 700px; } /* the columns and gaps add up to 692: at 640 the row ended before its own cells did */
.ds-row.head { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .5px; }