From 8588051141b4c907350ff683263da65d8873fe4d Mon Sep 17 00:00:00 2001 From: Emi Griffith Date: Sun, 19 Jul 2026 18:48:33 -0700 Subject: [PATCH] Remove em-dashes from site copy and tighten the prose (#206) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace em-dashes in user-facing copy across the server-rendered pages, static frontend views, and the strings the app injects at runtime, using colons, commas, parentheses or full stops as the context wants. Data placeholder glyphs (a lone "—" for a missing reading) are left alone, since a hyphen there reads as a minus sign in temperature columns. Also tighten the high-visibility surfaces (home hero and meta, about, privacy, city and records ledes, glossary blurbs) toward a plainer, more direct voice while keeping every factual claim intact. Claude-Session: https://claude.ai/code/session_01XXxmNFy9cZ6Gh8Y9thZn62 --- static/app.js | 10 +++++----- static/calendar.html | 10 +++++----- static/calendar.js | 8 ++++---- static/chart.js | 10 +++++----- static/compare.html | 10 +++++----- static/compare.js | 4 ++-- static/day.html | 6 +++--- static/digest.js | 4 ++-- static/ios-install.js | 2 +- static/legend.html | 14 +++++++------- static/mappicker.js | 4 ++-- static/score.html | 6 +++--- static/score.js | 6 +++--- static/subscriptions.html | 8 ++++---- static/subscriptions.js | 12 ++++++------ 15 files changed, 57 insertions(+), 57 deletions(-) diff --git a/static/app.js b/static/app.js index 9d28fba..b52df92 100644 --- a/static/app.js +++ b/static/app.js @@ -101,10 +101,10 @@ heroLocate?.addEventListener("click", () => { // like the button did nothing. locateMsg( err.code === err.PERMISSION_DENIED - ? "Location permission was declined — pick a spot on the map instead." + ? "Location permission was declined. Pick a spot on the map instead." : err.code === err.TIMEOUT ? "That took too long. Try again, or pick a spot on the map." - : "Your location isn't available right now — pick a spot on the map instead.", + : "Your location isn't available right now. Pick a spot on the map instead.", ); }, { enableHighAccuracy: false, timeout: 10000, maximumAge: 600000 }, @@ -255,7 +255,7 @@ function render(data) { Check historical data -
${data.target_date} vs a typical day — tap a metric for the full breakdown
+
${data.target_date} vs a typical day. Tap a metric for the full breakdown
${cmpCard("High", "tmax", c.tmax, fmtTemp)} ${cmpCard("Low", "tmin", c.tmin, fmtTemp)} @@ -366,7 +366,7 @@ function renderSeries() {
Trend vs normal${hasFc ? " · recent → forecast" : ""}
${tierKeyHtml()} -
Daily, graded${hasFc ? " — recent & 7-day forecast" : ""}
+
Daily, graded${hasFc ? ": recent & 7-day forecast" : ""}
← OlderNewer →
${daysTable([...days].reverse(), target) || '

Nothing to grade.

'} `; @@ -527,7 +527,7 @@ function exportTableSvg(C) { }); const markup = ` - Recent days, graded — ${esc(placeLabel(lastGraded))} + Recent days, graded: ${esc(placeLabel(lastGraded))} ${th(cDate, "DATE")}${th(cPcp, "PRECIP")}${th(cHigh, "HIGH")}${th(cLow, "LOW")} ${body}`; diff --git a/static/calendar.html b/static/calendar.html index a65f9c9..e721a8e 100644 --- a/static/calendar.html +++ b/static/calendar.html @@ -3,8 +3,8 @@ - Thermograph — 2-year calendar - + Thermograph: 2-year calendar + @@ -17,8 +17,8 @@ crawlers need absolute URLs and don't run JS. --> - - + + @@ -108,7 +108,7 @@
-

Search a place — or open this from a graded location on the map — to see how +

Search a place, or open this from a graded location on the map, to see how every day of the last two years graded against that spot's own climate history.

diff --git a/static/calendar.js b/static/calendar.js index 468620e..1b0ce93 100644 --- a/static/calendar.js +++ b/static/calendar.js @@ -255,7 +255,7 @@ const weatherDropdown = () => `
-
Day feel — from the daily high
+
Day feel, from the daily high
${FEEL_WORDS.map(([val, lab]) => ``).join("")}
Sky
@@ -561,8 +561,8 @@ function renderTotals(visible) { const total = buckets.reduce((n, b) => n + b.n, 0); const head = wActive - ? `${days.length.toLocaleString()} of ${visible.length.toLocaleString()} shown days match the weather filter — by ${title}:` - : `${visible.length.toLocaleString()} days shown — by ${title}:`; + ? `${days.length.toLocaleString()} of ${visible.length.toLocaleString()} shown days match the weather filter, by ${title}:` + : `${visible.length.toLocaleString()} days shown, by ${title}:`; // The strip prints each category's share by default, or the raw day count when the // "Show count" checkbox (state: showCount) is ticked. const countToggle = @@ -583,7 +583,7 @@ function renderKey() { const segs = steps.map(([lab, d]) => `
${lab}
`).join(""); - keyEl.innerHTML = `
Days since last rain — dry streak (caps at ${DRY_CAP} days)
+ keyEl.innerHTML = `
Days since last rain: dry streak (caps at ${DRY_CAP} days)
${segs}
${GUIDE_LINK}`; return; } diff --git a/static/chart.js b/static/chart.js index ace1159..d48dfae 100644 --- a/static/chart.js +++ b/static/chart.js @@ -196,8 +196,8 @@ export function tempChart(key, days, n, xFor, C) { legend: `${s.label} (dashed = median)` + `Band shaded by grade tier` + - `Points labeled value · percentile — grade from the band tier (scale below)`, - subtitle: `${s.label} — recent trend vs normal`, + `Points labeled value · percentile; grade from the band tier (scale below)`, + subtitle: `${s.label}: recent trend vs normal`, aria: `Recent ${s.label.toLowerCase()} versus the normal range.`, }); } @@ -220,7 +220,7 @@ export function precipChart(days, n, xFor, C) { `Precipitation (dashed = median)` + `Band shaded by rain-intensity tier` + `Rain days labeled amount · percentile (rain scale below)`, - subtitle: "Precipitation — daily totals vs normal", + subtitle: "Precipitation: daily totals vs normal", aria: "Daily precipitation totals versus the normal range.", }); } @@ -237,9 +237,9 @@ export function dryChart(days, n, xFor, C) { fmtLabel: (v) => `${Math.round(v)}`, labelOn: (v) => v > 0, // label the streak length; rain days (0) just show the dot legend: - `Days since last rain — dry streak` + + `Days since last rain: dry streak` + `Rain that day (streak = 0)`, - subtitle: "Dry streak — days since last measurable rain", + subtitle: "Dry streak: days since last measurable rain", aria: "Days since last measurable rain, as a line.", }); } diff --git a/static/compare.html b/static/compare.html index 5d7eb6f..17e2e2c 100644 --- a/static/compare.html +++ b/static/compare.html @@ -3,7 +3,7 @@ - Thermograph — compare comfort + Thermograph: compare comfort - + @@ -116,7 +116,7 @@
Time of year
- Count only days in these seasons/months — re-ranks instantly. + Count only days in these seasons/months; re-ranks instantly.
@@ -126,7 +126,7 @@
- Editing the range arms Load — tap it to reload. + Editing the range arms Load; tap it to reload.
@@ -142,7 +142,7 @@

Add two or more places to see which one best matches your comfort temperature - across a date range — the share of days that land in your comfort band, how + across a date range: the share of days that land in your comfort band, how often it runs colder or warmer, and by how much.

diff --git a/static/compare.js b/static/compare.js index 99dacac..3e415db 100644 --- a/static/compare.js +++ b/static/compare.js @@ -416,7 +416,7 @@ function rankCard(loc, s, rank) { const win = rank === 1; const parts = BUCKETS.map(([cls, label, key]) => ({ cls, label, w: s[key] })).filter((p) => p.w > 0); const bar = ``; // Key under the bar: a color swatch + name + share for every non-empty bucket, in // the same cold→hot order as the bar, so small spans are still clearly labeled. On @@ -510,7 +510,7 @@ function renderResults() { const span = `${monthLabel(range.start)} – ${monthLabel(range.end)}`; const winName = namePrimary(win.loc.name); const lead = scored.length > 1 - ? `${winName} is the best match for ${fmtTemp(lo)}–${fmtTemp(hi)} — score ${Math.round(win.s.score)}/100` + ? `${winName} is the best match for ${fmtTemp(lo)}–${fmtTemp(hi)}: score ${Math.round(win.s.score)}/100` : `${winName}: match score ${Math.round(win.s.score)}/100 for ${fmtTemp(lo)}–${fmtTemp(hi)}`; head.hidden = false; head.innerHTML = `

${lead}

` + diff --git a/static/day.html b/static/day.html index 4f88eab..71b0c1d 100644 --- a/static/day.html +++ b/static/day.html @@ -3,7 +3,7 @@ - Thermograph — a single day + Thermograph: a single day - + @@ -76,7 +76,7 @@
-

Pick a place and a day — or open this from a day on the map or calendar — to see +

Pick a place and a day, or open this from a day on the map or calendar, to see the value at every tier boundary for that day's typical climate.

diff --git a/static/digest.js b/static/digest.js index c408d65..a34b060 100644 --- a/static/digest.js +++ b/static/digest.js @@ -48,7 +48,7 @@ for (const form of document.querySelectorAll("form[data-digest]")) { }); const data = await res.json().catch(() => ({})); if (status) { - status.textContent = data.message || "Something went wrong — try again."; + status.textContent = data.message || "Something went wrong. Try again."; status.hidden = false; } if (res.ok) { @@ -60,7 +60,7 @@ for (const form of document.querySelectorAll("form[data-digest]")) { } } catch { if (status) { - status.textContent = "Couldn't reach the server — try again."; + status.textContent = "Couldn't reach the server. Try again."; status.hidden = false; } } finally { diff --git a/static/ios-install.js b/static/ios-install.js index 9ce5f72..183fa2d 100644 --- a/static/ios-install.js +++ b/static/ios-install.js @@ -49,7 +49,7 @@ function build() { '' + '' + '
Add Thermograph to your Home Screen' + - 'Tap Share, then Add to Home Screen — it’s how to get weather alerts on iPhone.
' + + 'Tap Share, then Add to Home Screen. It’s how to get weather alerts on iPhone.
' + ''; el.querySelector(".ios-a2hs-x").addEventListener("click", () => { remember(); diff --git a/static/legend.html b/static/legend.html index 5216ed4..cd33c7e 100644 --- a/static/legend.html +++ b/static/legend.html @@ -3,7 +3,7 @@ - Thermograph — reading the grades + Thermograph: reading the grades - + @@ -66,17 +66,17 @@ decades of local records, then reports the percentile where the day falls in it.

So a 60° day can be “Above Normal” in one place or season and “Below Normal” in another. That's why the categories read - “Above/Below Normal”, “High/Low”, and “Near Record” — + “Above/Below Normal”, “High/Low”, and “Near Record”, never “hot” or “cold”.

-

Grade scale — percentile of the local ±7-day history

+

Grade scale: percentile of the local ±7-day history

-

Rain intensity — percentile among rain days

+

Rain intensity: percentile among rain days

Precipitation is graded only across days that actually saw rain, so “Heavy” means heavy for a rainy day here. Dry days are colored by their dry streak instead (below).

@@ -87,14 +87,14 @@

The metrics

High / Low
The day's highest and lowest air temperature (°F).
-
Feels
Apparent temperature — what the air actually felt like once humidity +
Feels
Apparent temperature: what the air actually felt like once humidity and wind are factored in, taking whichever apparent extreme (heat-index high or wind-chill low) sits further from a temperate baseline, graded against its own history.
Humid
Absolute humidity: grams of water vapor per cubic meter of air (g/m³).
Wind
Average sustained wind speed (mph).
Gust
Peak wind gust for the day (mph).
Precip
Total precipitation (inches), graded by intensity among rain days.
-
Dry streak
Consecutive days since the last measurable rain — the color deepens +
Dry streak
Consecutive days since the last measurable rain; the color deepens the longer it's been dry, and resets to blue on a rain day.
diff --git a/static/mappicker.js b/static/mappicker.js index 10ffc4e..d1de4b1 100644 --- a/static/mappicker.js +++ b/static/mappicker.js @@ -121,7 +121,7 @@ function renderSug(list) { if (!shown.length) { sugEl.hidden = true; return; } shown.forEach((r) => { const li = document.createElement("li"); - li.innerHTML = `${r.name} — ${[r.admin1, r.country].filter(Boolean).join(", ")}`; + li.innerHTML = `${r.name}, ${[r.admin1, r.country].filter(Boolean).join(", ")}`; // A picked search result is an unambiguous choice — drop the pin, recenter, // and select it right away (the map is there for manual/fine picks). li.onclick = () => { @@ -146,7 +146,7 @@ function setPin(lat, lon, zoom) { else marker = L.marker([lat, lon], pinIcon ? { icon: pinIcon } : undefined).addTo(map); map.setView([lat, lon], zoom || map.getZoom()); confirmBtn.disabled = false; - hintEl.textContent = "Pin set — confirm below, or tap elsewhere to move it."; + hintEl.textContent = "Pin set. Confirm below, or tap elsewhere to move it."; } function finish(p) { diff --git a/static/score.html b/static/score.html index c75880d..69af6e5 100644 --- a/static/score.html +++ b/static/score.html @@ -3,7 +3,7 @@ - Thermograph — climate score + Thermograph: climate score - + @@ -63,7 +63,7 @@
-

Pick a place — or open this from the map or calendar — to see how much its +

Pick a place, or open this from the map or calendar, to see how much its recent climate has drifted from its long-term normal, metric by metric.

diff --git a/static/score.js b/static/score.js index 8ba4d6d..1a7fff6 100644 --- a/static/score.js +++ b/static/score.js @@ -158,7 +158,7 @@ function wetbulbNote(m, baseYrs) { const delta = d ? ` (${d >= 0 ? "+" : ""}${d} pts)` : ""; return `

${def} Heat-stress days (peak wet bulb ≥ ${esc(String(thr))}): ${f6}% of recent days - vs ${f45}% across ${baseYrs}${delta} — the rest are normal.

`; + vs ${f45}% across ${baseYrs}${delta}. The rest are normal.

`; } // One metric score card. The tier and its direction stack under the score on @@ -208,9 +208,9 @@ function sentencesHTML(annual) { .map((m) => { const mid = m.per_q && m.per_q.find((q) => q.q === 50); const where = mid ? ` At the median, recent readings sit about ${Math.abs(mid.d)} points ${mid.d >= 0 ? "higher" : "lower"} than the seasonal norm.` : ""; - return `
  • ${esc(m.label)}: ${esc(m.grade)} — about ${Math.abs(m.bias).toFixed(0)} percentile points ${esc(m.direction)} on average.${where}
  • `; + return `
  • ${esc(m.label)}: ${esc(m.grade)}, about ${Math.abs(m.bias).toFixed(0)} percentile points ${esc(m.direction)} on average.${where}
  • `; }); - if (!lines.length) return `

    Every metric is close to its long-term normal here — no notable recent drift.

    `; + if (!lines.length) return `

    Every metric is close to its long-term normal here, with no notable recent drift.

    `; return `

    What has shifted the most:

    `; } diff --git a/static/subscriptions.html b/static/subscriptions.html index 49da550..fa88637 100644 --- a/static/subscriptions.html +++ b/static/subscriptions.html @@ -3,8 +3,8 @@ - Thermograph — weather alerts - + Thermograph: weather alerts + @@ -14,8 +14,8 @@ - - + + diff --git a/static/subscriptions.js b/static/subscriptions.js index 2b2c2b7..199772c 100644 --- a/static/subscriptions.js +++ b/static/subscriptions.js @@ -45,7 +45,7 @@ async function readErr(res) { async function load() { let res; try { res = await apiFetch("api/v2/subscriptions"); } - catch (e) { body.innerHTML = `

    Offline — can't load alerts.

    `; return; } + catch (e) { body.innerHTML = `

    Offline. Can't load alerts.

    `; return; } if (res.status === 401) { renderGate(); return; } if (!res.ok) { body.innerHTML = `

    ${esc(await readErr(res))}

    `; return; } subs = await res.json(); @@ -57,14 +57,14 @@ function renderGate() {

    What are weather alerts?

    Thermograph can watch any city and tell you when its weather turns - unusually extreme for that place — a heat spell, a cold snap, heavy rain — graded + unusually extreme for that place (a heat spell, a cold snap, heavy rain), graded against ~45 years of local climate history.

      -
    1. Pick a city and the metrics to watch — high, low, feels-like, rain, wind…
    2. +
    3. Pick a city and the metrics to watch: high, low, feels-like, rain, wind…
    4. Set a threshold. Alert when a day passes, say, the 97th percentile for that location. Higher = rarer, so fewer but more significant alerts.
    5. -
    6. Get notified in-app (and via OS push on this device) when a recorded day — or - the upcoming forecast — crosses your threshold. At most one notification per week per alert.
    7. +
    8. Get notified in-app (and via OS push on this device) when a recorded day, or + the upcoming forecast, crosses your threshold. At most one notification per week per alert.

    For example: “Tell me when Tokyo's high passes the 97th percentile.”

    @@ -109,7 +109,7 @@ async function renderPushBar() { } if (pushClient.permission() === "denied") { el.hidden = false; - el.innerHTML = `

    Notifications are blocked for this site — re-enable + el.innerHTML = `

    Notifications are blocked for this site. Re-enable them in your browser settings to get OS alerts here.

    `; return; }