SunScope
| handleThClick('hour', e)} onMouseEnter=${(e) => handleThEnter('hour', e)} onMouseLeave=${handleThLeave}> Rows e.stopPropagation()}> <${RowStepper} value=${tableInterval} onChange=${setTableInterval} showLabel=${false} /> | ${visibleCols.utciP && html`handleThClick('utciP', e)} onMouseEnter=${(e) => handleThEnter('utciP', e)} onMouseLeave=${handleThLeave}>SunSoak °C felt${UTCI_ENVIRONMENTS[utciEnv]?.shortLabel ? html`${UTCI_ENVIRONMENTS[utciEnv].shortLabel}` : null} | `} ${visibleCols.shadeT && html`handleThClick('shadeT', e)} onMouseEnter=${(e) => handleThEnter('shadeT', e)} onMouseLeave=${handleThLeave}>Shade °C felt | `} ${visibleCols.vehicleT && html`handleThClick('vehicleT', e)} onMouseEnter=${(e) => handleThEnter('vehicleT', e)} onMouseLeave=${handleThLeave}>Vehicle °C peak | `} ${indoorMode === 'on' && !indoorManaged && html`handleThClick('indoorT', e)} onMouseEnter=${(e) => handleThEnter('indoorT', e)} onMouseLeave=${handleThLeave}>Indoors °C est. | `} ${indoorMode === 'on' && indoorManaged && html`handleThClick('managedT', e)} onMouseEnter=${(e) => handleThEnter('managedT', e)} onMouseLeave=${handleThLeave}>Managed °C est. | `} ${visibleCols.burn && html`handleThClick('burn', e)} onMouseEnter=${(e) => handleThEnter('burn', e)} onMouseLeave=${handleThLeave}>Burn to MED | `} ${visibleCols.utci && html`handleThClick('utci', e)} onMouseEnter=${(e) => handleThEnter('utci', e)} onMouseLeave=${handleThLeave}>UTCI °C felt | `} ${visibleCols.delta && html`handleThClick('delta', e)} onMouseEnter=${(e) => handleThEnter('delta', e)} onMouseLeave=${handleThLeave}>Δ UTCI−Air | `} ${visibleCols.tmrt && html`handleThClick('tmrt', e)} onMouseEnter=${(e) => handleThEnter('tmrt', e)} onMouseLeave=${handleThLeave}>Tmrt °C | `} ${visibleCols.concreteT && html`handleThClick('concreteT', e)} onMouseEnter=${(e) => handleThEnter('concreteT', e)} onMouseLeave=${handleThLeave}>Concrete °C surface | `} ${visibleCols.furSurfaceT && html`handleThClick('furSurfaceT', e)} onMouseEnter=${(e) => handleThEnter('furSurfaceT', e)} onMouseLeave=${handleThLeave}>Fur °C surface | `} ${visibleCols.pawT && html`handleThClick('pawT', e)} onMouseEnter=${(e) => handleThEnter('pawT', e)} onMouseLeave=${handleThLeave}>Paw °C surface | `} ${visibleCols.soilT && html`handleThClick('soilT', e)} onMouseEnter=${(e) => handleThEnter('soilT', e)} onMouseLeave=${handleThLeave}>Soil °C surface | `} ${visibleCols.soilT6 && html`handleThClick('soilT6', e)} onMouseEnter=${(e) => handleThEnter('soilT6', e)} onMouseLeave=${handleThLeave}>Soil 6cm °C root | `} ${visibleCols.soilM && html`handleThClick('soilM', e)} onMouseEnter=${(e) => handleThEnter('soilM', e)} onMouseLeave=${handleThLeave}>Soil moist % | `} ${visibleCols.petShadeT && html`handleThClick('petShadeT', e)} onMouseEnter=${(e) => handleThEnter('petShadeT', e)} onMouseLeave=${handleThLeave}>Pet Shade °C | `} ${visibleCols.petHomeT && html`handleThClick('petHomeT', e)} onMouseEnter=${(e) => handleThEnter('petHomeT', e)} onMouseLeave=${handleThLeave}>Pet Home °C est. | `} ${visibleCols.air && html`handleThClick('air', e)} onMouseEnter=${(e) => handleThEnter('air', e)} onMouseLeave=${handleThLeave}>Air °C | `} ${visibleCols.rh && html`handleThClick('rh', e)} onMouseEnter=${(e) => handleThEnter('rh', e)} onMouseLeave=${handleThLeave}>RH % | `} ${visibleCols.dew && html`handleThClick('dew', e)} onMouseEnter=${(e) => handleThEnter('dew', e)} onMouseLeave=${handleThLeave}>Dew °C | `} ${visibleCols.precip && html`handleThClick('precip', e)} onMouseEnter=${(e) => handleThEnter('precip', e)} onMouseLeave=${handleThLeave}>Pcpt mm/h | `} ${visibleCols.precipProb && html`handleThClick('precipProb', e)} onMouseEnter=${(e) => handleThEnter('precipProb', e)} onMouseLeave=${handleThLeave}>Rain % | `} ${visibleCols.lightning && html`handleThClick('lightning', e)} onMouseEnter=${(e) => handleThEnter('lightning', e)} onMouseLeave=${handleThLeave}>⚡ LPI | `} ${visibleCols.cloud && html`handleThClick('cloud', e)} onMouseEnter=${(e) => handleThEnter('cloud', e)} onMouseLeave=${handleThLeave}>Cloud % | `} ${visibleCols.vis && html`handleThClick('vis', e)} onMouseEnter=${(e) => handleThEnter('vis', e)} onMouseLeave=${handleThLeave}>Vis km | `} ${visibleCols.wind && html`handleThClick('wind', e)} onMouseEnter=${(e) => handleThEnter('wind', e)} onMouseLeave=${handleThLeave}>Wind mph (gust) | `} ${visibleCols.dir && html`handleThClick('dir', e)} onMouseEnter=${(e) => handleThEnter('dir', e)} onMouseLeave=${handleThLeave}>Dir - | `} ${visibleCols.aqi && html`handleThClick('aqi', e)} onMouseEnter=${(e) => handleThEnter('aqi', e)} onMouseLeave=${handleThLeave}>AQI EU idx | `} ${visibleCols.pollen && html`handleThClick('pollen', e)} onMouseEnter=${(e) => handleThEnter('pollen', e)} onMouseLeave=${handleThLeave}>${pollenType === 'all_pollen' ? 'Pollen' : POLLEN_TYPES[pollenType]?.name.replace(' pollen','') ?? 'Pollen'} grains/m³ | `} ${visibleCols.uvA && html`handleThClick('uvA', e)} onMouseEnter=${(e) => handleThEnter('uvA', e)} onMouseLeave=${handleThLeave}>UV-A est. idx | `} ${visibleCols.uvB && html`handleThClick('uvB', e)} onMouseEnter=${(e) => handleThEnter('uvB', e)} onMouseLeave=${handleThLeave}>UV-B est. idx | `} ${visibleCols.sun && html`handleThClick('sun', e)} onMouseEnter=${(e) => handleThEnter('sun', e)} onMouseLeave=${handleThLeave}>Sun elev° | `} ${visibleCols.direct && html`handleThClick('direct', e)} onMouseEnter=${(e) => handleThEnter('direct', e)} onMouseLeave=${handleThLeave}>Direct W/m² | `} ${visibleCols.diffuse && html`handleThClick('diffuse', e)} onMouseEnter=${(e) => handleThEnter('diffuse', e)} onMouseLeave=${handleThLeave}>Diffuse W/m² | `}
|---|
| <${SkyScope} elev=${r.elev} dt=${r.dt} glob=${r.glob} size=${38} /> ${localHHMM} ${(() => { const rowEvents = getCellTagEvents(selectedDayEvents, r); return rowEvents.map((ev, idx) => html` handleEventTagClick(rowEvents, idx, e)} onMouseEnter=${(e) => handleEventTagEnter(rowEvents, idx, e)} onMouseLeave=${handleEventTagLeave} role="button" tabIndex="0" aria-label=${ev.title} >${ev.emoji} `); })()} | ${visibleCols.utciP && html`${fmt(r.utciAdj)}${u('°C')} | `} ${visibleCols.shadeT && html`${r.shadeT != null ? fmt(r.shadeT) + u('°C') : '—'} | `} ${visibleCols.vehicleT && html`${fmt(r.vehicleT)}${u('°C')} | `} ${indoorMode === 'on' && !indoorManaged && html`${fmt(r.indoorT)}${u('°C')} | `} ${indoorMode === 'on' && indoorManaged && html`${fmt(r.managedT)}${u('°C')} | `} ${visibleCols.burn && html`0 ? (burnMins < 30 ? '#c44a3a' : '#c8601a') : '#4a3218', background: burnBg(burnMins, r.uv) }}> ${burnLabel(burnMins)} | `} ${visibleCols.utci && html`${fmt(r.utci)}${u('°C')} | `} ${visibleCols.delta && html`3 ? '#c8601a' : delta < -3 ? '#3f73c4' : '#4a3218', background: deltaBg(delta), }}> ${delta > 0 ? '+' : ''}${fmt(delta)}${u('°C')} | `} ${visibleCols.tmrt && html`${fmt(r.Tmrt)}${u('°C')} | `} ${visibleCols.concreteT && html`${fmt(r.concreteT)}${u('°C')} | `} ${visibleCols.furSurfaceT && html`${fmt(r.furSurfaceT)}${u('°C')} | `} ${visibleCols.pawT && html`${fmt(r.pawT)}${u('°C')} | `} ${visibleCols.soilT && html`${r.soilT0 != null ? fmt(r.soilT0) + u('°C') : '—'} | `} ${visibleCols.soilT6 && html`${r.soilT6 != null ? fmt(r.soilT6) + u('°C') : '—'} | `} ${visibleCols.soilM && html`{ const sm = soilMoistureBg(r.soilM); return { color: sm.fg, background: sm.bg }; })()}>${r.soilM != null ? fmt(r.soilM * 100, 1) + u('%') : '—'} | `} ${visibleCols.petShadeT && html`${r.petShadeT != null ? fmt(r.petShadeT) + u('°C') : '—'} | `} ${visibleCols.petHomeT && html`${fmt(r.indoorT)}${u('°C')} | `} ${visibleCols.air && html`${fmt(r.Ta)}${u('°C')} | `} ${visibleCols.rh && html`${Math.round(r.RH)}${u('%')} | `} ${visibleCols.dew && html`${fmt(r.dew)}${u('°C')} | `} ${visibleCols.precip && html`0 ? scaleBg(r.snow, 0, 4, '90,140,210') : scaleBg(r.precip, 0, 8, '70,145,200') }}> <${PrecipIcon} precip=${r.precipProb > 0 ? r.precip : 0} snow=${r.precipProb > 0 ? r.snow : 0} size=${28} /> 0 ? '#2a5fa8' : r.precip > 0 ? '#2a6a90' : '#7a5c30' }}> ${r.precipProb > 0 ? (r.snow > 0 ? fmt(r.snow) + u('cm') : r.precip > 0 ? fmt(r.precip) + u('mm') : '—') : '—'} | `} ${visibleCols.precipProb && html`= 50 ? '#1a4a70' : r.precipProb > 0 ? '#2a6a90' : '#7a8a90' }}> ${r.precipProb}${u('%')} | `} ${visibleCols.lightning && (() => { const lpi = r.lightning ?? 0; const bg = lpi >= 25 ? 'rgba(255,180,0,0.25)' : lpi >= 5 ? 'rgba(255,210,0,0.15)' : 'transparent'; const color = lpi >= 25 ? '#7a4800' : lpi >= 5 ? '#6a5800' : '#7a8a90'; return html`${lpi > 0 ? `${Math.round(lpi)}` : ''} | `; })()} ${visibleCols.cloud && html`<${CloudIcon} category=${r.cloudCat} elev=${r.elev} dt=${r.dt} size=${28} /> ${Math.round(r.cc)}${u('%')} | `} ${visibleCols.vis && (() => { const v = r.visKm; const bg = v == null ? 'transparent' : v < 1 ? 'rgba(180,80,80,0.18)' : v < 4 ? 'rgba(210,140,50,0.15)' : v < 10 ? 'rgba(200,190,80,0.12)' : 'transparent'; const color = v != null && v < 4 ? '#8a3a1a' : '#4a3218'; return html`${v != null ? fmt(v) + u('km') : '—'} | `; })()} ${visibleCols.wind && html`${Math.round(r.va * 2.237)}${u('mph')}${r.gust != null && r.gust > r.va + 0.5 ? (gm => html`= 25 ? 700 : 'normal', opacity: gm >= 25 ? 1 : 0.65, color: gm >= 55 ? '#b81010' : gm >= 40 ? '#d44010' : gm >= 25 ? '#c47a00' : 'inherit' }}>(${Math.round(gm)}${u('mph')})`)(r.gust * 2.237) : ''} | `} ${visibleCols.dir && html`<${WindVane} bearing=${r.wd} size=${28} /> ${r.compass.label} | `} ${visibleCols.aqi && (() => { const v = r.aqi == null ? null : Math.round(r.aqi); const bg = v == null ? 'transparent' : v < 20 ? 'rgba(80,180,100,0.15)' : v < 40 ? 'rgba(140,200,100,0.13)' : v < 60 ? 'rgba(220,200,60,0.15)' : v < 80 ? 'rgba(220,130,50,0.18)' : v < 100 ? 'rgba(200,70,50,0.18)' : 'rgba(160,30,100,0.20)'; const color = v != null && v >= 60 ? '#7a2010' : v != null && v >= 40 ? '#7a4a10' : '#2a4a20'; const label = v == null ? '—' : v < 20 ? `${v} Good` : v < 40 ? `${v} Fair` : v < 60 ? `${v} Mod` : v < 80 ? `${v} Poor` : v < 100 ? `${v} V.Poor` : `${v} Hazard`; return html`= 60 ? 600 : 400 }}>${label} | `; })()} ${visibleCols.pollen && (() => { const pollenMap = { all_pollen: [r.grassPollen, r.birchPollen, r.alderPollen, r.mugwortPollen, r.olivePollen, r.ragweedPollen].reduce((s, x) => x != null ? s + x : s, null), grass_pollen: r.grassPollen, birch_pollen: r.birchPollen, alder_pollen: r.alderPollen, mugwort_pollen: r.mugwortPollen, olive_pollen: r.olivePollen, ragweed_pollen: r.ragweedPollen, }; const v = pollenMap[pollenType] ?? null; const bg = v == null ? 'transparent' : v < 10 ? 'transparent' : v < 50 ? 'rgba(180,200,80,0.13)' : v < 200 ? 'rgba(210,150,50,0.16)' : 'rgba(200,70,50,0.18)'; const color = v != null && v >= 200 ? '#8a2010' : v != null && v >= 50 ? '#7a4a10' : '#4a3218'; const label = v == null ? '—' : v < 10 ? `${Math.round(v)} Low` : v < 50 ? `${Math.round(v)} Mod` : v < 200 ? `${Math.round(v)} High` : `${Math.round(v)} V.High`; return html`= 50 ? 600 : 400 }}>${label} | `; })()} ${visibleCols.uvA && html`0 ? '#c8922a' : '#4a3218', background: scaleBg(r.uvA, 0, 8, '220,155,40') }}> ${r.uvA > 0 ? fmt(r.uvA) + u(' idx') : '—'} | `} ${visibleCols.uvB && html`0 ? '#c44a3a' : '#4a3218', background: scaleBg(r.uvB, 0, 1.2, '210,70,50') }}> ${r.uvB > 0 ? fmt(r.uvB, 2) + u(' idx') : '—'} | `} ${visibleCols.sun && html`0 ? r.elev : null, 0, 70, '225,160,45') }}>${r.elev > 0 ? fmt(r.elev) + u('°') : '—'} | `} ${visibleCols.direct && html`${Math.round(r.dir)}${u('W/m²')} | `} ${visibleCols.diffuse && html`${Math.round(r.dif)}${u('W/m²')} | `}
${COL_DESCRIPTIONS[colPopup.key].desc}
${ev.message}
What is SunScope?
SunScope is a free hourly weather forecast built around felt temperature, not just air temperature. It uses the Universal Thermal Climate Index (UTCI) — the biometeorological standard used in heat-health warning systems worldwide — as its foundation, combining air temperature, humidity, wind, and solar radiation into a single honest number. Then it goes further. Our SunSoak index layers three extra dimensions on top: a rain and snow penalty so wet, windy days read as cold as they feel; an environment modifier that adjusts the solar load for where you actually are — forest canopy, alpine altitude, lakeside glare, shaded riverbank, desert ground heat; and the full radiant heat absorbed from surrounding surfaces. One number that honestly answers: what will my body actually feel out there?
Beyond SunSoak, SunScope calculates vehicle cabin heat (choose your vehicle type; toggle windows open), indoor temperature (seven building types including office blocks; managed heatwave mode), urban concrete surface temperature, UV index and sunburn time by skin type, and soil temperature and moisture for farming and motorhome use. Switch profiles — Places, Activities, Work — to see the data that matters for your situation, or go Custom and build your own view. Learn more →
Reading the table
SunSoak is the number to watch — it reflects everything your body actually experiences, not just what the thermometer says. A large gap between SunSoak and Air temperature means solar radiation is doing significant work on your body. On clear sunny days that gap can exceed 10 °C even at modest air temperatures. The environment modifier in the SunSoak dropdown adjusts the solar load for your surroundings — switch it to match where you are for the most accurate reading. ${' '} How it works →