thermograph/frontend/static/legend.html
Emi Griffith d6df04eab2 Subtree-merge thermograph-frontend (origin/main) into frontend/
git-subtree-dir: frontend
git-subtree-mainline: a4be7066e5
git-subtree-split: 3a98146da4
2026-07-22 22:01:11 -07:00

120 lines
11 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Thermograph: reading the grades</title>
<meta name="description" content="How Thermograph grades weather: percentiles of a location's own ±7-day seasonal history, the scale from Below Normal to Near Record, and what every metric means." />
<meta name="theme-color" content="#f0803c" />
<!-- iOS/Android home-screen install: run standalone (no browser chrome) when
added to the Home Screen. On iOS 16.4+ this is what unlocks Web Push. -->
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
<meta name="apple-mobile-web-app-title" content="Thermograph" />
<!-- Link previews (Discord, Slack, iMessage…). The server fills the og:url /
og:image origins in from this request's scheme://host + base path —
crawlers need absolute URLs and don't run JS. -->
<meta property="og:type" content="website" />
<meta property="og:site_name" content="Thermograph" />
<meta property="og:title" content="Thermograph: reading the grades" />
<meta property="og:description" content="How Thermograph grades weather: percentiles of a location's own ±7-day seasonal history, the scale from Below Normal to Near Record, and what every metric means." />
<meta property="og:url" content="__ORIGIN__/legend" />
<meta property="og:image" content="__ORIGIN__/logo.png?v=3" />
<meta property="og:image:width" content="512" />
<meta property="og:image:height" content="512" />
<meta property="og:image:alt" content="Thermograph logo" />
<meta name="twitter:card" content="summary" />
<link rel="canonical" href="__ORIGIN__/legend" />
<link rel="icon" href="favicon.svg?v=3" type="image/svg+xml" />
<link rel="icon" href="favicon-48.png?v=3" type="image/png" sizes="48x48" />
<link rel="icon" href="favicon-32.png?v=3" type="image/png" sizes="32x32" />
<link rel="icon" href="favicon-16.png?v=3" type="image/png" sizes="16x16" />
<link rel="apple-touch-icon" href="apple-touch-icon.png?v=3" />
<link rel="manifest" href="manifest.webmanifest" />
<link rel="stylesheet" href="style.css" />
</head>
<body>
<header>
<div class="brand">
<div>
<h1><a href="./"><span class="logo"><svg viewBox="0 0 512 512" width="28" height="28" aria-hidden="true"><rect x="32" y="32" width="448" height="448" rx="96" fill="#10141B" stroke="#FFFFFF" stroke-opacity="0.08" stroke-width="4"/><rect x="115" y="403" width="42" height="42" rx="10" fill="#111924"/><rect x="163" y="403" width="42" height="42" rx="10" fill="#111924"/><rect x="211" y="403" width="42" height="42" rx="10" fill="#111924"/><rect x="259" y="403" width="42" height="42" rx="10" fill="#111924"/><rect x="307" y="403" width="42" height="42" rx="10" fill="#111924"/><rect x="355" y="403" width="42" height="42" rx="10" fill="#111924"/><rect x="403" y="403" width="42" height="42" rx="10" fill="#111924"/><rect x="163" y="355" width="42" height="42" rx="10" fill="#131C25"/><rect x="211" y="355" width="42" height="42" rx="10" fill="#131C25"/><rect x="259" y="355" width="42" height="42" rx="10" fill="#131C25"/><rect x="307" y="355" width="42" height="42" rx="10" fill="#131C25"/><rect x="355" y="355" width="42" height="42" rx="10" fill="#131C25"/><rect x="403" y="355" width="42" height="42" rx="10" fill="#131C25"/><rect x="67" y="307" width="42" height="42" rx="10" fill="#181F27"/><rect x="163" y="307" width="42" height="42" rx="10" fill="#181F27"/><rect x="211" y="307" width="42" height="42" rx="10" fill="#181F27"/><rect x="259" y="307" width="42" height="42" rx="10" fill="#181F27"/><rect x="307" y="307" width="42" height="42" rx="10" fill="#181F27"/><rect x="355" y="307" width="42" height="42" rx="10" fill="#181F27"/><rect x="403" y="307" width="42" height="42" rx="10" fill="#181F27"/><rect x="67" y="259" width="42" height="42" rx="10" fill="#131C1F"/><rect x="211" y="259" width="42" height="42" rx="10" fill="#131C1F"/><rect x="403" y="259" width="42" height="42" rx="10" fill="#131C1F"/><rect x="67" y="211" width="42" height="42" rx="10" fill="#1E1E22"/><rect x="115" y="211" width="42" height="42" rx="10" fill="#1E1E22"/><rect x="211" y="211" width="42" height="42" rx="10" fill="#1E1E22"/><rect x="307" y="211" width="42" height="42" rx="10" fill="#1E1E22"/><rect x="67" y="163" width="42" height="42" rx="10" fill="#1D1C1E"/><rect x="115" y="163" width="42" height="42" rx="10" fill="#1D1C1E"/><rect x="307" y="163" width="42" height="42" rx="10" fill="#1D1C1E"/><rect x="355" y="163" width="42" height="42" rx="10" fill="#1D1C1E"/><rect x="67" y="115" width="42" height="42" rx="10" fill="#1C191E"/><rect x="115" y="115" width="42" height="42" rx="10" fill="#1C191E"/><rect x="163" y="115" width="42" height="42" rx="10" fill="#1C191E"/><rect x="211" y="115" width="42" height="42" rx="10" fill="#1C191E"/><rect x="259" y="115" width="42" height="42" rx="10" fill="#1C191E"/><rect x="307" y="115" width="42" height="42" rx="10" fill="#1C191E"/><rect x="355" y="115" width="42" height="42" rx="10" fill="#1C191E"/><rect x="67" y="67" width="42" height="42" rx="10" fill="#18141B"/><rect x="115" y="67" width="42" height="42" rx="10" fill="#18141B"/><rect x="163" y="67" width="42" height="42" rx="10" fill="#18141B"/><rect x="211" y="67" width="42" height="42" rx="10" fill="#18141B"/><rect x="259" y="67" width="42" height="42" rx="10" fill="#18141B"/><rect x="307" y="67" width="42" height="42" rx="10" fill="#18141B"/><rect x="355" y="67" width="42" height="42" rx="10" fill="#18141B"/><rect x="67" y="403" width="42" height="42" rx="10" fill="#2166ac"/><rect x="67" y="355" width="42" height="42" rx="10" fill="#4393c3"/><rect x="115" y="355" width="42" height="42" rx="10" fill="#4393c3"/><rect x="115" y="307" width="42" height="42" rx="10" fill="#92c5de"/><rect x="115" y="259" width="42" height="42" rx="10" fill="#4a9d5b"/><rect x="163" y="259" width="42" height="42" rx="10" fill="#4a9d5b"/><rect x="163" y="211" width="42" height="42" rx="10" fill="#f6c18a"/><rect x="163" y="163" width="42" height="42" rx="10" fill="#ef9351"/><rect x="211" y="163" width="42" height="42" rx="10" fill="#ef9351"/><rect x="259" y="163" width="42" height="42" rx="10" fill="#ef9351"/><rect x="259" y="211" width="42" height="42" rx="10" fill="#f6c18a"/><rect x="259" y="259" width="42" height="42" rx="10" fill="#4a9d5b"/><rect x="307" y="259" width="42" height="42" rx="10" fill="#4a9d5b"/><rect x="355" y="259" width="42" height="42" rx="10" fill="#4a9d5b"/><rect x="355" y="211" width="42" height="42" rx="10" fill="#f6c18a"/><rect x="403" y="211" width="42" height="42" rx="10" fill="#f6c18a"/><rect x="403" y="163" width="42" height="42" rx="10" fill="#ef9351"/><rect x="403" y="115" width="42" height="42" rx="10" fill="#dd6b52"/><rect x="403" y="67" width="42" height="42" rx="10" fill="#8f0e20"/></svg></span>Thermograph</a></h1>
<p class="tag">What the metrics and percentile grades mean</p>
</div>
<details class="nav-menu">
<summary class="nav-toggle" aria-label="Menu"><svg viewBox="0 0 24 24" width="22" height="22" aria-hidden="true" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><path d="M4 7h16M4 12h16M4 17h16"/></svg></summary>
<div class="nav-panel">
<nav class="view-nav" aria-label="Views">
<a href="./" data-view="map">Weekly</a>
<a href="calendar" data-view="calendar">Calendar</a>
<a href="day" data-view="day">Day Detail</a>
<a href="compare" data-view="compare">Compare</a>
<a href="score" data-view="score">Score</a>
<a href="climate">Climate</a>
<a href="alerts">Alerts</a>
</nav>
</div>
</details>
</div>
</header>
<main class="guide">
<section class="guide-block">
<h2>How the grades work</h2>
<p>Every grade is <b>relative to this place's own climate history</b>, not an absolute
temperature. For each day Thermograph builds a &plusmn;7-day seasonal distribution from
decades of local records, then reports the <b>percentile</b> where the day falls in it.</p>
<p>So a 60&deg; day can be &ldquo;Above Normal&rdquo; in one place or season and
&ldquo;Below Normal&rdquo; in another. That's why the categories read
&ldquo;Above/Below Normal&rdquo;, &ldquo;High/Low&rdquo;, and &ldquo;Near Record&rdquo;,
never &ldquo;hot&rdquo; or &ldquo;cold&rdquo;.</p>
</section>
<section class="guide-block">
<h2>Grade scale: percentile of the local &plusmn;7-day history</h2>
<div class="guide-scale" id="temp-scale"></div>
</section>
<section class="guide-block">
<h2>Rain intensity: percentile among rain days</h2>
<p class="muted">Precipitation is graded only across days that actually saw rain, so
&ldquo;Heavy&rdquo; means heavy <i>for a rainy day here</i>. Dry days are colored by
their dry streak instead (below).</p>
<div class="guide-scale" id="rain-scale"></div>
</section>
<section class="guide-block">
<h2>The metrics</h2>
<dl class="guide-metrics">
<dt>High / Low</dt><dd>The day's highest and lowest air temperature (<span data-unit-label="temp">&deg;F</span>).</dd>
<dt>Feels</dt><dd>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.</dd>
<dt>Humid</dt><dd>Absolute humidity: grams of water vapor per cubic meter of air (g/m&sup3;).</dd>
<dt>Wind</dt><dd>Average sustained wind speed (<span data-unit-label="wind">mph</span>).</dd>
<dt>Gust</dt><dd>Peak wind gust for the day (<span data-unit-label="wind">mph</span>).</dd>
<dt>Precip</dt><dd>Total precipitation (<span data-unit-label="precip">inches</span>), graded by intensity among rain days.</dd>
<dt>Dry streak</dt><dd>Consecutive days since the last measurable rain; the color deepens
the longer it's been dry, and resets to blue on a rain day.</dd>
</dl>
</section>
<p class="guide-back"><a href="./">&larr; Back to Thermograph</a></p>
</main>
<script type="module">
import "./nav.js"; // header view-links follow the last location
import "./units.js"; // the °F/°C toggle
import "./account.js"; // header sign-in entry + notification bell
// The scale rows come from the shared tier tables, so this guide can't
// drift from what the app actually shows.
import { SCALE_TEMP, SCALE_RAIN } from "./shared.js";
const row = ({ c, label, range }) =>
`<div class="guide-seg"><span class="guide-sw" style="background:var(--${c})"></span>` +
`<span class="guide-lbl">${label}</span><span class="guide-rng">${range}</span></div>`;
document.getElementById("temp-scale").innerHTML = SCALE_TEMP.map(row).join("");
document.getElementById("rain-scale").innerHTML = SCALE_RAIN.map(row).join("");
</script>
<script type="module" src="ios-install.js"></script>
</body>
</html>