diff --git a/static/compare.js b/static/compare.js index 456b9e8..53648e0 100644 --- a/static/compare.js +++ b/static/compare.js @@ -331,6 +331,17 @@ const pct = (v) => `${v < 10 ? v.toFixed(1) : Math.round(v)}%`; // Temperature *differences* (avg miss, typical miss) in the active unit. const deg = (v) => fmtDelta(v); +// Names the five bar colors once, above the ranked cards. +const LEGEND = + ``; + function renderLocList() { locList.innerHTML = locations.map((l, i) => { let label, cls = "cmp-chip", lead = ""; @@ -347,24 +358,29 @@ function renderLocList() { }).join(""); } -// A ranked place: the 0–100 match score up top, then a diverging bar whose colder -// days grow left of a fixed center (the comfort zone) and warmer days grow right. -// Each side is split into a tolerance band (lighter, nearest the center) and a -// beyond-tolerance band (saturated, further out), so how far a place misses — not -// just how often — is visible, and a cold place and a hot one mirror each other. +// A ranked place: the 0–100 match score up top, then one full-width bar showing how +// this place's days split across five temperature buckets, ordered cold → hot using +// the site's own temperature colors — too cold, cool (within tolerance), comfortable, +// warm (within tolerance), too hot. The whole bar is 100% of the days, so every +// segment is a real share and a place that runs cold vs one that runs hot read at a +// glance (more blue vs more red). The legend above the cards names each color. function rankCard(loc, s, rank) { const win = rank === 1; - const seg = (cls, w) => (w > 0 ? `` : ""); - const bar = ``; + // Label a segment inline when it's wide enough to fit the number legibly. + const seg = (cls, w) => (w > 0 + ? `${w >= 10 ? `${Math.round(w)}%` : ""}` + : ""); + const bar = ``; const cap = - `
` + - `${pct(s.belowPct)} colder${s.below ? ` · avg ${deg(s.avgBelow)} under` : ""}` + - `${s.above ? `avg ${deg(s.avgAbove)} over · ` : ""}${pct(s.abovePct)} warmer` + + `
` + + `${pct(s.belowPct)} below comfort${s.below ? ` · avg ${deg(s.avgBelow)} colder` : ""}` + + `${s.above ? `avg ${deg(s.avgAbove)} hotter · ` : ""}${pct(s.abovePct)} above comfort` + `
`; - const bands = `
${pct(s.comfortPct)} in comfort · ${pct(s.tolPct)} in tolerance · typically off by ${deg(s.mad)}
`; + const bands = `
${pct(s.comfortPct)} comfortable · ${pct(s.tolPct)} within tolerance · typically ${deg(s.mad)} off
`; const sub = win ? `★ Best match · ${s.n} days` : `${s.n} days`; return `
` + `
` + @@ -447,7 +463,7 @@ function renderResults() { `

By ${BASIS_LABEL[basis]} · comfort ${fmtTemp(lo)}–${fmtTemp(hi)}, tolerance ${fmtTemp(tlo)}–${fmtTemp(thi)} · ${span}` + `${loading ? ` · loading ${loading} more…` : ""}

`; - results.innerHTML = scored.map((x, i) => rankCard(x.loc, x.s, i + 1)).join("") + + results.innerHTML = LEGEND + scored.map((x, i) => rankCard(x.loc, x.s, i + 1)).join("") + (loading ? Array.from({ length: loading }, skeletonRow).join("") : ""); } diff --git a/static/style.css b/static/style.css index 1160aa9..249c37e 100644 --- a/static/style.css +++ b/static/style.css @@ -847,19 +847,26 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 24px 60px; } /* Diverging comfort bar: a fixed center marks the comfort zone; colder days grow left of it, warmer days grow right. Both halves share one 0–100% scale, so a cold-skewed place and a hot-skewed one mirror each other and compare at a glance. */ -.cmp-dv { display: flex; align-items: stretch; height: 16px; margin: 14px 0 8px; } -.cmp-dv-side { flex: 1 1 0; display: flex; background: var(--surface-2); overflow: hidden; } -.cmp-dv-left { border-radius: 8px 0 0 8px; justify-content: flex-end; } /* colder packs toward center */ -.cmp-dv-right { border-radius: 0 8px 8px 0; justify-content: flex-start; } /* warmer packs toward center */ -.cmp-dv-seg { height: 100%; flex: 0 0 auto; } -/* Nearest the centre = within tolerance (lighter); further out = beyond it (saturated). */ -.cmp-seg-cool { background: var(--cool); } -.cmp-seg-cold { background: var(--cold); } -.cmp-seg-warm { background: var(--warm); } -.cmp-seg-hot { background: var(--hot); } -.cmp-dv-mid { flex: 0 0 4px; background: var(--normal); } /* the comfort-zone marker */ +/* Legend naming the five bar colors once, above the cards. */ +.cmp-legend { display: flex; flex-wrap: wrap; align-items: center; gap: 6px 14px; margin: 2px 0 4px; font-size: 12px; color: var(--muted); } +.cmp-legend > span { display: inline-flex; align-items: center; gap: 6px; } +.cmp-sw { width: 12px; height: 12px; border-radius: 3px; flex: 0 0 auto; } +.cmp-legend-note { font-style: italic; opacity: .85; } -.cmp-dv-cap { display: flex; justify-content: space-between; gap: 10px; font-size: 12px; } +/* Full-width day-distribution bar: five temperature buckets, cold → hot, together + 100% of the days, in the site's own temperature colors. Every segment is a real + share, so more blue = runs cold, more red = runs hot — no empty space to decode. */ +.cmp-bar { display: flex; height: 22px; border-radius: 6px; overflow: hidden; margin: 14px 0 8px; background: var(--surface-2); } +.cmp-seg { height: 100%; display: flex; align-items: center; justify-content: center; min-width: 0; } +.cmp-seg-n { font-size: 10.5px; font-weight: 700; color: rgba(10, 15, 20, .68); } +.cmp-seg-vcold .cmp-seg-n, .cmp-seg-comf .cmp-seg-n, .cmp-seg-vhot .cmp-seg-n { color: rgba(255, 255, 255, .95); } +.cmp-seg-vcold { background: var(--very-cold); } +.cmp-seg-cool { background: var(--cool); } +.cmp-seg-comf { background: var(--normal); } +.cmp-seg-warm { background: var(--warm); } +.cmp-seg-vhot { background: var(--very-hot); } + +.cmp-cap-row { display: flex; justify-content: space-between; gap: 10px; font-size: 12px; } .cmp-cap-below { color: var(--cold); } .cmp-cap-above { color: var(--hot); text-align: right; } .cmp-bands { margin: 8px 0 0; font-size: 12.5px; color: var(--muted); } @@ -1058,5 +1065,6 @@ main { max-width: 1200px; margin: 0 auto; padding: 20px 24px 60px; } .cmp-bl-row { grid-template-columns: 84px 1fr; gap: 8px; } .cmp-bl-name { font-size: 12px; } .cmp-bl-axis { margin-left: 92px; } - .cmp-dv-cap { font-size: 11px; } + .cmp-cap-row { font-size: 11px; } + .cmp-legend { font-size: 11px; gap: 5px 10px; } }