diff --git a/public/css/style.css b/public/css/style.css
index d9630ab..fff8934 100644
--- a/public/css/style.css
+++ b/public/css/style.css
@@ -4465,7 +4465,16 @@ textarea::selection {
}
.paper-table .cell-anon .anon-sub a { color: var(--ink-muted); border-bottom: 1px dotted var(--border-color); }
.paper-table .cell-anon .anon-sub a:hover { color: var(--color); }
-.paper-table .cell-conf { font-family: var(--font-mono); font-size: 13px; color: var(--color); }
+.paper-table .cell-conf {
+ font-family: var(--font-mono);
+ font-size: 13px;
+ color: var(--color);
+ min-width: 0;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+.paper-table .cell-conf span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.paper-table .cell-status { display: flex; flex-wrap: wrap; align-items: center; gap: 2px 8px; font-size: 14px; color: var(--color); }
.paper-table .cell-status .status-line { display: inline-flex; align-items: center; gap: 8px; }
.paper-table .cell-status .status-sub { flex-basis: 100%; font-size: 11px; line-height: 1.2; color: var(--ink-muted); }
@@ -5295,6 +5304,12 @@ textarea::selection {
gap: 6px;
text-align: left;
}
+ .paper-table .cell-conf {
+ max-width: 100%;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
.paper-table .cell-views.num { text-align: left; }
.paper-table .cell-views::before {
content: '\f06e';
diff --git a/public/partials/dashboard.htm b/public/partials/dashboard.htm
index 8b3eb3f..00250dc 100644
--- a/public/partials/dashboard.htm
+++ b/public/partials/dashboard.htm
@@ -195,7 +195,7 @@
-
+
—
diff --git a/test/dashboard-conference-overflow.test.js b/test/dashboard-conference-overflow.test.js
new file mode 100644
index 0000000..e3530cb
--- /dev/null
+++ b/test/dashboard-conference-overflow.test.js
@@ -0,0 +1,49 @@
+const { expect } = require("chai");
+const fs = require("fs");
+const path = require("path");
+
+/**
+ * Regression test for the dashboard "Conference" column overflowing into
+ * the "Status" column.
+ *
+ * Root cause: `.cell-conf` is a CSS grid item with the default
+ * `min-width: auto`, so an unbreakable long string (e.g. a conference URL)
+ * forces the grid track wider than its `minmax(140px, 1fr)` track size,
+ * visually bleeding into the next cell instead of being clipped.
+ */
+
+const cssPath = path.join(__dirname, "..", "public", "css", "style.css");
+const htmlPath = path.join(__dirname, "..", "public", "partials", "dashboard.htm");
+
+function getRuleBody(css, selector) {
+ const index = css.indexOf(selector);
+ expect(index, `selector "${selector}" not found in style.css`).to.be.greaterThan(-1);
+ const start = css.indexOf("{", index);
+ const end = css.indexOf("}", start);
+ return css.slice(start + 1, end);
+}
+
+describe("dashboard .cell-conf overflow fix", function () {
+ const css = fs.readFileSync(cssPath, "utf8");
+
+ it("clips overflowing conference text instead of letting it bleed into other cells", function () {
+ const rule = getRuleBody(css, ".paper-table .cell-conf {");
+ expect(rule).to.match(/min-width:\s*0/);
+ expect(rule).to.match(/overflow:\s*hidden/);
+ expect(rule).to.match(/text-overflow:\s*ellipsis/);
+ expect(rule).to.match(/white-space:\s*nowrap/);
+ });
+
+ it("keeps the fix in the stacked mobile layout too", function () {
+ const mobileSectionStart = css.indexOf("@media (max-width: 900px)");
+ const mobileRule = getRuleBody(css.slice(mobileSectionStart), ".paper-table .cell-conf {");
+ expect(mobileRule).to.match(/overflow:\s*hidden/);
+ expect(mobileRule).to.match(/text-overflow:\s*ellipsis/);
+ expect(mobileRule).to.match(/white-space:\s*nowrap/);
+ });
+
+ it("exposes the full conference value via a title attribute for truncated text", function () {
+ const html = fs.readFileSync(htmlPath, "utf8");
+ expect(html).to.match(/cell-conf[\s\S]*?title="\{\{item\.conference\}\}"/);
+ });
+});