diff --git a/.forge/prs/39589eba-2219-4c35-9b17-0527275b24be/patch.diff b/.forge/prs/39589eba-2219-4c35-9b17-0527275b24be/patch.diffnew file mode 100644index 0000000..c034b50--- /dev/null+++ b/.forge/prs/39589eba-2219-4c35-9b17-0527275b24be/patch.diff@@ -0,0 +1,490 @@1+From dacca5a820d372be31d9058ab209055921e63e70 Mon Sep 17 00:00:00 20012+From: gl0w <gl0w@world>3+Date: Thu, 10 Sep 2026 08:39:46 +01004+Subject: [PATCH] m6/no-scroll: stacked cards on mobile, tables on desktop5+6+---7+ src/build.rs | 39 +++++++++++++---8+ src/commits.rs | 34 ++++++++++++--9+ src/forge.rs | 100 +++++++++++++++++++++++++++++++++++++++--10+ src/releases.rs | 46 ++++++++++++++++++-11+ static/style.css | 21 +++++++++12+ tests/forge_readers.rs | 10 ++---13+ tests/releases_api.rs | 10 ++---14+ tests/responsive.rs | 8 ++--15+ 8 files changed, 238 insertions(+), 30 deletions(-)16+17+diff --git a/src/build.rs b/src/build.rs18+index d70dc3f..b0b69ca 10064419+--- a/src/build.rs20++++ b/src/build.rs21+@@ -584,8 +584,8 @@ pub fn file_table_html(22+ );23+ if let Some((href, label)) = parent {24+ out.push_str(&format!(25+- "<tr class=\"parent\"><td><a href=\"{}\">{}</a></td>\26+- <td></td><td></td></tr>\n",27++ "<tr class=\"parent\"><td data-label=\"Name\"><a href=\"{}\">{}</a></td>\28++ <td data-label=\"Last commit\"></td><td data-label=\"Age\"></td></tr>\n",29+ esc(href),30+ esc(label)31+ ));32+@@ -600,8 +600,8 @@ pub fn file_table_html(33+ let msg_short = truncate_msg(&msg);34+ if e.is_dir {35+ out.push_str(&format!(36+- "<tr class=\"dir\"><td><a href=\"/files/{}.html\">{}/</a></td>\37+- <td class=\"title\">{}</td><td>{}</td></tr>\n",38++ "<tr class=\"dir\"><td data-label=\"Name\"><a href=\"/files/{}.html\">{}/</a></td>\39++ <td class=\"title\" data-label=\"Last commit\">{}</td><td data-label=\"Age\">{}</td></tr>\n",40+ href_esc(&full),41+ esc(&e.name),42+ esc(&msg_short),43+@@ -609,8 +609,8 @@ pub fn file_table_html(44+ ));45+ } else {46+ out.push_str(&format!(47+- "<tr class=\"file\"><td><a href=\"/files/{}.html\">{}</a></td>\48+- <td class=\"title\">{}</td><td>{}</td></tr>\n",49++ "<tr class=\"file\"><td data-label=\"Name\"><a href=\"/files/{}.html\">{}</a></td>\50++ <td class=\"title\" data-label=\"Last commit\">{}</td><td data-label=\"Age\">{}</td></tr>\n",51+ href_esc(&full),52+ esc(&e.name),53+ esc(&msg_short),54+@@ -1651,7 +1651,32 @@ mod tests {55+ "breadcrumb kept on subdir"56+ );57+ // Message + age columns exist even when history is empty-ish.58+- assert!(sub.contains("<td class=\"title\">"), "message cell");59++ assert!(sub.contains("<td class=\"title\""), "message cell");60++ }61++62++ /// m6/no-scroll (f0rge #39): file-table rows carry data-labels for cards.63++ #[test]64++ fn file_table_cells_have_data_labels() {65++ let css = include_str!("../static/style.css");66++ assert!(css.contains("content:attr(data-label)"), "label rule");67++ assert!(css.contains("@media (max-width:767px)"), "narrow query");68++ let (_repo, out) = build_fixture();69++ for page in ["index.html", "files/sub.html"] {70++ let html = std::fs::read_to_string(out.join(page)).expect(page);71++ let tds = html.matches("<td").count();72++ assert!(tds > 0, "{page} has cells");73++ assert_eq!(74++ html.matches("data-label=").count(),75++ tds,76++ "{page}: every td labeled"77++ );78++ for label in ["Name", "Last commit", "Age"] {79++ assert!(80++ html.contains(&format!("data-label=\"{label}\"")),81++ "{page}: label {label}"82++ );83++ }84++ }85+ }86+ 87+ #[test]88+diff --git a/src/commits.rs b/src/commits.rs89+index d5fa33e..7075113 10064490+--- a/src/commits.rs91++++ b/src/commits.rs92+@@ -40,7 +40,7 @@ pub fn render_loaded(93+ body.push_str("<div class=\"table-scroll\"><table>\n<thead><tr><th>sha</th><th>subject</th><th>author</th><th>date</th></tr></thead>\n<tbody>\n");94+ for c in log {95+ body.push_str(&format!(96+- "<tr><td class=\"sha\"><a href=\"/commit/{}.html\"><code>{}</code></a></td><td class=\"title\">{}</td><td>{}</td><td>{}</td></tr>\n",97++ "<tr><td class=\"sha\" data-label=\"sha\"><a href=\"/commit/{}.html\"><code>{}</code></a></td><td class=\"title\" data-label=\"subject\">{}</td><td data-label=\"author\">{}</td><td data-label=\"date\">{}</td></tr>\n",98+ esc(&c.sha),99+ esc(short_sha(&c.sha)),100+ esc(&c.subject),101+@@ -342,8 +342,8 @@ mod tests {102+ list.contains("<div class=\"table-scroll\"><table>"),103+ "wrapper"104+ );105+- assert!(list.contains("<td class=\"title\">"), "title cell");106+- assert!(list.contains("<td class=\"sha\">"), "sha cell");107++ assert!(list.contains("<td class=\"title\""), "title cell");108++ assert!(list.contains("<td class=\"sha\""), "sha cell");109+ assert!(list.contains(&long_subject), "long subject kept");110+ let sha = shas(&dir)[0].clone();111+ assert!(112+@@ -368,4 +368,32 @@ mod tests {113+ let list = std::fs::read_to_string(out.join("commits.html")).expect("list");114+ assert!(list.contains("No commits yet."));115+ }116++117++ /// m6/no-scroll (f0rge #39): commit rows carry data-labels for cards.118++ #[test]119++ fn commit_cells_have_data_labels() {120++ let css = include_str!("../static/style.css");121++ assert!(css.contains("content:attr(data-label)"), "label rule");122++ assert!(css.contains("@media (max-width:767px)"), "narrow query");123++ let dir = mktemp("labels");124++ git(&dir, &["init", "-b", "main"]);125++ git(&dir, &["config", "user.name", "hu13-test"]);126++ git(&dir, &["config", "user.email", "[email protected]"]);127++ git(&dir, &["config", "commit.gpgsign", "false"]);128++ std::fs::write(dir.join("x.txt"), "x\n").unwrap();129++ git(&dir, &["add", "-A"]);130++ git(&dir, &["commit", "-m", "labeled subject"]);131++ let out = mktemp("out");132++ render(&dir, &out, "main", 100, "test", "<nav>", None).expect("render");133++ let list = std::fs::read_to_string(out.join("commits.html")).expect("list");134++ let tds = list.matches("<td").count();135++ assert!(tds > 0, "has cells");136++ assert_eq!(list.matches("data-label=").count(), tds, "every td labeled");137++ for label in ["sha", "subject", "author", "date"] {138++ assert!(139++ list.contains(&format!("data-label=\"{label}\"")),140++ "label {label}"141++ );142++ }143++ }144+ }145+diff --git a/src/forge.rs b/src/forge.rs146+index fe2c0e4..b41ed7a 100644147+--- a/src/forge.rs148++++ b/src/forge.rs149+@@ -986,7 +986,7 @@ fn render_issues_index(150+ body.push_str("<div class=\"table-scroll\"><table>\n<thead><tr><th>#</th><th>title</th><th>state</th><th>comments</th><th>created</th></tr></thead>\n<tbody>\n");151+ for issue in &data.issues {152+ body.push_str(&format!(153+- "<tr><td class=\"num\">{n}</td><td class=\"title\"><a href=\"/issues/{id}.html\">{title}</a></td><td>{state}</td><td>{comments}</td><td>{created}</td></tr>\n",154++ "<tr><td class=\"num\" data-label=\"#\">{n}</td><td class=\"title\" data-label=\"title\"><a href=\"/issues/{id}.html\">{title}</a></td><td data-label=\"state\">{state}</td><td data-label=\"comments\">{comments}</td><td data-label=\"created\">{created}</td></tr>\n",155+ n = issue.number,156+ id = esc(&issue.id),157+ title = esc(&issue.title),158+@@ -1045,7 +1045,7 @@ fn render_pulls_index(159+ let lanes = format!("{}→{}", esc(&pr.head), esc(&pr.base));160+ let draft = if pr.draft { " draft" } else { "" };161+ body.push_str(&format!(162+- "<tr><td class=\"num\">{n}</td><td class=\"title\"><a href=\"/pulls/{id}.html\">{title}</a>{draft}</td><td>{state}</td><td><code>{lanes}</code></td><td>{created}</td></tr>\n",163++ "<tr><td class=\"num\" data-label=\"#\">{n}</td><td class=\"title\" data-label=\"title\"><a href=\"/pulls/{id}.html\">{title}</a>{draft}</td><td data-label=\"state\">{state}</td><td data-label=\"head→base\"><code>{lanes}</code></td><td data-label=\"created\">{created}</td></tr>\n",164+ n = pr.number,165+ id = esc(&pr.id),166+ title = esc(&pr.title),167+@@ -1143,7 +1143,7 @@ fn render_ci_index(ci: &CiInfo, site_name: &str, nav: &str, description: Option<168+ body.push_str("<div class=\"table-scroll\"><table>\n<thead><tr><th>step</th><th>exit</th><th>duration_ms</th></tr></thead>\n<tbody>\n");169+ for s in &run.steps {170+ body.push_str(&format!(171+- "<tr><td><code>{}</code></td><td>{}</td><td>{}</td></tr>\n",172++ "<tr><td data-label=\"step\"><code>{}</code></td><td data-label=\"exit\">{}</td><td data-label=\"duration_ms\">{}</td></tr>\n",173+ esc(&s.name),174+ s.exit,175+ s.duration_ms,176+@@ -1690,7 +1690,7 @@ mod tests {177+ html.contains("<div class=\"table-scroll\"><table>"),178+ "{page}: wrapper"179+ );180+- assert!(html.contains("<td class=\"title\">"), "{page}: title cell");181++ assert!(html.contains("<td class=\"title\""), "{page}: title cell");182+ assert!(html.contains(&long_title), "{page}: long title kept");183+ }184+ let pulls = std::fs::read_to_string(out.join("pulls/index.html")).expect("pulls");185+@@ -1750,4 +1750,96 @@ mod tests {186+ assert!(pulls.contains("1 closed"), "pulls closed total");187+ assert!(pulls.contains(" draft"), "draft stays marked");188+ }189++190++ /// m6/no-scroll (f0rge #39): stacked-card reflow on narrow, desktop untouched.191++ /// Research: from knowledge of Gitea mobile (issue/PR lists collapse to192++ /// title-led cards; file table stacks name + meta) — no live fetch here.193++ #[test]194++ fn stacked_cards_no_scroll() {195++ let css = include_str!("../static/style.css");196++ assert!(css.contains("@media (max-width:767px)"), "narrow query");197++ assert!(css.contains("overflow-x:visible"), "no scroll on narrow");198++ assert!(199++ css.contains("content:attr(data-label)"),200++ "label via ::before"201++ );202++ assert!(css.contains(".table-scroll td::before"), "label rule");203++ assert!(204++ css.contains(".table-scroll td.title::before"),205++ "title leads card"206++ );207++ assert!(208++ css.contains("display:block") && css.contains("display:flex"),209++ "card layout"210++ );211++ assert!(css.contains("width:max-content"), "desktop width kept");212++ assert!(213++ css.contains(".table-scroll th,.table-scroll td") && css.contains("white-space:nowrap"),214++ "desktop nowrap kept"215++ );216++ let repo = mktemp("cards");217++ write_forge(218++ &repo,219++ "issues/aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa/issue.md",220++ "---\nid: aaaaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa\ntitle: Card issue\nstate: open\ncreated: 2026-09-10T03:00:00Z\n---\nbody\n",221++ );222++ write_forge(223++ &repo,224++ "prs/bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb/pr.md",225++ "---\nid: bbbbbbbb-bbbb-bbbb-bbbb-bbbbbbbbbbbb\ntitle: Card pr\nstate: open\nbase: main\nhead: feat\ncreated: 2026-09-10T03:00:00Z\n---\ndesc\n",226++ );227++ write_forge(228++ &repo,229++ "tasks/cccccccc-cccc-cccc-cccc-cccccccccccc.md",230++ "---\nid: cccccccc-cccc-cccc-cccc-cccccccccccc\ntitle: Card task\nstatus: todo\ncreated: 2026-09-10T03:00:00Z\n---\nwork\n",231++ );232++ write_forge(&repo, "ci/ci.env", "runner: local\n");233++ std::fs::create_dir_all(repo.join(".forge").join("ci/steps")).unwrap();234++ std::fs::write(235++ repo.join(".forge").join("ci/steps/00_test.sh"),236++ "#!/bin/sh\n",237++ )238++ .unwrap();239++ write_forge(240++ &repo,241++ "ci/runs/2026-09-10T00-00-00Z/summary.md",242++ "---\nrun: 2026-09-10T00-00-00Z\nverdict: pass\ncreated: 2026-09-10T00:00:00Z\nsteps:\n - name: 00_test.sh\n exit: 0\n duration_ms: 7\n---\nok\n",243++ );244++ let out = mktemp("cards-out");245++ render_loaded(&load(&repo), &out, "test", "<nav>", None).expect("render");246++ for (page, labels) in [247++ (248++ "issues/index.html",249++ vec!["#", "title", "state", "comments", "created"],250++ ),251++ (252++ "pulls/index.html",253++ vec!["#", "title", "state", "head→base", "created"],254++ ),255++ ("ci/index.html", vec!["step", "exit", "duration_ms"]),256++ ] {257++ let html = std::fs::read_to_string(out.join(page)).expect(page);258++ let tds = html.matches("<td").count();259++ assert!(tds > 0, "{page} has cells");260++ assert_eq!(261++ html.matches("data-label=").count(),262++ tds,263++ "{page}: every td labeled"264++ );265++ for label in labels {266++ assert!(267++ html.contains(&format!("data-label=\"{label}\"")),268++ "{page}: label {label}"269++ );270++ }271++ }272++ let tasks = std::fs::read_to_string(out.join("tasks/index.html")).expect("tasks");273++ assert!(!tasks.contains("table-scroll"), "tasks: no scroll wrapper");274++ assert!(tasks.contains("todo (1)"), "tasks group kept");275++ let issues = std::fs::read_to_string(out.join("issues/index.html")).expect("issues");276++ assert!(277++ issues.contains("open") && issues.contains("2026-09-10"),278++ "issues data kept in DOM"279++ );280++ }281+ }282+diff --git a/src/releases.rs b/src/releases.rs283+index ad238d9..87493bb 100644284+--- a/src/releases.rs285++++ b/src/releases.rs286+@@ -391,7 +391,7 @@ pub fn render_loaded(287+ body.push_str("<div class=\"table-scroll\"><table>\n<thead><tr><th>version</th><th>title</th><th>created</th><th>artifacts</th></tr></thead>\n<tbody>\n");288+ for rel in releases {289+ body.push_str(&format!(290+- "<tr><td><a href=\"/releases/{v}.html\">{ve}</a></td><td class=\"title\">{t}</td><td>{c}</td><td>{n}</td></tr>\n",291++ "<tr><td data-label=\"version\"><a href=\"/releases/{v}.html\">{ve}</a></td><td class=\"title\" data-label=\"title\">{t}</td><td data-label=\"created\">{c}</td><td data-label=\"artifacts\">{n}</td></tr>\n",292+ v = esc(&rel.version),293+ ve = esc(&rel.version),294+ t = esc(rel.display_title()),295+@@ -493,7 +493,7 @@ mod tests {296+ index.contains("<div class=\"table-scroll\"><table>"),297+ "wrapper"298+ );299+- assert!(index.contains("<td class=\"title\">"), "title cell");300++ assert!(index.contains("<td class=\"title\""), "title cell");301+ assert!(index.contains(&long_title), "long title kept");302+ }303+ 304+@@ -510,4 +510,46 @@ mod tests {305+ std::fs::create_dir_all(&dir).unwrap();306+ assert!(load(&dir).is_empty());307+ }308++309++ /// m6/no-scroll (f0rge #39): release rows carry data-labels for cards.310++ #[test]311++ fn release_cells_have_data_labels() {312++ let css = include_str!("../static/style.css");313++ assert!(css.contains("content:attr(data-label)"), "label rule");314++ let rel = Release {315++ version: "v1.2.3".to_string(),316++ title: Some("Labeled".to_string()),317++ created: "2026-09-10T03:00:00Z".to_string(),318++ sha: None,319++ body: "notes\n".to_string(),320++ artifacts: Vec::new(),321++ };322++ let repo = std::env::temp_dir().join(format!(323++ "hu13-rel-labels-{}-{}",324++ std::process::id(),325++ std::time::SystemTime::now()326++ .duration_since(std::time::UNIX_EPOCH)327++ .map(|d| d.as_nanos())328++ .unwrap_or(0)329++ ));330++ std::fs::create_dir_all(&repo).unwrap();331++ let out = repo.join("out");332++ std::fs::create_dir_all(&out).unwrap();333++ render_loaded(&[rel], &repo, &out, "test", "<nav>", None).expect("render");334++ let index =335++ std::fs::read_to_string(out.join("releases").join("index.html")).expect("index");336++ let tds = index.matches("<td").count();337++ assert!(tds > 0, "has cells");338++ assert_eq!(339++ index.matches("data-label=").count(),340++ tds,341++ "every td labeled"342++ );343++ for label in ["version", "title", "created", "artifacts"] {344++ assert!(345++ index.contains(&format!("data-label=\"{label}\"")),346++ "label {label}"347++ );348++ }349++ }350+ }351+diff --git a/static/style.css b/static/style.css352+index a09ecf2..2efee6d 100644353+--- a/static/style.css354++++ b/static/style.css355+@@ -183,3 +183,24 @@ details.clonebox{margin:1rem 0;padding:.25rem 1rem 1rem;background:var(--bg-alt)356+ details.clonebox>summary{cursor:pointer;min-height:40px;font-weight:700;color:var(--cyan)}357+ details.clonebox>summary:focus-visible{outline:2px solid var(--blue);outline-offset:2px}358+ .clone-hint,.clonebox summary .muted{font-weight:400}359++/* Stacked cards on narrow (m6/no-scroll, f0rge #39): Gitea-style reflow.360++ Desktop (>=768px) keeps .table-scroll horizontal scroll untouched above.361++ Narrow: no scroll (overflow visible), thead clipped for SR only, each362++ tr becomes a bordered card, each td a labeled flex row via data-label.363++ Title cells lead bold without a repeated label; nothing is deleted. */364++@media (max-width:767px){365++ .table-scroll{overflow-x:visible}366++ .table-scroll table,.file-table table{width:100%;min-width:0}367++ .table-scroll thead{position:absolute;max-width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap}368++ .table-scroll tbody,.table-scroll tr{display:block;width:auto}369++ .table-scroll tr{border:1px solid var(--border);background:var(--bg-alt);margin:0 0 .75rem;padding:.25rem 0}370++ .table-scroll th,.table-scroll td,.file-table th:nth-child(3),.file-table td:nth-child(3){white-space:normal}371++ .table-scroll td{display:flex;gap:.5rem;align-items:baseline;padding:.4rem .75rem;border:none;border-bottom:1px solid var(--border);min-width:0;overflow-wrap:anywhere}372++ .table-scroll td:last-child{border-bottom:none}373++ .table-scroll td::before{content:attr(data-label);flex:0 0 6rem;color:var(--muted);font-weight:600;font-size:.8rem;overflow-wrap:anywhere}374++ .table-scroll td.title::before{display:none}375++ .table-scroll td.title{font-weight:700;font-size:1.02rem;min-width:0}376++ .table-scroll td.num,.table-scroll td.sha{width:auto}377++ .table-scroll td code,.table-scroll td a{overflow-wrap:anywhere;word-break:break-all;min-width:0}378++ .table-scroll tbody tr:hover td{background:none}379++}380+diff --git a/tests/forge_readers.rs b/tests/forge_readers.rs381+index d695600..b3aa723 100644382+--- a/tests/forge_readers.rs383++++ b/tests/forge_readers.rs384+@@ -224,19 +224,19 @@ fn forge_fixture_renders_counts_numbers_and_ci() {385+ 386+ // --- issues index: 2 rendered rows (broken one skipped) ---387+ let issues = read("issues/index.html");388+- let rows = issues.matches("<tr><td class=\"num\">").count();389++ let rows = issues.matches("<tr><td class=\"num\"").count();390+ assert_eq!(rows, 2, "rendered issues match file-count minus broken");391+ assert!(!issues.contains("No title here"), "broken issue skipped");392+ // 5-number spot check (1): #1 links the older uuid, #2 the newer uuid.393+ assert!(394+ issues.contains(&format!(395+- "<td class=\"num\">1</td><td class=\"title\"><a href=\"/issues/{OLDER_UUID}.html\">"396++ "<td class=\"num\" data-label=\"#\">1</td><td class=\"title\" data-label=\"title\"><a href=\"/issues/{OLDER_UUID}.html\">"397+ )),398+ "issue #1 -> older uuid"399+ );400+ assert!(401+ issues.contains(&format!(402+- "<td class=\"num\">2</td><td class=\"title\"><a href=\"/issues/{NEWER_UUID}.html\">"403++ "<td class=\"num\" data-label=\"#\">2</td><td class=\"title\" data-label=\"title\"><a href=\"/issues/{NEWER_UUID}.html\">"404+ )),405+ "issue #2 -> newer uuid"406+ );407+@@ -263,13 +263,13 @@ fn forge_fixture_renders_counts_numbers_and_ci() {408+ // (4): pulls — same shape + head/base/draft/ci + patch <pre>.409+ let pulls = read("pulls/index.html");410+ assert_eq!(411+- pulls.matches("<tr><td class=\"num\">").count(),412++ pulls.matches("<tr><td class=\"num\"").count(),413+ 1,414+ "prs match file-count"415+ );416+ assert!(417+ pulls.contains(&format!(418+- "<td class=\"num\">1</td><td class=\"title\"><a href=\"/pulls/{PR_UUID}.html\">"419++ "<td class=\"num\" data-label=\"#\">1</td><td class=\"title\" data-label=\"title\"><a href=\"/pulls/{PR_UUID}.html\">"420+ )),421+ "pr #1 -> uuid"422+ );423+diff --git a/tests/releases_api.rs b/tests/releases_api.rs424+index 9ea9a71..ea32b2c 100644425+--- a/tests/releases_api.rs426++++ b/tests/releases_api.rs427+@@ -110,7 +110,7 @@ fn releases_artifacts_api_agents_feed() {428+ 429+ // --- releases: index + both detail pages ---430+ let index = read("releases/index.html");431+- assert_eq!(index.matches("<tr><td>").count(), 2, "two release rows");432++ assert_eq!(index.matches("<tr><td").count(), 2, "two release rows");433+ assert!(index.contains("v1.0"), "v1.0 listed");434+ assert!(index.contains("v2.0"), "v2.0 listed");435+ 436+@@ -154,15 +154,15 @@ fn releases_artifacts_api_agents_feed() {437+ 438+ // --- api counts == html counts (same struct pass) ---439+ let issues_html = read("issues/index.html")440+- .matches("<tr><td class=\"num\">")441++ .matches("<tr><td class=\"num\"")442+ .count();443+ let pulls_html = read("pulls/index.html")444+- .matches("<tr><td class=\"num\">")445++ .matches("<tr><td class=\"num\"")446+ .count();447+ let commits_html = read("commits.html")448+- .matches("<tr><td class=\"sha\">")449++ .matches("<tr><td class=\"sha\"")450+ .count();451+- let releases_html = read("releases/index.html").matches("<tr><td>").count();452++ let releases_html = read("releases/index.html").matches("<tr><td").count();453+ let wiki_html = read("wiki/index.html")454+ .matches("<li><a href=\"/wiki/")455+ .count();456+diff --git a/tests/responsive.rs b/tests/responsive.rs457+index 37b536e..f0c4b2d 100644458+--- a/tests/responsive.rs459++++ b/tests/responsive.rs460+@@ -377,23 +377,23 @@ fn forge_list_tables_scroll_and_wrap_long_titles() {461+ for page in ["issues/index.html", "pulls/index.html"] {462+ let html = read(page);463+ assert!(464+- html.contains("<td class=\"title\">"),465++ html.contains("<td class=\"title\""),466+ "{page} marks title cells"467+ );468+ assert!(469+ html.contains(&long_title),470+ "{page} keeps the full long title"471+ );472+- assert!(html.contains("<td class=\"num\">"), "{page} keeps # narrow");473++ assert!(html.contains("<td class=\"num\""), "{page} keeps # narrow");474+ }475+ let commits = read("commits.html");476+ assert!(477+- commits.contains("<td class=\"sha\">"),478++ commits.contains("<td class=\"sha\""),479+ "commits marks sha cells"480+ );481+ let releases = read("releases/index.html");482+ assert!(483+- releases.contains("<td class=\"title\">"),484++ releases.contains("<td class=\"title\""),485+ "releases marks title cells"486+ );487+ // Labels from the fixture still list on the issues index.488+-- 489+2.55.0490+