From 598a60c7f5eb126d835f6464f3321cdc7344d5f9 Mon Sep 17 00:00:00 2001 From: mrkmntal Date: Tue, 14 Apr 2026 12:17:42 -0400 Subject: [PATCH] Add Ground View screen for server edition, powered by the Windy API https://api.windy.com/ --- .gitignore | 1 + gulp/publish-frontend.mjs | 1 + index.mjs | 32 ++++++ server/scripts/index.mjs | 1 + server/scripts/modules/groundview.mjs | 94 ++++++++++++++++ server/styles/scss/_ground-view.scss | 57 ++++++++++ server/styles/scss/ws.scss | 1 + server/styles/ws.min.css | 2 +- src/windy-webcams.mjs | 150 ++++++++++++++++++++++++++ views/index.ejs | 3 + views/partials/ground-view.ejs | 8 ++ 11 files changed, 349 insertions(+), 1 deletion(-) create mode 100644 server/scripts/modules/groundview.mjs create mode 100644 server/styles/scss/_ground-view.scss create mode 100644 src/windy-webcams.mjs create mode 100644 views/partials/ground-view.ejs diff --git a/.gitignore b/.gitignore index 8e24341..e06e8cb 100644 --- a/.gitignore +++ b/.gitignore @@ -17,3 +17,4 @@ dist/* #environment variables .env nohup.out +windy-*.txt diff --git a/gulp/publish-frontend.mjs b/gulp/publish-frontend.mjs index 1d13237..46fac99 100644 --- a/gulp/publish-frontend.mjs +++ b/gulp/publish-frontend.mjs @@ -77,6 +77,7 @@ const mjsSources = [ 'server/scripts/modules/hourly-graph.mjs', 'server/scripts/modules/localforecast.mjs', 'server/scripts/modules/radar.mjs', + 'server/scripts/modules/groundview.mjs', 'server/scripts/modules/regionalforecast.mjs', 'server/scripts/modules/travelforecast.mjs', 'server/scripts/modules/progress.mjs', diff --git a/index.mjs b/index.mjs index f37295f..7f61e70 100644 --- a/index.mjs +++ b/index.mjs @@ -20,6 +20,7 @@ import OVERRIDES from './src/overrides.mjs'; import cache from './proxy/cache.mjs'; import devTools from './src/com.chrome.devtools.mjs'; import { discoverThemes } from './src/theme-discovery.mjs'; +import { findNearestWindyWebcam, loadWindyApiKey } from './src/windy-webcams.mjs'; const execAsync = promisify(exec); @@ -252,6 +253,37 @@ if (!process.env?.STATIC) { } }); + app.get('/api/ground-view', async (req, res) => { + try { + const lat = parseFloat(req.query.lat); + const lon = parseFloat(req.query.lon); + const city = String(req.query.city ?? '').trim(); + + if (Number.isNaN(lat) || Number.isNaN(lon) || !city) { + res.status(400).json({ + success: false, + webcam: null, + error: 'Missing or invalid lat/lon/city', + }); + return; + } + + const apiKey = await loadWindyApiKey(); + const webcam = await findNearestWindyWebcam(lat, lon, city, apiKey); + + res.json({ + success: true, + webcam, + }); + } catch (error) { + res.status(500).json({ + success: false, + webcam: null, + error: error.message, + }); + } + }); + app.use('/api/', weatherProxy); // Cache management DELETE endpoint to allow "uncaching" specific URLs diff --git a/server/scripts/index.mjs b/server/scripts/index.mjs index 781129b..8bb592c 100644 --- a/server/scripts/index.mjs +++ b/server/scripts/index.mjs @@ -8,6 +8,7 @@ import { registerHiddenSetting } from './modules/share.mjs'; import settings from './modules/settings.mjs'; import './modules/utils/theme.mjs'; import './modules/latestobservations.mjs'; +import './modules/groundview.mjs'; import AutoComplete from './modules/autocomplete.mjs'; import { loadAllData } from './modules/utils/data-loader.mjs'; import { debugFlag } from './modules/utils/debug.mjs'; diff --git a/server/scripts/modules/groundview.mjs b/server/scripts/modules/groundview.mjs new file mode 100644 index 0000000..2bb19a3 --- /dev/null +++ b/server/scripts/modules/groundview.mjs @@ -0,0 +1,94 @@ +import STATUS from './status.mjs'; +import WeatherDisplay from './weatherdisplay.mjs'; +import { registerDisplay } from './navigation.mjs'; +import { safeJson } from './utils/fetch.mjs'; +import { withBasePath } from './utils/base-path.mjs'; + +class GroundView extends WeatherDisplay { + constructor(navId, elemId) { + super(navId, elemId, 'Ground View', true); + this.refreshTime = 5 * 60 * 1000; + this.requestToken = 0; + } + + async getData(weatherParameters, refresh) { + const superResult = super.getData(weatherParameters, refresh); + this.setAutoReload(); + const requestToken = ++this.requestToken; + if (!this.weatherParameters?.latitude || !this.weatherParameters?.longitude || !this.weatherParameters?.city) { + this.setStatus(STATUS.noData); + return superResult; + } + + const url = new URL(withBasePath('api/ground-view'), window.location.origin); + url.searchParams.set('lat', this.weatherParameters.latitude); + url.searchParams.set('lon', this.weatherParameters.longitude); + url.searchParams.set('city', this.weatherParameters.city); + + const response = await safeJson(url.toString(), { + retryCount: 1, + }); + + if (requestToken !== this.requestToken) { + return superResult; + } + + if (!response?.success) { + this.data = null; + this.setStatus(STATUS.failed); + return superResult; + } + + this.data = { + webcam: response.webcam, + hasWebcam: Boolean(response.webcam?.imageUrl), + }; + this.setStatus(STATUS.loaded); + return superResult; + } + + resetViewState() { + const image = this.elem.querySelector('.ground-view-image'); + const label = this.elem.querySelector('.ground-view-label'); + const empty = this.elem.querySelector('.ground-view-empty'); + const media = this.elem.querySelector('.ground-view-media'); + + image.removeAttribute('src'); + image.alt = ''; + label.textContent = ''; + empty.textContent = ''; + media.classList.add('hidden'); + label.classList.add('hidden'); + empty.classList.add('hidden'); + } + + drawCanvas() { + super.drawCanvas(); + const image = this.elem.querySelector('.ground-view-image'); + const label = this.elem.querySelector('.ground-view-label'); + const empty = this.elem.querySelector('.ground-view-empty'); + const media = this.elem.querySelector('.ground-view-media'); + + this.resetViewState(); + + if (this.data?.hasWebcam) { + image.src = this.data.webcam.imageUrl; + image.alt = this.data.webcam.label; + label.textContent = this.data.webcam.label; + media.classList.remove('hidden'); + label.classList.remove('hidden'); + empty.classList.add('hidden'); + } else { + image.removeAttribute('src'); + label.textContent = ''; + media.classList.add('hidden'); + label.classList.add('hidden'); + empty.classList.remove('hidden'); + empty.textContent = 'No Ground View Available'; + } + + this.finishDraw(); + } +} + +registerDisplay(new GroundView(12, 'ground-view')); diff --git a/server/styles/scss/_ground-view.scss b/server/styles/scss/_ground-view.scss new file mode 100644 index 0000000..b230e7c --- /dev/null +++ b/server/styles/scss/_ground-view.scss @@ -0,0 +1,57 @@ +@use 'shared/_colors' as c; +@use 'shared/_utils' as u; + +.weather-display .main.ground-view { + &.main { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 12px; + padding: 20px 24px 18px; + + .hidden { + display: none; + } + + .ground-view-media { + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + flex: 1 1 auto; + min-height: 0; + margin-right: 60px; + margin-top: 30px; + } + + .ground-view-image { + display: block; + max-width: 300px; + max-height: 150px; + object-fit: contain; + border: 2px solid hsl(0deg 0% 100% / 35%); + background: #000; + } + + .ground-view-label, + .ground-view-empty { + width: 100%; + text-align: center; + font-family: 'Star4000'; + font-size: 15pt; + color: #ff0; + padding: 10px; + margin-right: 80px; + text-shadow: 3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000; + } + + .ground-view-empty { + flex: 1 1 auto; + display: flex; + align-items: center; + justify-content: center; + } + } +} diff --git a/server/styles/scss/ws.scss b/server/styles/scss/ws.scss index 2135c50..76c0867 100644 --- a/server/styles/scss/ws.scss +++ b/server/styles/scss/ws.scss @@ -9,6 +9,7 @@ @use 'local-forecast'; @use 'progress'; @use 'radar'; +@use 'ground-view'; @use 'regional-forecast'; @use 'almanac'; @use 'hazards'; diff --git a/server/styles/ws.min.css b/server/styles/ws.min.css index 941e216..6966ffa 100644 --- a/server/styles/ws.min.css +++ b/server/styles/ws.min.css @@ -1 +1 @@ -@font-face{font-family:"Star4000";src:url("../fonts/Star4000.woff") format("woff");font-display:swap}body{font-family:"Star4000";margin:0}@media(prefers-color-scheme: dark){body{background-color:#000;color:#fff}}@media(prefers-color-scheme: dark){body a{color:#add8e6}}body.kiosk{margin:0px;padding:0px;overflow:hidden;width:100vw;background-color:#000 !important}#divQuery{max-width:640px;padding:8px}#divQuery .buttons{display:inline-block;width:150px;text-align:right}#divQuery .buttons #imgGetGps{height:13px;vertical-align:middle}#divQuery .buttons button{font-size:16pt;border:1px solid #a9a9a9}@media(prefers-color-scheme: dark){#divQuery .buttons button{background-color:#000;color:#fff}}#divQuery .buttons #btnGetGps img.dark{display:none}@media(prefers-color-scheme: dark){#divQuery .buttons #btnGetGps img.dark{display:inline-block}}@media(prefers-color-scheme: dark){#divQuery .buttons #btnGetGps img.light{display:none}}#divQuery .buttons #btnGetGps.active{background-color:#000}@media(prefers-color-scheme: dark){#divQuery .buttons #btnGetGps.active{background-color:#fff}}#divQuery .buttons #btnGetGps.active img{filter:invert(1)}#divQuery input,#divQuery button{font-family:"Star4000"}#divQuery #txtLocation{width:calc(100% - 170px);max-width:490px;font-size:16pt;min-width:200px;display:inline-block;background-color:#fff;color:#000;border:2px inset gray}@media(prefers-color-scheme: dark){#divQuery #txtLocation{background-color:#000;color:#fff;border:2px inset gray}}.autocomplete-suggestions{background-color:#fff;border:1px solid #000;position:absolute;z-index:9999}@media(prefers-color-scheme: dark){.autocomplete-suggestions{background-color:#000}}.autocomplete-suggestions div{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:16pt}.autocomplete-suggestions div.selected{background-color:blue;color:#fff}#divTwc{display:block;background-color:#000;color:#fff;width:100%;max-width:640px;margin:0}#divTwc.wide{max-width:854px}.content-wrapper{padding:8px}#divTwcMain{width:640px;height:480px;position:relative}.wide #divTwcMain{width:854px}.kiosk #divTwc{max-width:unset}#divTwcLeft{display:none;text-align:right;flex-direction:column;vertical-align:middle}#divTwcLeft>div{flex:1;padding-right:12px;display:flex;flex-direction:column;justify-content:center}#divTwcRight{text-align:left;display:none;flex-direction:column;vertical-align:middle}#divTwcRight>div{flex:1;padding-left:12px;display:flex;flex-direction:column;justify-content:center}#divTwcBottom{display:flex;flex-direction:row;background-color:#000;color:#fff;width:640px}.wide #divTwcBottom{width:854px}@media(prefers-color-scheme: dark){#divTwcBottom{background-color:#303030}}#divTwcBottom>div{padding-left:6px;padding-right:6px}@media(max-width: 550px){#divTwcBottom>div{font-size:.9em}}@media(max-width: 500px){#divTwcBottom>div{font-size:.8em}}@media(max-width: 450px){#divTwcBottom>div{font-size:.7em}}@media(max-width: 400px){#divTwcBottom>div{font-size:.6em}}@media(max-width: 350px){#divTwcBottom>div{font-size:.5em}}#divTwcBottomLeft{flex:1;text-align:left}#divTwcBottomMiddle{flex:0;text-align:center}#divTwcBottomRight{flex:1;text-align:right}#divTwcNavContainer{display:none}#divTwcNav{width:100%;display:flex;flex-direction:row;background-color:#000;color:#fff;max-width:640px}#divTwcNav>div{padding-left:6px;padding-right:6px}#divTwcNavLeft{flex:1;text-align:left}#divTwcNavMiddle{flex:0;text-align:center}#divTwcNavRight{flex:1;text-align:right}#imgPause1x{visibility:hidden;position:absolute}.HideCursor{cursor:none !important}#txtScrollText{width:475px}@font-face{font-family:"Star4000 Extended";src:url("../fonts/Star4000 Extended.woff") format("woff");font-display:swap}@font-face{font-family:"Star4000 Large";src:url("../fonts/Star4000 Large.woff") format("woff");font-display:swap}@font-face{font-family:"Star4000 Small";src:url("../fonts/Star4000 Small.woff") format("woff");font-display:swap}:root{--theme-background-1: url('../images/backgrounds/1.png');--theme-background-1-chart: url('../images/backgrounds/1-chart.png');--theme-background-2: url('../images/backgrounds/2.png');--theme-background-3: url('../images/backgrounds/3.png');--theme-background-4: url('../images/backgrounds/4.png');--theme-background-5: url('../images/backgrounds/5.png');--theme-background-6: url('../images/backgrounds/6.png')}#display{font-family:"Star4000";margin:0 0 0 0;width:100%}#container{position:relative;width:640px;height:480px;background-image:var(--theme-background-1);transform-origin:0 0;background-repeat:no-repeat}.wide #container{padding-left:107px;padding-right:107px;background:url(../images/backgrounds/1-wide.png);background-repeat:no-repeat}#divTwc:fullscreen #container,.kiosk #divTwc #container{width:unset;height:unset}#loading{width:640px;height:480px;max-width:100%;text-shadow:4px 4px #000;display:flex;align-items:center;text-align:center;justify-content:center}#loading .title{font-family:Star4000 Large;font-size:36px;color:#ff0;margin-bottom:0px}#loading .version{margin-bottom:35px}#loading .instructions{font-size:18pt}.heading{font-weight:bold;margin-top:15px}#settings{margin-bottom:15px}#enabledDisplays,#settings{margin-bottom:15px}#enabledDisplays .loading,#enabledDisplays .retrying,#settings .loading,#settings .retrying{color:#ff0}#enabledDisplays .press-here,#settings .press-here{color:lime;cursor:pointer}#enabledDisplays .failed,#settings .failed{color:red}#enabledDisplays .no-data,#settings .no-data{color:silver}#enabledDisplays .disabled,#settings .disabled{color:silver}#enabledDisplays .press-here,#settings .press-here{color:#fff}@media(prefers-color-scheme: light){#enabledDisplays .loading,#enabledDisplays .retrying,#settings .loading,#settings .retrying{color:#990}#enabledDisplays .press-here,#settings .press-here{color:#000;cursor:pointer}#enabledDisplays .failed,#settings .failed{color:#900}#enabledDisplays .no-data,#settings .no-data{color:hsl(0,0%,30%)}#enabledDisplays .disabled,#settings .disabled{color:hsl(0,0%,30%)}}#enabledDisplays label,#settings label{display:block;max-width:fit-content;cursor:pointer}#enabledDisplays label .alert,#settings label .alert{display:none}#enabledDisplays label .alert.show,#settings label .alert.show{display:inline;color:red}#divTwcBottom img{transform:scale(0.75)}@media(max-width: 550px){.wide #divTwcBottom img{transform:scale(1)}}#divTwc:fullscreen,.kiosk #divTwc{display:flex;align-items:center;justify-content:center;align-content:center}#divTwc:fullscreen.no-cursor,.kiosk #divTwc.no-cursor{cursor:none}#divTwc:fullscreen #display,.kiosk #divTwc #display{position:relative}#divTwc:fullscreen #divTwcBottom,.kiosk #divTwc #divTwcBottom{display:flex;flex-direction:row;background-color:rgba(0,0,0,.5);color:#fff;width:100%;position:absolute;bottom:0px}.kiosk #divTwc #divTwcBottom{display:none}.navButton{cursor:pointer}#ToggleScanlines{display:inline-block}#ToggleScanlines .on{display:none}#ToggleScanlines .off{display:inline-block}#ToggleScanlines.on .on{display:inline-block}#ToggleScanlines.on .off{display:none}.visible{visibility:visible;opacity:1;transition:opacity .1s linear}#divTwc:fullscreen .hidden{visibility:hidden;opacity:0;transition:visibility 0s 1s,opacity 1s linear}.github-links{width:610px;max-width:calc(100vw - 30px);display:flex;justify-content:space-evenly;flex-wrap:wrap}.github-links span a{text-decoration:none;outline:0}.github-links span .widget{display:inline-block;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif;font-size:0;line-height:0;white-space:nowrap}.github-links span .btn,.github-links span .social-count{position:relative;display:inline-block;display:inline-flex;height:14px;padding:2px 5px;font-size:11px;font-weight:600;line-height:14px;vertical-align:bottom;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-repeat:repeat-x;background-position:-1px -1px;background-size:110% 110%;border:1px solid}.github-links span .btn{border-radius:.25em}.github-links span .btn:not(:last-child){border-radius:.25em 0 0 .25em}.github-links span .social-count{border-left:0;border-radius:0 .25em .25em 0}.github-links span .widget-lg .btn,.github-links span .widget-lg .social-count{height:16px;padding:5px 10px;font-size:12px;line-height:16px}.github-links span .octicon{display:inline-block;vertical-align:text-top;fill:currentColor;overflow:visible}.github-links span .btn:focus-visible,.github-links span .social-count:focus-visible{outline:2px solid #0969da;outline-offset:-2px}.github-links span .btn{color:#24292f;background-color:#ebf0f4;border-color:#ccd1d5;border-color:rgba(27,31,36,.15);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3clinearGradient id='o' x2='0' y2='1'%3e%3cstop stop-color='%23f6f8fa'/%3e%3cstop offset='90%25' stop-color='%23ebf0f4'/%3e%3c/linearGradient%3e%3crect width='100%25' height='100%25' fill='url(%23o)'/%3e%3c/svg%3e");background-image:-moz-linear-gradient(top, #f6f8fa, #ebf0f4 90%);background-image:linear-gradient(180deg, #f6f8fa, #ebf0f4 90%);filter:progid:DXImageTransform.Microsoft.Gradient(startColorstr='#FFF6F8FA', endColorstr='#FFEAEFF3')}.github-links span :root .btn{filter:none}.github-links span .btn:hover,.github-links span .btn:focus{background-color:#e9ebef;background-position:0 -0.5em;border-color:#caccd1;border-color:rgba(27,31,36,.15);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3clinearGradient id='o' x2='0' y2='1'%3e%3cstop stop-color='%23f3f4f6'/%3e%3cstop offset='90%25' stop-color='%23e9ebef'/%3e%3c/linearGradient%3e%3crect width='100%25' height='100%25' fill='url(%23o)'/%3e%3c/svg%3e");background-image:-moz-linear-gradient(top, #f3f4f6, #e9ebef 90%);background-image:linear-gradient(180deg, #f3f4f6, #e9ebef 90%);filter:progid:DXImageTransform.Microsoft.Gradient(startColorstr='#FFF3F4F6', endColorstr='#FFE8EAEE')}.github-links span :root .btn:hover,.github-links span :root .btn:focus{filter:none}.github-links span .btn:active{background-color:#e5e9ed;border-color:#c7cbcf;border-color:rgba(27,31,36,.15);box-shadow:inset 0 .15em .3em rgba(27,31,36,.15);background-image:none;filter:none}.github-links span .social-count{color:#24292f;background-color:#fff;border-color:#ddddde;border-color:rgba(27,31,36,.15)}.github-links span .social-count:hover,.github-links span .social-count:focus{color:#0969da}.github-links span .octicon-heart{color:#bf3989}@media(prefers-color-scheme: light){.github-links span .btn:focus-visible,.github-links span .social-count:focus-visible{outline:2px solid #0969da;outline-offset:-2px}.github-links span .btn{color:#24292f;background-color:#ebf0f4;border-color:#ccd1d5;border-color:rgba(27,31,36,.15);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3clinearGradient id='o' x2='0' y2='1'%3e%3cstop stop-color='%23f6f8fa'/%3e%3cstop offset='90%25' stop-color='%23ebf0f4'/%3e%3c/linearGradient%3e%3crect width='100%25' height='100%25' fill='url(%23o)'/%3e%3c/svg%3e");background-image:-moz-linear-gradient(top, #f6f8fa, #ebf0f4 90%);background-image:linear-gradient(180deg, #f6f8fa, #ebf0f4 90%);filter:progid:DXImageTransform.Microsoft.Gradient(startColorstr='#FFF6F8FA', endColorstr='#FFEAEFF3')}.github-links span :root .btn{filter:none}.github-links span .btn:hover,.github-links span .btn:focus{background-color:#e9ebef;background-position:0 -0.5em;border-color:#caccd1;border-color:rgba(27,31,36,.15);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3clinearGradient id='o' x2='0' y2='1'%3e%3cstop stop-color='%23f3f4f6'/%3e%3cstop offset='90%25' stop-color='%23e9ebef'/%3e%3c/linearGradient%3e%3crect width='100%25' height='100%25' fill='url(%23o)'/%3e%3c/svg%3e");background-image:-moz-linear-gradient(top, #f3f4f6, #e9ebef 90%);background-image:linear-gradient(180deg, #f3f4f6, #e9ebef 90%);filter:progid:DXImageTransform.Microsoft.Gradient(startColorstr='#FFF3F4F6', endColorstr='#FFE8EAEE')}.github-links span :root .btn:hover,.github-links span :root .btn:focus{filter:none}.github-links span .btn:active{background-color:#e5e9ed;border-color:#c7cbcf;border-color:rgba(27,31,36,.15);box-shadow:inset 0 .15em .3em rgba(27,31,36,.15);background-image:none;filter:none}.github-links span .social-count{color:#24292f;background-color:#fff;border-color:#ddddde;border-color:rgba(27,31,36,.15)}.github-links span .social-count:hover,.github-links span .social-count:focus{color:#0969da}.github-links span .octicon-heart{color:#bf3989}}@media(prefers-color-scheme: dark){.github-links span .btn:focus-visible,.github-links span .social-count:focus-visible{outline:2px solid #58a6ff;outline-offset:-2px}.github-links span .btn{color:#c9d1d9;background-color:#1a1e23;border-color:#2f3439;border-color:rgba(240,246,252,.1);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3clinearGradient id='o' x2='0' y2='1'%3e%3cstop stop-color='%2321262d'/%3e%3cstop offset='90%25' stop-color='%231a1e23'/%3e%3c/linearGradient%3e%3crect width='100%25' height='100%25' fill='url(%23o)'/%3e%3c/svg%3e");background-image:-moz-linear-gradient(top, #21262d, #1a1e23 90%);background-image:linear-gradient(180deg, #21262d, #1a1e23 90%);filter:progid:DXImageTransform.Microsoft.Gradient(startColorstr='#FF21262D', endColorstr='#FF191D22')}.github-links span :root .btn{filter:none}.github-links span .btn:hover,.github-links span .btn:focus{background-color:#292e33;background-position:0 -0.5em;border-color:#8b949e;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3clinearGradient id='o' x2='0' y2='1'%3e%3cstop stop-color='%2330363d'/%3e%3cstop offset='90%25' stop-color='%23292e33'/%3e%3c/linearGradient%3e%3crect width='100%25' height='100%25' fill='url(%23o)'/%3e%3c/svg%3e");background-image:-moz-linear-gradient(top, #30363d, #292e33 90%);background-image:linear-gradient(180deg, #30363d, #292e33 90%);filter:progid:DXImageTransform.Microsoft.Gradient(startColorstr='#FF30363D', endColorstr='#FF282D32')}.github-links span :root .btn:hover,.github-links span :root .btn:focus{filter:none}.github-links span .btn:active{background-color:#161719;border-color:#8b949e;box-shadow:inset 0 .15em .3em rgba(1,4,9,.15);background-image:none;filter:none}.github-links span .social-count{color:#c9d1d9;background-color:#0d1117;border-color:#24282e;border-color:rgba(240,246,252,.1)}.github-links span .social-count:hover,.github-links span .social-count:focus{color:#58a6ff}.github-links span .octicon-heart{color:#db61a2}}#share-link-copied{color:#990;display:none}#share-link-instructions{display:none}body.kiosk #loading .instructions{display:none !important}.kiosk>*:not(#divTwc){display:none !important}#divInfo{display:grid;grid-template-columns:1fr 1fr;max-width:250px}.weather-display{width:640px;height:480px;overflow:hidden;position:relative;background-image:var(--theme-background-1);height:0px}.weather-display.show{height:480px}.weather-display .template{display:none}.weather-display .header{width:640px;height:60px;padding-top:30px;position:relative;z-index:20}.weather-display .header .title{color:#ff0;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;font-family:"Star4000";font-size:24pt;position:absolute;width:250px}.weather-display .header .title.single{left:170px;top:25px}.weather-display .header .title.dual{left:170px}.weather-display .header .title.dual>div{position:absolute}.weather-display .header .title.dual .top{top:-3px}.weather-display .header .title.dual .bottom{top:26px}.weather-display .header .logo{top:30px;left:50px;position:absolute;z-index:10}.weather-display .header .noaa-logo{position:absolute;top:39px;left:356px}.weather-display .header .title.single{top:40px}.weather-display .header .date-time{white-space:pre;color:#fff;font-family:"Star4000 Small";font-size:24pt;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;left:436px;width:170px;text-align:right;position:absolute}.weather-display .header .date-time.date{padding-top:22px}.weather-display .main{position:relative}.weather-display .main.has-scroll{width:640px;margin-top:0;height:320px;overflow:hidden}.weather-display .main.has-scroll.no-header{height:400px;margin-top:0}.weather-display .main.has-box{margin-left:64px;margin-right:64px;width:calc(100% - 128px)}#container>.scroll{display:none;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;width:640px;height:77px;overflow:hidden;margin-top:3px;position:absolute;bottom:0px;z-index:1}#container>.scroll.hazard{background-color:#702323}#container>.scroll .scroll-container{width:640px}#container>.scroll .scroll-container .fixed,#container>.scroll .scroll-container .scroll-header{margin-left:55px;margin-right:55px;overflow:hidden;white-space:nowrap}#container>.scroll .scroll-container .scroll-header{height:26px;font-family:"Star4000 Small";font-size:20pt;margin-top:-10px}#container>.scroll .scroll-container .fixed{font-family:"Star4000";font-size:24pt}#container>.scroll .scroll-container .fixed .scroll-area{text-wrap:nowrap;position:relative}.radar #container>.scroll{z-index:100}.radar #container>.scroll.hazard{z-index:1000 !important}.wide #container>.scroll{width:854px;margin-left:-107px}.wide #container>.scroll .scroll-container{margin-left:107px}.weather-display .main.current-weather.main .col{height:50px;width:255px;display:inline-block;margin-top:10px;padding-top:10px;position:absolute;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .main.current-weather.main .col.left{font-family:"Star4000 Extended";font-size:18pt}.weather-display .main.current-weather.main .col.right{right:0px;font-family:"Star4000 Large";font-size:18px;font-weight:bold;line-height:24px}.weather-display .main.current-weather.main .col.right .row{margin-bottom:12px}.weather-display .main.current-weather.main .col.right .row .label,.weather-display .main.current-weather.main .col.right .row .value{display:inline-block}.weather-display .main.current-weather.main .col.right .row .label{margin-left:20px}.weather-display .main.current-weather.main .col.right .row .value{float:right;margin-right:10px}.weather-display .main.current-weather.main .center{text-align:center}.weather-display .main.current-weather.main .temp{font-family:"Star4000 Large";font-size:24pt}.weather-display .main.current-weather.main .icon img{margin:0 auto;display:block;width:108px}.weather-display .main.current-weather.main .wind-container{margin-left:10px;display:flex}.weather-display .main.current-weather.main .wind-container>div{width:50%}.weather-display .main.current-weather.main .wind-container .wind{text-align:right}.weather-display .main.current-weather.main .wind-gusts{text-align:right;font-size:28px}.weather-display .main.current-weather.main .location{color:#ff0;max-height:32px;margin-bottom:10px;padding-top:4px;overflow:hidden;text-wrap:nowrap;padding-left:15px}:root[data-theme]:not([data-theme=default]) .weather-display .main.current-weather.main .col.left{margin-top:35px}:root[data-theme]:not([data-theme=default]) .weather-display .main.current-weather.main .col.right{margin-top:36px;padding:21px}:root[data-theme]:not([data-theme=default]) .weather-display .main.current-weather.main .wind-container{display:block}:root[data-theme]:not([data-theme=default]) .weather-display .main.current-weather.main .wind-container>div{width:50%}:root[data-theme]:not([data-theme=default]) .weather-display .main.current-weather.main .wind-container .wind{text-align:right;font-size:22px;display:contents}.weather-display .main.latest-observations.main{padding-top:18px}.weather-display .main.latest-observations.main .column-headers{display:flex;font-family:"Star4000";font-size:14pt;font-weight:bold;color:#ff0;width:70%;margin:8px auto 10px;padding-top:20px;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .main.latest-observations.main .column-headers .city{width:30%}.weather-display .main.latest-observations.main .column-headers .temp{width:15%;text-align:center}.weather-display .main.latest-observations.main .column-headers .conditions{width:30%;text-align:center}.weather-display .main.latest-observations.main .column-headers .wind{width:25%;text-align:right;padding-right:4px}.weather-display .main.latest-observations.main .observation-lines{width:70%;margin:0 auto}.weather-display .main.latest-observations.main .observation-lines .observation-row{display:flex;font-family:"Star4000";font-size:14pt;line-height:1.4;margin-bottom:4px;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .main.latest-observations.main .observation-lines .observation-row.template{display:none}.weather-display .main.latest-observations.main .observation-lines .observation-row .city{width:30%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#fff}.weather-display .main.latest-observations.main .observation-lines .observation-row .temp{width:15%;text-align:center;color:#ff0}.weather-display .main.latest-observations.main .observation-lines .observation-row .conditions{width:30%;text-align:center;color:#fff}.weather-display .main.latest-observations.main .observation-lines .observation-row .wind{width:25%;text-align:right;padding-right:4px;color:#fff}#extended-forecast-html.weather-display{background-image:var(--theme-background-2)}.weather-display .main.extended-forecast .day-container{margin-top:16px;margin-left:27px}.weather-display .main.extended-forecast .day{text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;padding:5px;height:285px;width:155px;display:inline-block;margin:0px 15px;font-family:"Star4000";font-size:24pt}.weather-display .main.extended-forecast .day .date{text-transform:uppercase;text-align:center;color:#ff0}.weather-display .main.extended-forecast .day .condition{text-align:center;height:74px;margin-top:5px}.weather-display .main.extended-forecast .day .icon{text-align:center;height:75px}.weather-display .main.extended-forecast .day .icon img{max-height:75px}.weather-display .main.extended-forecast .day .temperatures{width:100%}.weather-display .main.extended-forecast .day .temperatures .temperature-block{display:inline-block;width:44%;vertical-align:top}.weather-display .main.extended-forecast .day .temperatures .temperature-block>div{text-align:center}.weather-display .main.extended-forecast .day .temperatures .temperature-block .value{font-family:"Star4000 Large";margin-top:4px}.weather-display .main.extended-forecast .day .temperatures .temperature-block.lo .label{color:#8080ff}.weather-display .main.extended-forecast .day .temperatures .temperature-block.hi .label{color:#ff0}.weather-display .main.hourly.main{overflow-y:hidden}.weather-display .main.hourly.main .column-headers{background-color:#200057;height:20px;position:absolute;width:100%}.weather-display .main.hourly.main .column-headers{position:sticky;top:0px;z-index:5}.weather-display .main.hourly.main .column-headers div{display:inline-block;font-family:"Star4000 Small";font-size:24pt;color:#ff0;position:absolute;top:-14px;z-index:5;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .main.hourly.main .column-headers .temp{left:355px}.weather-display .main.hourly.main .column-headers .like{left:435px}.weather-display .main.hourly.main .column-headers .wind{left:535px}.weather-display .main.hourly.main .hourly-lines{min-height:338px;padding-top:10px;background:#0b0b39}.weather-display .main.hourly.main .hourly-lines .hourly-row{font-family:"Star4000 Large";font-size:24pt;height:72px;color:#ff0;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;position:relative}.weather-display .main.hourly.main .hourly-lines .hourly-row>div{position:absolute;white-space:pre;top:8px}.weather-display .main.hourly.main .hourly-lines .hourly-row .hour{left:25px}.weather-display .main.hourly.main .hourly-lines .hourly-row .icon{left:255px;width:70px;text-align:center;top:unset}.weather-display .main.hourly.main .hourly-lines .hourly-row .temp{left:355px}.weather-display .main.hourly.main .hourly-lines .hourly-row .like{left:425px}.weather-display .main.hourly.main .hourly-lines .hourly-row .like.heat-index{color:#e00}.weather-display .main.hourly.main .hourly-lines .hourly-row .like.wind-chill{color:#8080ff}.weather-display .main.hourly.main .hourly-lines .hourly-row .wind{left:505px;width:100px;text-align:right}#hourly-graph-html{background-image:var(--theme-background-1-chart)}#hourly-graph-html .header .right{position:absolute;top:35px;right:60px;width:360px;font-family:"Star4000 Small";font-size:28px;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;text-align:right}#hourly-graph-html .header .right div{margin-top:-18px}#hourly-graph-html .header .right .temperature{color:red}#hourly-graph-html .header .right .dewpoint{color:green}#hourly-graph-html .header .right .cloud{color:#d3d3d3}#hourly-graph-html .header .right .rain{color:aqua}.weather-display .main.hourly-graph.main>div{position:absolute}.weather-display .main.hourly-graph.main .label{font-family:"Star4000 Small";font-size:24pt;color:#ff0;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;margin-top:-15px;position:absolute}.weather-display .main.hourly-graph.main .x-axis{bottom:0px;left:54px;width:532px;height:20px}.weather-display .main.hourly-graph.main .x-axis .label{text-align:center;transform:translateX(-50%);white-space:nowrap}.weather-display .main.hourly-graph.main .x-axis .label.l-1{left:0px}.weather-display .main.hourly-graph.main .x-axis .label.l-2{left:133px}.weather-display .main.hourly-graph.main .x-axis .label.l-3{left:266px}.weather-display .main.hourly-graph.main .x-axis .label.l-4{left:399px}.weather-display .main.hourly-graph.main .x-axis .label.l-5{left:532px}.weather-display .main.hourly-graph.main .chart{top:0px;left:50px}.weather-display .main.hourly-graph.main .chart img{width:532px;height:285px}.weather-display .main.hourly-graph.main .y-axis{top:0px;left:0px;width:50px;height:285px}.weather-display .main.hourly-graph.main .y-axis .label{text-align:right;right:0px}.weather-display .main.hourly-graph.main .y-axis .label.l-1{top:0px}.weather-display .main.hourly-graph.main .y-axis .label.l-2{top:93.3333333333px}.weather-display .main.hourly-graph.main .y-axis .label.l-3{bottom:82.3333333333px}.weather-display .main.hourly-graph.main .y-axis .label.l-4{bottom:0px}.weather-display .main.hourly-graph.main .column-headers{background-color:#200057;height:20px;position:absolute;width:100%}.weather-display .main.hourly-graph.main .column-headers{position:sticky;top:0px;z-index:5}.weather-display .main.hourly-graph.main .column-headers .temp{left:355px}.weather-display .main.hourly-graph.main .column-headers .like{left:435px}.weather-display .main.hourly-graph.main .column-headers .wind{left:535px}.weather-display .main.travel.main{overflow-y:hidden}.weather-display .main.travel.main .column-headers{background-color:#200057;height:20px;position:sticky;top:0px;width:100%;z-index:5;overflow:hidden}.weather-display .main.travel.main .column-headers div{display:inline-block;font-family:"Star4000 Small";font-size:24pt;color:#ff0;position:absolute;top:-14px;z-index:5;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .main.travel.main .column-headers .temp{width:50px;text-align:center}.weather-display .main.travel.main .column-headers .temp.low{left:455px}.weather-display .main.travel.main .column-headers .temp.high{left:510px;width:60px}.weather-display .main.travel.main .travel-lines{min-height:338px;padding-top:10px;background:#0b0b39}.weather-display .main.travel.main .travel-lines .travel-row{font-family:"Star4000 Large";font-size:24pt;height:72px;color:#ff0;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;position:relative}.weather-display .main.travel.main .travel-lines .travel-row>div{position:absolute;white-space:pre;top:8px}.weather-display .main.travel.main .travel-lines .travel-row .city{left:80px}.weather-display .main.travel.main .travel-lines .travel-row .icon{left:330px;width:70px;text-align:center;top:unset}.weather-display .main.travel.main .travel-lines .travel-row .icon img{max-width:47px}.weather-display .main.travel.main .travel-lines .travel-row .temp{width:50px;text-align:center}.weather-display .main.travel.main .travel-lines .travel-row .temp.low{left:455px}.weather-display .main.travel.main .travel-lines .travel-row .temp.high{left:510px;width:60px}.weather-display .local-forecast .container{position:relative;top:15px;margin:0px 10px;box-sizing:border-box;height:280px;overflow:hidden}.weather-display .local-forecast .forecasts{position:relative}.weather-display .local-forecast .forecast{font-family:"Star4000";font-size:24pt;text-transform:uppercase;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;min-height:280px;line-height:40px}:root[data-theme]:not([data-theme=default]) .weather-display .local-forecast .forecasts{margin-top:16px}:root[data-theme]:not([data-theme=default]) .weather-display .local-forecast .forecast{font-size:24px;padding-right:20px}.weather-display .progress{text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;font-family:"Star4000 Extended";font-size:18pt}.weather-display .progress .container{position:relative;top:15px;margin:0px 10px;box-sizing:border-box;height:310px;overflow:hidden;line-height:26px}.weather-display .progress .container .item{position:relative}.weather-display .progress .container .item .name{white-space:nowrap}.weather-display .progress .container .item .name::after{content:"........................................................................"}.weather-display .progress .container .item .links{position:absolute;text-align:right;right:0px;top:0px}.weather-display .progress .container .item .links>div{background-color:#26235a;display:none;padding-left:4px}.weather-display .progress .container .item .links .loading,.weather-display .progress .container .item .links .retrying{color:#ff0}.weather-display .progress .container .item .links .press-here{color:lime;cursor:pointer}.weather-display .progress .container .item .links .failed{color:red}.weather-display .progress .container .item .links .no-data{color:silver}.weather-display .progress .container .item .links .disabled{color:silver}.weather-display .progress .container .item .links.loading .loading,.weather-display .progress .container .item .links.press-here .press-here,.weather-display .progress .container .item .links.failed .failed,.weather-display .progress .container .item .links.no-data .no-data,.weather-display .progress .container .item .links.disabled .disabled,.weather-display .progress .container .item .links.retrying .retrying{display:block}@keyframes progress-scroll{0%{background-position:-40px 0}100%{background-position:40px 0}}#progress-html.weather-display .scroll .progress-bar-container{border:2px solid #000;background-color:#fff;margin:20px auto;width:524px;position:relative;display:none}#progress-html.weather-display .scroll .progress-bar-container.show{display:block}#progress-html.weather-display .scroll .progress-bar-container .progress-bar{height:20px;margin:2px;width:520px;background:repeating-linear-gradient(90deg, #09246f 0px, #09246f 5px, #364ac0 5px, #364ac0 10px, #4f99f9 10px, #4f99f9 15px, #8ffdfa 15px, #8ffdfa 20px, #4f99f9 20px, #4f99f9 25px, #364ac0 25px, #364ac0 30px, #09246f 30px, #09246f 40px);animation-duration:2s;animation-fill-mode:forwards;animation-iteration-count:infinite;animation-name:progress-scroll;animation-timing-function:steps(8, end)}#progress-html.weather-display .scroll .progress-bar-container .cover{position:absolute;top:0px;right:0px;background-color:#fff;width:100%;height:24px;transition:width 1s steps(6)}#radar-html.weather-display{background-image:var(--theme-background-4)}#radar-html.weather-display .header{height:83px}#radar-html.weather-display .header .title.dual{color:#fff;font-family:"Arial",sans-serif;font-weight:bold;font-size:28pt;left:155px}#radar-html.weather-display .header .title.dual .top{top:-4px}#radar-html.weather-display .header .title.dual .bottom{top:31px}#radar-html.weather-display .header .right{position:absolute;right:0px;width:360px;margin-top:2px;font-family:"Star4000";font-size:18pt;font-weight:bold;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;text-align:center}#radar-html.weather-display .header .right .scale-table{display:flex;justify-content:center;gap:4px}#radar-html.weather-display .header .right .scale-table .item{display:flex;flex-direction:column;align-items:center;width:25px;gap:2px}#radar-html.weather-display .header .right .scale-table .box{display:block;border:2.7px solid #000;width:90%;height:12px;margin-top:4px;padding:0}#radar-html.weather-display .header .right .scale-table .box-1{background-color:#49bef6}#radar-html.weather-display .header .right .scale-table .box-2{background-color:#31d216}#radar-html.weather-display .header .right .scale-table .box-3{background-color:#f1e458}#radar-html.weather-display .header .right .scale-table .box-4{background-color:#e08e2f}#radar-html.weather-display .header .right .scale-table .box-5{background-color:#c42a2a}#radar-html.weather-display .header .right .scale-table .box-6{background-color:#913bb8}#radar-html.weather-display .header .right .scale-table .label{font-family:"Star4000 Small";font-size:10pt;line-height:1;white-space:nowrap}#radar-html.weather-display .header .right .scale{margin-top:-2px}#radar-html.weather-display .header .right .time{position:relative;font-weight:normal;top:-20px;font-family:"Star4000 Small";font-size:18pt;left:132px}.weather-display .main.radar{overflow:hidden;height:367px}.weather-display .main.radar .container{position:relative;height:100%}.weather-display .main.radar .container .scroll-area{position:relative;height:100%}.weather-display .main.radar .container .frame{height:100%}.weather-display .main.radar .container .map{height:100%;width:100%}.weather-display .main.radar .container .leaflet-map{height:100%;width:100%;background:#061f3e}.weather-display .main.radar .container .leaflet-container{background:#061f3e;font-family:inherit}.weather-display .main.radar .container .radar-base-layer,.weather-display .main.radar .container .radar-base-layer .leaflet-tile{filter:grayscale(0.35) brightness(0.58) contrast(1.1) saturate(0.2)}.weather-display .main.radar .container .radar-boundary-layer,.weather-display .main.radar .container .radar-boundary-layer .leaflet-tile{filter:grayscale(0.8) brightness(0.7) contrast(1.3) saturate(0.1)}.weather-display .main.radar .container .leaflet-control-container,.weather-display .main.radar .container .leaflet-control-attribution,.weather-display .main.radar .container .leaflet-control-zoom{display:none}.weather-display .main.radar .container .location-marker{background:#ff0;border:2px solid #000;border-radius:50%}.weather-display .main.radar .container .nearby-weather-marker{display:none;background:rgba(0,0,0,0);border:0}.weather-display .main.radar .container .nearby-weather-marker .nearby-weather-marker-inner{display:inline-flex;flex-direction:column;align-items:center;min-width:72px;padding:2px 4px;background:rgba(18,34,61,0);color:#fff;text-align:center;opacity:.35}.weather-display .main.radar .container .nearby-weather-marker .city{font-family:"Star4000 Small";font-size:11pt;line-height:1;white-space:nowrap;margin-bottom:1px;text-shadow:1px 1px 0 #000;display:none}.weather-display .main.radar .container .nearby-weather-marker .details{display:flex;align-items:center;gap:2px}.weather-display .main.radar .container .nearby-weather-marker .temp{font-family:"Star4000";font-size:18pt;line-height:1;color:#ff0;text-shadow:1px 1px 0 #000}.weather-display .main.radar .container .nearby-weather-marker img{width:auto;height:20px}.wide.radar #container{background:url(../images/backgrounds/4-wide.png)}#regional-forecast-html.weather-display{background-image:var(--theme-background-5)}.weather-display .main.regional-forecast{position:relative;overflow:hidden;z-index:0}.weather-display .main.regional-forecast .map{position:absolute;inset:0}.weather-display .main.regional-forecast .leaflet-map{height:100%;width:100%;background:#061f3e}.weather-display .main.regional-forecast .leaflet-container{background:#061f3e;font-family:inherit}.weather-display .main.regional-forecast .radar-base-layer,.weather-display .main.regional-forecast .radar-base-layer .leaflet-tile{filter:grayscale(0.35) brightness(0.58) contrast(1.1) saturate(0.2)}.weather-display .main.regional-forecast .radar-boundary-layer,.weather-display .main.regional-forecast .radar-boundary-layer .leaflet-tile{filter:grayscale(0.8) brightness(0.7) contrast(1.3) saturate(0.1)}.weather-display .main.regional-forecast .leaflet-control-container,.weather-display .main.regional-forecast .leaflet-control-attribution,.weather-display .main.regional-forecast .leaflet-control-zoom{display:none}.weather-display .main.regional-forecast .location-marker{background:#ff0;border:2px solid #000;border-radius:50%}.weather-display .main.regional-forecast .nearby-weather-marker{background:rgba(0,0,0,0);border:0}.weather-display .main.regional-forecast .nearby-weather-marker .nearby-weather-marker-inner{display:inline-flex;flex-direction:column;align-items:center;min-width:72px;color:#fff;text-align:center;opacity:.75}.weather-display .main.regional-forecast .nearby-weather-marker .city{display:none}.weather-display .main.regional-forecast .nearby-weather-marker .details{display:flex;align-items:center;gap:2px}.weather-display .main.regional-forecast .nearby-weather-marker .temp{font-family:"Star4000";font-size:21pt;line-height:1;color:#ff0;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .main.regional-forecast .nearby-weather-marker img{width:auto;height:32px}#almanac-html.weather-display{background-image:var(--theme-background-3)}.weather-display .main.almanac{font-family:"Star4000";font-size:24pt;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .main.almanac .sun{display:grid;grid-template-columns:auto auto auto;grid-template-rows:auto auto auto;gap:0px 90px;margin:3px auto 5px auto;width:fit-content;line-height:30px}.weather-display .main.almanac .sun .grid-item{width:auto;height:auto;padding:0;margin:0;position:relative}.weather-display .main.almanac .sun .grid-item.header{color:#ff0;text-align:center}.weather-display .main.almanac .sun .grid-item.row-label{text-align:right}.weather-display .main.almanac .sun .grid-item.time{text-align:center}.weather-display .main.almanac .moon{position:relative;padding:7px 50px;line-height:36px}.weather-display .main.almanac .moon .title{color:#ff0;padding-left:13px}.weather-display .main.almanac .moon .day{display:inline-block;text-align:center;width:132px}.weather-display .main.almanac .moon .day .icon{padding-left:10px}.weather-display .main.almanac .moon .day .date{position:relative;top:-10px}#hazards-html.weather-display{background-image:url("../images/backgrounds/7.png")}.weather-display .main.hazards.main{overflow-y:hidden;height:480px;background-color:#702323}.weather-display .main.hazards.main .hazard-lines{min-height:400px;padding-top:10px}.weather-display .main.hazards.main .hazard-lines .hazard{font-family:"Star4000";font-size:24pt;color:#fff;text-shadow:0px 0px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;position:relative;text-transform:uppercase;margin-top:10px;margin-left:80px;margin-right:80px;padding-bottom:10px}.wide.hazards #container{background:url(../images/backgrounds/7-wide.png)}.media{display:none}#ToggleMediaContainer{display:none;position:relative}#ToggleMediaContainer.available{display:inline-block}#ToggleMediaContainer.available img.on{display:none}#ToggleMediaContainer.available img.off{display:block}#ToggleMediaContainer.available.playing img.on{display:block}#ToggleMediaContainer.available.playing img.off{display:none}#ToggleMediaContainer .volume-slider{display:none;position:absolute;top:0px;transform:translateY(-100%);width:100%;background-color:#000;text-align:center;z-index:100}@media(prefers-color-scheme: dark){#ToggleMediaContainer .volume-slider{background-color:#303030}}#ToggleMediaContainer .volume-slider input[type=range]{writing-mode:vertical-lr;direction:rtl;margin-top:20px;margin-bottom:20px}#ToggleMediaContainer .volume-slider.show{display:block}#spc-outlook-html.weather-display{background-image:var(--theme-background-6)}.weather-display .spc-outlook .container{position:relative;top:0px;margin:0px 10px;box-sizing:border-box;height:300px;overflow:hidden}.weather-display .spc-outlook .risk-levels{position:absolute;left:206px;font-family:"Star4000 Small";font-size:32px;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .spc-outlook .risk-levels .risk-level{position:relative;top:-14px;height:20px}.weather-display .spc-outlook .risk-levels .risk-level:nth-child(1){left:100px}.weather-display .spc-outlook .risk-levels .risk-level:nth-child(2){left:80px}.weather-display .spc-outlook .risk-levels .risk-level:nth-child(3){left:60px}.weather-display .spc-outlook .risk-levels .risk-level:nth-child(4){left:40px}.weather-display .spc-outlook .risk-levels .risk-level:nth-child(5){left:20px}.weather-display .spc-outlook .risk-levels .risk-level:nth-child(6){left:0px}.weather-display .spc-outlook .days{position:absolute;top:120px}.weather-display .spc-outlook .days .day{height:60px}.weather-display .spc-outlook .days .day .day-name{position:absolute;font-family:"Star4000";font-size:24pt;width:200px;text-align:right;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;padding-top:20px}.weather-display .spc-outlook .days .day .risk-bar{position:absolute;width:150px;height:40px;left:210px;margin-top:20px;border:3px outset hsl(0,0%,70%);background:linear-gradient(0deg, hsl(0, 0%, 40%) 0%, hsl(0, 0%, 60%) 50%, hsl(0, 0%, 40%) 100%)}#server-observations-html.weather-display .header .title.single{font-size:20pt}.weather-display .server-observations.main{height:auto !important;min-height:250px}.weather-display .server-observations .container{position:relative;top:15px;box-sizing:border-box;height:250px;overflow:hidden}.weather-display .server-observations .server-output{position:relative;font-family:"Star4000";font-size:20pt;line-height:32px;color:#fff;text-transform:uppercase;text-align:center;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .server-observations .server-output .server-page{height:250px;padding:0 8px;box-sizing:border-box;margin-top:16px;padding-right:30px}.weather-display .server-observations .server-output .server-line{white-space:normal;overflow-wrap:anywhere;word-break:break-word;margin-bottom:6px}.weather-display .linux-news.main{height:auto !important;min-height:250px}.weather-display .linux-news .container{position:relative;top:15px;margin:0px 10px;box-sizing:border-box;height:250px;overflow:hidden}.weather-display .linux-news .news-output{position:relative}.weather-display .linux-news .news-output .news-page{height:250px;box-sizing:border-box;padding:0 8px;display:flex;flex-direction:column;justify-content:space-between}.weather-display .linux-news .news-output .story{height:116px;overflow:hidden;margin-top:20px}.weather-display .linux-news .news-output .headline{font-family:"Star4000";font-size:17pt;line-height:22px;color:#ff0;text-transform:uppercase;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;margin-bottom:4px}.weather-display .linux-news .news-output .blurb{font-family:"Star4000";font-size:12pt;line-height:10pt;color:#fff;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;overflow:hidden;padding-right:30px}.scanlines{position:relative;overflow:hidden;isolation:isolate}.scanlines #container{position:relative;z-index:1;transform:translateZ(0);will-change:filter;filter:blur(0.6px) saturate(1.12) contrast(1.02) brightness(1)}.scanlines #container::before,.scanlines #container::after{content:"";position:absolute;inset:0;pointer-events:none;z-index:3}.scanlines #container::before{background:linear-gradient(to right, rgba(255, 0, 0, 0.05) 0%, rgba(255, 0, 0, 0.01) 15%, rgba(255, 0, 0, 0) 50%, rgba(255, 0, 0, 0.01) 85%, rgba(255, 0, 0, 0.05) 100%);transform:translateX(-1px);filter:blur(1.8px);mix-blend-mode:screen}.scanlines #container::after{background:linear-gradient(to right, rgba(0, 140, 255, 0.05) 0%, rgba(0, 140, 255, 0.01) 15%, rgba(0, 140, 255, 0) 50%, rgba(0, 140, 255, 0.01) 85%, rgba(0, 140, 255, 0.05) 100%);transform:translateX(1px);filter:blur(1.8px);mix-blend-mode:screen}.scanlines:before,.scanlines:after{display:block;pointer-events:none;content:"";position:absolute;left:0;right:0}.scanlines:before{height:var(--scanline-thickness, 1px);z-index:2147483650;background:rgba(0,0,0,.3);opacity:.75;animation:scanline 6s linear infinite}.scanlines:after{top:0;bottom:0;z-index:2147483648;background:repeating-linear-gradient(to bottom, transparent 0, transparent var(--scanline-thickness, 1px), rgba(0, 0, 0, 0.3) var(--scanline-thickness, 1px), rgba(0, 0, 0, 0.3) calc(var(--scanline-thickness, 1px) * 2));animation:none}.scanlines{box-shadow:inset 0 0 80px rgba(0,0,0,.16),inset 0 0 18px hsla(0,0%,100%,.08)}.scanlines.crt-panels-only #container{filter:none}.scanlines.crt-panels-only .weather-display{filter:blur(0.6px) saturate(1.12) contrast(1.02) brightness(1);transform:translateZ(0)}.scanlines .header,.scanlines .main,.scanlines .scroll,.scanlines .date-time,.scanlines .city,.scanlines .temp,.scanlines .condition,.scanlines .location,.scanlines .label,.scanlines .value,.scanlines .title{text-shadow:0 0 1px hsla(0,0%,100%,.18),0 0 2px hsla(0,0%,100%,.06)}@keyframes scanline{0%{transform:translate3d(0, 200000%, 0)}}@keyframes scanlines{0%{background-position:0 50%}} \ No newline at end of file +@font-face{font-family:"Star4000";src:url("../fonts/Star4000.woff") format("woff");font-display:swap}body{font-family:"Star4000";margin:0}@media(prefers-color-scheme: dark){body{background-color:#000;color:#fff}}@media(prefers-color-scheme: dark){body a{color:#add8e6}}body.kiosk{margin:0px;padding:0px;overflow:hidden;width:100vw;background-color:#000 !important}#divQuery{max-width:640px;padding:8px}#divQuery .buttons{display:inline-block;width:150px;text-align:right}#divQuery .buttons #imgGetGps{height:13px;vertical-align:middle}#divQuery .buttons button{font-size:16pt;border:1px solid #a9a9a9}@media(prefers-color-scheme: dark){#divQuery .buttons button{background-color:#000;color:#fff}}#divQuery .buttons #btnGetGps img.dark{display:none}@media(prefers-color-scheme: dark){#divQuery .buttons #btnGetGps img.dark{display:inline-block}}@media(prefers-color-scheme: dark){#divQuery .buttons #btnGetGps img.light{display:none}}#divQuery .buttons #btnGetGps.active{background-color:#000}@media(prefers-color-scheme: dark){#divQuery .buttons #btnGetGps.active{background-color:#fff}}#divQuery .buttons #btnGetGps.active img{filter:invert(1)}#divQuery input,#divQuery button{font-family:"Star4000"}#divQuery #txtLocation{width:calc(100% - 170px);max-width:490px;font-size:16pt;min-width:200px;display:inline-block;background-color:#fff;color:#000;border:2px inset gray}@media(prefers-color-scheme: dark){#divQuery #txtLocation{background-color:#000;color:#fff;border:2px inset gray}}.autocomplete-suggestions{background-color:#fff;border:1px solid #000;position:absolute;z-index:9999}@media(prefers-color-scheme: dark){.autocomplete-suggestions{background-color:#000}}.autocomplete-suggestions div{white-space:nowrap;overflow:hidden;text-overflow:ellipsis;font-size:16pt}.autocomplete-suggestions div.selected{background-color:blue;color:#fff}#divTwc{display:block;background-color:#000;color:#fff;width:100%;max-width:640px;margin:0}#divTwc.wide{max-width:854px}.content-wrapper{padding:8px}#divTwcMain{width:640px;height:480px;position:relative}.wide #divTwcMain{width:854px}.kiosk #divTwc{max-width:unset}#divTwcLeft{display:none;text-align:right;flex-direction:column;vertical-align:middle}#divTwcLeft>div{flex:1;padding-right:12px;display:flex;flex-direction:column;justify-content:center}#divTwcRight{text-align:left;display:none;flex-direction:column;vertical-align:middle}#divTwcRight>div{flex:1;padding-left:12px;display:flex;flex-direction:column;justify-content:center}#divTwcBottom{display:flex;flex-direction:row;background-color:#000;color:#fff;width:640px}.wide #divTwcBottom{width:854px}@media(prefers-color-scheme: dark){#divTwcBottom{background-color:#303030}}#divTwcBottom>div{padding-left:6px;padding-right:6px}@media(max-width: 550px){#divTwcBottom>div{font-size:.9em}}@media(max-width: 500px){#divTwcBottom>div{font-size:.8em}}@media(max-width: 450px){#divTwcBottom>div{font-size:.7em}}@media(max-width: 400px){#divTwcBottom>div{font-size:.6em}}@media(max-width: 350px){#divTwcBottom>div{font-size:.5em}}#divTwcBottomLeft{flex:1;text-align:left}#divTwcBottomMiddle{flex:0;text-align:center}#divTwcBottomRight{flex:1;text-align:right}#divTwcNavContainer{display:none}#divTwcNav{width:100%;display:flex;flex-direction:row;background-color:#000;color:#fff;max-width:640px}#divTwcNav>div{padding-left:6px;padding-right:6px}#divTwcNavLeft{flex:1;text-align:left}#divTwcNavMiddle{flex:0;text-align:center}#divTwcNavRight{flex:1;text-align:right}#imgPause1x{visibility:hidden;position:absolute}.HideCursor{cursor:none !important}#txtScrollText{width:475px}@font-face{font-family:"Star4000 Extended";src:url("../fonts/Star4000 Extended.woff") format("woff");font-display:swap}@font-face{font-family:"Star4000 Large";src:url("../fonts/Star4000 Large.woff") format("woff");font-display:swap}@font-face{font-family:"Star4000 Small";src:url("../fonts/Star4000 Small.woff") format("woff");font-display:swap}:root{--theme-background-1: url('../images/backgrounds/1.png');--theme-background-1-chart: url('../images/backgrounds/1-chart.png');--theme-background-2: url('../images/backgrounds/2.png');--theme-background-3: url('../images/backgrounds/3.png');--theme-background-4: url('../images/backgrounds/4.png');--theme-background-5: url('../images/backgrounds/5.png');--theme-background-6: url('../images/backgrounds/6.png')}#display{font-family:"Star4000";margin:0 0 0 0;width:100%}#container{position:relative;width:640px;height:480px;background-image:var(--theme-background-1);transform-origin:0 0;background-repeat:no-repeat}.wide #container{padding-left:107px;padding-right:107px;background:url(../images/backgrounds/1-wide.png);background-repeat:no-repeat}#divTwc:fullscreen #container,.kiosk #divTwc #container{width:unset;height:unset}#loading{width:640px;height:480px;max-width:100%;text-shadow:4px 4px #000;display:flex;align-items:center;text-align:center;justify-content:center}#loading .title{font-family:Star4000 Large;font-size:36px;color:#ff0;margin-bottom:0px}#loading .version{margin-bottom:35px}#loading .instructions{font-size:18pt}.heading{font-weight:bold;margin-top:15px}#settings{margin-bottom:15px}#enabledDisplays,#settings{margin-bottom:15px}#enabledDisplays .loading,#enabledDisplays .retrying,#settings .loading,#settings .retrying{color:#ff0}#enabledDisplays .press-here,#settings .press-here{color:lime;cursor:pointer}#enabledDisplays .failed,#settings .failed{color:red}#enabledDisplays .no-data,#settings .no-data{color:silver}#enabledDisplays .disabled,#settings .disabled{color:silver}#enabledDisplays .press-here,#settings .press-here{color:#fff}@media(prefers-color-scheme: light){#enabledDisplays .loading,#enabledDisplays .retrying,#settings .loading,#settings .retrying{color:#990}#enabledDisplays .press-here,#settings .press-here{color:#000;cursor:pointer}#enabledDisplays .failed,#settings .failed{color:#900}#enabledDisplays .no-data,#settings .no-data{color:hsl(0,0%,30%)}#enabledDisplays .disabled,#settings .disabled{color:hsl(0,0%,30%)}}#enabledDisplays label,#settings label{display:block;max-width:fit-content;cursor:pointer}#enabledDisplays label .alert,#settings label .alert{display:none}#enabledDisplays label .alert.show,#settings label .alert.show{display:inline;color:red}#divTwcBottom img{transform:scale(0.75)}@media(max-width: 550px){.wide #divTwcBottom img{transform:scale(1)}}#divTwc:fullscreen,.kiosk #divTwc{display:flex;align-items:center;justify-content:center;align-content:center}#divTwc:fullscreen.no-cursor,.kiosk #divTwc.no-cursor{cursor:none}#divTwc:fullscreen #display,.kiosk #divTwc #display{position:relative}#divTwc:fullscreen #divTwcBottom,.kiosk #divTwc #divTwcBottom{display:flex;flex-direction:row;background-color:rgba(0,0,0,.5);color:#fff;width:100%;position:absolute;bottom:0px}.kiosk #divTwc #divTwcBottom{display:none}.navButton{cursor:pointer}#ToggleScanlines{display:inline-block}#ToggleScanlines .on{display:none}#ToggleScanlines .off{display:inline-block}#ToggleScanlines.on .on{display:inline-block}#ToggleScanlines.on .off{display:none}.visible{visibility:visible;opacity:1;transition:opacity .1s linear}#divTwc:fullscreen .hidden{visibility:hidden;opacity:0;transition:visibility 0s 1s,opacity 1s linear}.github-links{width:610px;max-width:calc(100vw - 30px);display:flex;justify-content:space-evenly;flex-wrap:wrap}.github-links span a{text-decoration:none;outline:0}.github-links span .widget{display:inline-block;overflow:hidden;font-family:-apple-system,BlinkMacSystemFont,Segoe UI,Helvetica,Arial,sans-serif;font-size:0;line-height:0;white-space:nowrap}.github-links span .btn,.github-links span .social-count{position:relative;display:inline-block;display:inline-flex;height:14px;padding:2px 5px;font-size:11px;font-weight:600;line-height:14px;vertical-align:bottom;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-repeat:repeat-x;background-position:-1px -1px;background-size:110% 110%;border:1px solid}.github-links span .btn{border-radius:.25em}.github-links span .btn:not(:last-child){border-radius:.25em 0 0 .25em}.github-links span .social-count{border-left:0;border-radius:0 .25em .25em 0}.github-links span .widget-lg .btn,.github-links span .widget-lg .social-count{height:16px;padding:5px 10px;font-size:12px;line-height:16px}.github-links span .octicon{display:inline-block;vertical-align:text-top;fill:currentColor;overflow:visible}.github-links span .btn:focus-visible,.github-links span .social-count:focus-visible{outline:2px solid #0969da;outline-offset:-2px}.github-links span .btn{color:#24292f;background-color:#ebf0f4;border-color:#ccd1d5;border-color:rgba(27,31,36,.15);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3clinearGradient id='o' x2='0' y2='1'%3e%3cstop stop-color='%23f6f8fa'/%3e%3cstop offset='90%25' stop-color='%23ebf0f4'/%3e%3c/linearGradient%3e%3crect width='100%25' height='100%25' fill='url(%23o)'/%3e%3c/svg%3e");background-image:-moz-linear-gradient(top, #f6f8fa, #ebf0f4 90%);background-image:linear-gradient(180deg, #f6f8fa, #ebf0f4 90%);filter:progid:DXImageTransform.Microsoft.Gradient(startColorstr='#FFF6F8FA', endColorstr='#FFEAEFF3')}.github-links span :root .btn{filter:none}.github-links span .btn:hover,.github-links span .btn:focus{background-color:#e9ebef;background-position:0 -0.5em;border-color:#caccd1;border-color:rgba(27,31,36,.15);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3clinearGradient id='o' x2='0' y2='1'%3e%3cstop stop-color='%23f3f4f6'/%3e%3cstop offset='90%25' stop-color='%23e9ebef'/%3e%3c/linearGradient%3e%3crect width='100%25' height='100%25' fill='url(%23o)'/%3e%3c/svg%3e");background-image:-moz-linear-gradient(top, #f3f4f6, #e9ebef 90%);background-image:linear-gradient(180deg, #f3f4f6, #e9ebef 90%);filter:progid:DXImageTransform.Microsoft.Gradient(startColorstr='#FFF3F4F6', endColorstr='#FFE8EAEE')}.github-links span :root .btn:hover,.github-links span :root .btn:focus{filter:none}.github-links span .btn:active{background-color:#e5e9ed;border-color:#c7cbcf;border-color:rgba(27,31,36,.15);box-shadow:inset 0 .15em .3em rgba(27,31,36,.15);background-image:none;filter:none}.github-links span .social-count{color:#24292f;background-color:#fff;border-color:#ddddde;border-color:rgba(27,31,36,.15)}.github-links span .social-count:hover,.github-links span .social-count:focus{color:#0969da}.github-links span .octicon-heart{color:#bf3989}@media(prefers-color-scheme: light){.github-links span .btn:focus-visible,.github-links span .social-count:focus-visible{outline:2px solid #0969da;outline-offset:-2px}.github-links span .btn{color:#24292f;background-color:#ebf0f4;border-color:#ccd1d5;border-color:rgba(27,31,36,.15);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3clinearGradient id='o' x2='0' y2='1'%3e%3cstop stop-color='%23f6f8fa'/%3e%3cstop offset='90%25' stop-color='%23ebf0f4'/%3e%3c/linearGradient%3e%3crect width='100%25' height='100%25' fill='url(%23o)'/%3e%3c/svg%3e");background-image:-moz-linear-gradient(top, #f6f8fa, #ebf0f4 90%);background-image:linear-gradient(180deg, #f6f8fa, #ebf0f4 90%);filter:progid:DXImageTransform.Microsoft.Gradient(startColorstr='#FFF6F8FA', endColorstr='#FFEAEFF3')}.github-links span :root .btn{filter:none}.github-links span .btn:hover,.github-links span .btn:focus{background-color:#e9ebef;background-position:0 -0.5em;border-color:#caccd1;border-color:rgba(27,31,36,.15);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3clinearGradient id='o' x2='0' y2='1'%3e%3cstop stop-color='%23f3f4f6'/%3e%3cstop offset='90%25' stop-color='%23e9ebef'/%3e%3c/linearGradient%3e%3crect width='100%25' height='100%25' fill='url(%23o)'/%3e%3c/svg%3e");background-image:-moz-linear-gradient(top, #f3f4f6, #e9ebef 90%);background-image:linear-gradient(180deg, #f3f4f6, #e9ebef 90%);filter:progid:DXImageTransform.Microsoft.Gradient(startColorstr='#FFF3F4F6', endColorstr='#FFE8EAEE')}.github-links span :root .btn:hover,.github-links span :root .btn:focus{filter:none}.github-links span .btn:active{background-color:#e5e9ed;border-color:#c7cbcf;border-color:rgba(27,31,36,.15);box-shadow:inset 0 .15em .3em rgba(27,31,36,.15);background-image:none;filter:none}.github-links span .social-count{color:#24292f;background-color:#fff;border-color:#ddddde;border-color:rgba(27,31,36,.15)}.github-links span .social-count:hover,.github-links span .social-count:focus{color:#0969da}.github-links span .octicon-heart{color:#bf3989}}@media(prefers-color-scheme: dark){.github-links span .btn:focus-visible,.github-links span .social-count:focus-visible{outline:2px solid #58a6ff;outline-offset:-2px}.github-links span .btn{color:#c9d1d9;background-color:#1a1e23;border-color:#2f3439;border-color:rgba(240,246,252,.1);background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3clinearGradient id='o' x2='0' y2='1'%3e%3cstop stop-color='%2321262d'/%3e%3cstop offset='90%25' stop-color='%231a1e23'/%3e%3c/linearGradient%3e%3crect width='100%25' height='100%25' fill='url(%23o)'/%3e%3c/svg%3e");background-image:-moz-linear-gradient(top, #21262d, #1a1e23 90%);background-image:linear-gradient(180deg, #21262d, #1a1e23 90%);filter:progid:DXImageTransform.Microsoft.Gradient(startColorstr='#FF21262D', endColorstr='#FF191D22')}.github-links span :root .btn{filter:none}.github-links span .btn:hover,.github-links span .btn:focus{background-color:#292e33;background-position:0 -0.5em;border-color:#8b949e;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg'%3e%3clinearGradient id='o' x2='0' y2='1'%3e%3cstop stop-color='%2330363d'/%3e%3cstop offset='90%25' stop-color='%23292e33'/%3e%3c/linearGradient%3e%3crect width='100%25' height='100%25' fill='url(%23o)'/%3e%3c/svg%3e");background-image:-moz-linear-gradient(top, #30363d, #292e33 90%);background-image:linear-gradient(180deg, #30363d, #292e33 90%);filter:progid:DXImageTransform.Microsoft.Gradient(startColorstr='#FF30363D', endColorstr='#FF282D32')}.github-links span :root .btn:hover,.github-links span :root .btn:focus{filter:none}.github-links span .btn:active{background-color:#161719;border-color:#8b949e;box-shadow:inset 0 .15em .3em rgba(1,4,9,.15);background-image:none;filter:none}.github-links span .social-count{color:#c9d1d9;background-color:#0d1117;border-color:#24282e;border-color:rgba(240,246,252,.1)}.github-links span .social-count:hover,.github-links span .social-count:focus{color:#58a6ff}.github-links span .octicon-heart{color:#db61a2}}#share-link-copied{color:#990;display:none}#share-link-instructions{display:none}body.kiosk #loading .instructions{display:none !important}.kiosk>*:not(#divTwc){display:none !important}#divInfo{display:grid;grid-template-columns:1fr 1fr;max-width:250px}.weather-display{width:640px;height:480px;overflow:hidden;position:relative;background-image:var(--theme-background-1);height:0px}.weather-display.show{height:480px}.weather-display .template{display:none}.weather-display .header{width:640px;height:60px;padding-top:30px;position:relative;z-index:20}.weather-display .header .title{color:#ff0;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;font-family:"Star4000";font-size:24pt;position:absolute;width:250px}.weather-display .header .title.single{left:170px;top:25px}.weather-display .header .title.dual{left:170px}.weather-display .header .title.dual>div{position:absolute}.weather-display .header .title.dual .top{top:-3px}.weather-display .header .title.dual .bottom{top:26px}.weather-display .header .logo{top:30px;left:50px;position:absolute;z-index:10}.weather-display .header .noaa-logo{position:absolute;top:39px;left:356px}.weather-display .header .title.single{top:40px}.weather-display .header .date-time{white-space:pre;color:#fff;font-family:"Star4000 Small";font-size:24pt;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;left:436px;width:170px;text-align:right;position:absolute}.weather-display .header .date-time.date{padding-top:22px}.weather-display .main{position:relative}.weather-display .main.has-scroll{width:640px;margin-top:0;height:320px;overflow:hidden}.weather-display .main.has-scroll.no-header{height:400px;margin-top:0}.weather-display .main.has-box{margin-left:64px;margin-right:64px;width:calc(100% - 128px)}#container>.scroll{display:none;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;width:640px;height:77px;overflow:hidden;margin-top:3px;position:absolute;bottom:0px;z-index:1}#container>.scroll.hazard{background-color:#702323}#container>.scroll .scroll-container{width:640px}#container>.scroll .scroll-container .fixed,#container>.scroll .scroll-container .scroll-header{margin-left:55px;margin-right:55px;overflow:hidden;white-space:nowrap}#container>.scroll .scroll-container .scroll-header{height:26px;font-family:"Star4000 Small";font-size:20pt;margin-top:-10px}#container>.scroll .scroll-container .fixed{font-family:"Star4000";font-size:24pt}#container>.scroll .scroll-container .fixed .scroll-area{text-wrap:nowrap;position:relative}.radar #container>.scroll{z-index:100}.radar #container>.scroll.hazard{z-index:1000 !important}.wide #container>.scroll{width:854px;margin-left:-107px}.wide #container>.scroll .scroll-container{margin-left:107px}.weather-display .main.current-weather.main .col{height:50px;width:255px;display:inline-block;margin-top:10px;padding-top:10px;position:absolute;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .main.current-weather.main .col.left{font-family:"Star4000 Extended";font-size:18pt}.weather-display .main.current-weather.main .col.right{right:0px;font-family:"Star4000 Large";font-size:18px;font-weight:bold;line-height:24px}.weather-display .main.current-weather.main .col.right .row{margin-bottom:12px}.weather-display .main.current-weather.main .col.right .row .label,.weather-display .main.current-weather.main .col.right .row .value{display:inline-block}.weather-display .main.current-weather.main .col.right .row .label{margin-left:20px}.weather-display .main.current-weather.main .col.right .row .value{float:right;margin-right:10px}.weather-display .main.current-weather.main .center{text-align:center}.weather-display .main.current-weather.main .temp{font-family:"Star4000 Large";font-size:24pt}.weather-display .main.current-weather.main .icon img{margin:0 auto;display:block;width:108px}.weather-display .main.current-weather.main .wind-container{margin-left:10px;display:flex}.weather-display .main.current-weather.main .wind-container>div{width:50%}.weather-display .main.current-weather.main .wind-container .wind{text-align:right}.weather-display .main.current-weather.main .wind-gusts{text-align:right;font-size:28px}.weather-display .main.current-weather.main .location{color:#ff0;max-height:32px;margin-bottom:10px;padding-top:4px;overflow:hidden;text-wrap:nowrap;padding-left:15px}:root[data-theme]:not([data-theme=default]) .weather-display .main.current-weather.main .col.left{margin-top:35px}:root[data-theme]:not([data-theme=default]) .weather-display .main.current-weather.main .col.right{margin-top:36px;padding:21px}:root[data-theme]:not([data-theme=default]) .weather-display .main.current-weather.main .wind-container{display:block}:root[data-theme]:not([data-theme=default]) .weather-display .main.current-weather.main .wind-container>div{width:50%}:root[data-theme]:not([data-theme=default]) .weather-display .main.current-weather.main .wind-container .wind{text-align:right;font-size:22px;display:contents}.weather-display .main.latest-observations.main{padding-top:18px}.weather-display .main.latest-observations.main .column-headers{display:flex;font-family:"Star4000";font-size:14pt;font-weight:bold;color:#ff0;width:70%;margin:8px auto 10px;padding-top:20px;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .main.latest-observations.main .column-headers .city{width:30%}.weather-display .main.latest-observations.main .column-headers .temp{width:15%;text-align:center}.weather-display .main.latest-observations.main .column-headers .conditions{width:30%;text-align:center}.weather-display .main.latest-observations.main .column-headers .wind{width:25%;text-align:right;padding-right:4px}.weather-display .main.latest-observations.main .observation-lines{width:70%;margin:0 auto}.weather-display .main.latest-observations.main .observation-lines .observation-row{display:flex;font-family:"Star4000";font-size:14pt;line-height:1.4;margin-bottom:4px;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .main.latest-observations.main .observation-lines .observation-row.template{display:none}.weather-display .main.latest-observations.main .observation-lines .observation-row .city{width:30%;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;color:#fff}.weather-display .main.latest-observations.main .observation-lines .observation-row .temp{width:15%;text-align:center;color:#ff0}.weather-display .main.latest-observations.main .observation-lines .observation-row .conditions{width:30%;text-align:center;color:#fff}.weather-display .main.latest-observations.main .observation-lines .observation-row .wind{width:25%;text-align:right;padding-right:4px;color:#fff}#extended-forecast-html.weather-display{background-image:var(--theme-background-2)}.weather-display .main.extended-forecast .day-container{margin-top:16px;margin-left:27px}.weather-display .main.extended-forecast .day{text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;padding:5px;height:285px;width:155px;display:inline-block;margin:0px 15px;font-family:"Star4000";font-size:24pt}.weather-display .main.extended-forecast .day .date{text-transform:uppercase;text-align:center;color:#ff0}.weather-display .main.extended-forecast .day .condition{text-align:center;height:74px;margin-top:5px}.weather-display .main.extended-forecast .day .icon{text-align:center;height:75px}.weather-display .main.extended-forecast .day .icon img{max-height:75px}.weather-display .main.extended-forecast .day .temperatures{width:100%}.weather-display .main.extended-forecast .day .temperatures .temperature-block{display:inline-block;width:44%;vertical-align:top}.weather-display .main.extended-forecast .day .temperatures .temperature-block>div{text-align:center}.weather-display .main.extended-forecast .day .temperatures .temperature-block .value{font-family:"Star4000 Large";margin-top:4px}.weather-display .main.extended-forecast .day .temperatures .temperature-block.lo .label{color:#8080ff}.weather-display .main.extended-forecast .day .temperatures .temperature-block.hi .label{color:#ff0}.weather-display .main.hourly.main{overflow-y:hidden}.weather-display .main.hourly.main .column-headers{background-color:#200057;height:20px;position:absolute;width:100%}.weather-display .main.hourly.main .column-headers{position:sticky;top:0px;z-index:5}.weather-display .main.hourly.main .column-headers div{display:inline-block;font-family:"Star4000 Small";font-size:24pt;color:#ff0;position:absolute;top:-14px;z-index:5;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .main.hourly.main .column-headers .temp{left:355px}.weather-display .main.hourly.main .column-headers .like{left:435px}.weather-display .main.hourly.main .column-headers .wind{left:535px}.weather-display .main.hourly.main .hourly-lines{min-height:338px;padding-top:10px;background:#0b0b39}.weather-display .main.hourly.main .hourly-lines .hourly-row{font-family:"Star4000 Large";font-size:24pt;height:72px;color:#ff0;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;position:relative}.weather-display .main.hourly.main .hourly-lines .hourly-row>div{position:absolute;white-space:pre;top:8px}.weather-display .main.hourly.main .hourly-lines .hourly-row .hour{left:25px}.weather-display .main.hourly.main .hourly-lines .hourly-row .icon{left:255px;width:70px;text-align:center;top:unset}.weather-display .main.hourly.main .hourly-lines .hourly-row .temp{left:355px}.weather-display .main.hourly.main .hourly-lines .hourly-row .like{left:425px}.weather-display .main.hourly.main .hourly-lines .hourly-row .like.heat-index{color:#e00}.weather-display .main.hourly.main .hourly-lines .hourly-row .like.wind-chill{color:#8080ff}.weather-display .main.hourly.main .hourly-lines .hourly-row .wind{left:505px;width:100px;text-align:right}#hourly-graph-html{background-image:var(--theme-background-1-chart)}#hourly-graph-html .header .right{position:absolute;top:35px;right:60px;width:360px;font-family:"Star4000 Small";font-size:28px;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;text-align:right}#hourly-graph-html .header .right div{margin-top:-18px}#hourly-graph-html .header .right .temperature{color:red}#hourly-graph-html .header .right .dewpoint{color:green}#hourly-graph-html .header .right .cloud{color:#d3d3d3}#hourly-graph-html .header .right .rain{color:aqua}.weather-display .main.hourly-graph.main>div{position:absolute}.weather-display .main.hourly-graph.main .label{font-family:"Star4000 Small";font-size:24pt;color:#ff0;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;margin-top:-15px;position:absolute}.weather-display .main.hourly-graph.main .x-axis{bottom:0px;left:54px;width:532px;height:20px}.weather-display .main.hourly-graph.main .x-axis .label{text-align:center;transform:translateX(-50%);white-space:nowrap}.weather-display .main.hourly-graph.main .x-axis .label.l-1{left:0px}.weather-display .main.hourly-graph.main .x-axis .label.l-2{left:133px}.weather-display .main.hourly-graph.main .x-axis .label.l-3{left:266px}.weather-display .main.hourly-graph.main .x-axis .label.l-4{left:399px}.weather-display .main.hourly-graph.main .x-axis .label.l-5{left:532px}.weather-display .main.hourly-graph.main .chart{top:0px;left:50px}.weather-display .main.hourly-graph.main .chart img{width:532px;height:285px}.weather-display .main.hourly-graph.main .y-axis{top:0px;left:0px;width:50px;height:285px}.weather-display .main.hourly-graph.main .y-axis .label{text-align:right;right:0px}.weather-display .main.hourly-graph.main .y-axis .label.l-1{top:0px}.weather-display .main.hourly-graph.main .y-axis .label.l-2{top:93.3333333333px}.weather-display .main.hourly-graph.main .y-axis .label.l-3{bottom:82.3333333333px}.weather-display .main.hourly-graph.main .y-axis .label.l-4{bottom:0px}.weather-display .main.hourly-graph.main .column-headers{background-color:#200057;height:20px;position:absolute;width:100%}.weather-display .main.hourly-graph.main .column-headers{position:sticky;top:0px;z-index:5}.weather-display .main.hourly-graph.main .column-headers .temp{left:355px}.weather-display .main.hourly-graph.main .column-headers .like{left:435px}.weather-display .main.hourly-graph.main .column-headers .wind{left:535px}.weather-display .main.travel.main{overflow-y:hidden}.weather-display .main.travel.main .column-headers{background-color:#200057;height:20px;position:sticky;top:0px;width:100%;z-index:5;overflow:hidden}.weather-display .main.travel.main .column-headers div{display:inline-block;font-family:"Star4000 Small";font-size:24pt;color:#ff0;position:absolute;top:-14px;z-index:5;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .main.travel.main .column-headers .temp{width:50px;text-align:center}.weather-display .main.travel.main .column-headers .temp.low{left:455px}.weather-display .main.travel.main .column-headers .temp.high{left:510px;width:60px}.weather-display .main.travel.main .travel-lines{min-height:338px;padding-top:10px;background:#0b0b39}.weather-display .main.travel.main .travel-lines .travel-row{font-family:"Star4000 Large";font-size:24pt;height:72px;color:#ff0;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;position:relative}.weather-display .main.travel.main .travel-lines .travel-row>div{position:absolute;white-space:pre;top:8px}.weather-display .main.travel.main .travel-lines .travel-row .city{left:80px}.weather-display .main.travel.main .travel-lines .travel-row .icon{left:330px;width:70px;text-align:center;top:unset}.weather-display .main.travel.main .travel-lines .travel-row .icon img{max-width:47px}.weather-display .main.travel.main .travel-lines .travel-row .temp{width:50px;text-align:center}.weather-display .main.travel.main .travel-lines .travel-row .temp.low{left:455px}.weather-display .main.travel.main .travel-lines .travel-row .temp.high{left:510px;width:60px}.weather-display .local-forecast .container{position:relative;top:15px;margin:0px 10px;box-sizing:border-box;height:280px;overflow:hidden}.weather-display .local-forecast .forecasts{position:relative}.weather-display .local-forecast .forecast{font-family:"Star4000";font-size:24pt;text-transform:uppercase;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;min-height:280px;line-height:40px}:root[data-theme]:not([data-theme=default]) .weather-display .local-forecast .forecasts{margin-top:16px}:root[data-theme]:not([data-theme=default]) .weather-display .local-forecast .forecast{font-size:24px;padding-right:20px}.weather-display .progress{text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;font-family:"Star4000 Extended";font-size:18pt}.weather-display .progress .container{position:relative;top:15px;margin:0px 10px;box-sizing:border-box;height:310px;overflow:hidden;line-height:26px}.weather-display .progress .container .item{position:relative}.weather-display .progress .container .item .name{white-space:nowrap}.weather-display .progress .container .item .name::after{content:"........................................................................"}.weather-display .progress .container .item .links{position:absolute;text-align:right;right:0px;top:0px}.weather-display .progress .container .item .links>div{background-color:#26235a;display:none;padding-left:4px}.weather-display .progress .container .item .links .loading,.weather-display .progress .container .item .links .retrying{color:#ff0}.weather-display .progress .container .item .links .press-here{color:lime;cursor:pointer}.weather-display .progress .container .item .links .failed{color:red}.weather-display .progress .container .item .links .no-data{color:silver}.weather-display .progress .container .item .links .disabled{color:silver}.weather-display .progress .container .item .links.loading .loading,.weather-display .progress .container .item .links.press-here .press-here,.weather-display .progress .container .item .links.failed .failed,.weather-display .progress .container .item .links.no-data .no-data,.weather-display .progress .container .item .links.disabled .disabled,.weather-display .progress .container .item .links.retrying .retrying{display:block}@keyframes progress-scroll{0%{background-position:-40px 0}100%{background-position:40px 0}}#progress-html.weather-display .scroll .progress-bar-container{border:2px solid #000;background-color:#fff;margin:20px auto;width:524px;position:relative;display:none}#progress-html.weather-display .scroll .progress-bar-container.show{display:block}#progress-html.weather-display .scroll .progress-bar-container .progress-bar{height:20px;margin:2px;width:520px;background:repeating-linear-gradient(90deg, #09246f 0px, #09246f 5px, #364ac0 5px, #364ac0 10px, #4f99f9 10px, #4f99f9 15px, #8ffdfa 15px, #8ffdfa 20px, #4f99f9 20px, #4f99f9 25px, #364ac0 25px, #364ac0 30px, #09246f 30px, #09246f 40px);animation-duration:2s;animation-fill-mode:forwards;animation-iteration-count:infinite;animation-name:progress-scroll;animation-timing-function:steps(8, end)}#progress-html.weather-display .scroll .progress-bar-container .cover{position:absolute;top:0px;right:0px;background-color:#fff;width:100%;height:24px;transition:width 1s steps(6)}#radar-html.weather-display{background-image:var(--theme-background-4)}#radar-html.weather-display .header{height:83px}#radar-html.weather-display .header .title.dual{color:#fff;font-family:"Arial",sans-serif;font-weight:bold;font-size:28pt;left:155px}#radar-html.weather-display .header .title.dual .top{top:-4px}#radar-html.weather-display .header .title.dual .bottom{top:31px}#radar-html.weather-display .header .right{position:absolute;right:0px;width:360px;margin-top:2px;font-family:"Star4000";font-size:18pt;font-weight:bold;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;text-align:center}#radar-html.weather-display .header .right .scale-table{display:flex;justify-content:center;gap:4px}#radar-html.weather-display .header .right .scale-table .item{display:flex;flex-direction:column;align-items:center;width:25px;gap:2px}#radar-html.weather-display .header .right .scale-table .box{display:block;border:2.7px solid #000;width:90%;height:12px;margin-top:4px;padding:0}#radar-html.weather-display .header .right .scale-table .box-1{background-color:#49bef6}#radar-html.weather-display .header .right .scale-table .box-2{background-color:#31d216}#radar-html.weather-display .header .right .scale-table .box-3{background-color:#f1e458}#radar-html.weather-display .header .right .scale-table .box-4{background-color:#e08e2f}#radar-html.weather-display .header .right .scale-table .box-5{background-color:#c42a2a}#radar-html.weather-display .header .right .scale-table .box-6{background-color:#913bb8}#radar-html.weather-display .header .right .scale-table .label{font-family:"Star4000 Small";font-size:10pt;line-height:1;white-space:nowrap}#radar-html.weather-display .header .right .scale{margin-top:-2px}#radar-html.weather-display .header .right .time{position:relative;font-weight:normal;top:-20px;font-family:"Star4000 Small";font-size:18pt;left:132px}.weather-display .main.radar{overflow:hidden;height:367px}.weather-display .main.radar .container{position:relative;height:100%}.weather-display .main.radar .container .scroll-area{position:relative;height:100%}.weather-display .main.radar .container .frame{height:100%}.weather-display .main.radar .container .map{height:100%;width:100%}.weather-display .main.radar .container .leaflet-map{height:100%;width:100%;background:#061f3e}.weather-display .main.radar .container .leaflet-container{background:#061f3e;font-family:inherit}.weather-display .main.radar .container .radar-base-layer,.weather-display .main.radar .container .radar-base-layer .leaflet-tile{filter:grayscale(0.35) brightness(0.58) contrast(1.1) saturate(0.2)}.weather-display .main.radar .container .radar-boundary-layer,.weather-display .main.radar .container .radar-boundary-layer .leaflet-tile{filter:grayscale(0.8) brightness(0.7) contrast(1.3) saturate(0.1)}.weather-display .main.radar .container .leaflet-control-container,.weather-display .main.radar .container .leaflet-control-attribution,.weather-display .main.radar .container .leaflet-control-zoom{display:none}.weather-display .main.radar .container .location-marker{background:#ff0;border:2px solid #000;border-radius:50%}.weather-display .main.radar .container .nearby-weather-marker{display:none;background:rgba(0,0,0,0);border:0}.weather-display .main.radar .container .nearby-weather-marker .nearby-weather-marker-inner{display:inline-flex;flex-direction:column;align-items:center;min-width:72px;padding:2px 4px;background:rgba(18,34,61,0);color:#fff;text-align:center;opacity:.35}.weather-display .main.radar .container .nearby-weather-marker .city{font-family:"Star4000 Small";font-size:11pt;line-height:1;white-space:nowrap;margin-bottom:1px;text-shadow:1px 1px 0 #000;display:none}.weather-display .main.radar .container .nearby-weather-marker .details{display:flex;align-items:center;gap:2px}.weather-display .main.radar .container .nearby-weather-marker .temp{font-family:"Star4000";font-size:18pt;line-height:1;color:#ff0;text-shadow:1px 1px 0 #000}.weather-display .main.radar .container .nearby-weather-marker img{width:auto;height:20px}.wide.radar #container{background:url(../images/backgrounds/4-wide.png)}.weather-display .main.ground-view.main{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:12px;padding:20px 24px 18px}.weather-display .main.ground-view.main .hidden{display:none}.weather-display .main.ground-view.main .ground-view-media{width:100%;height:100%;display:flex;align-items:center;justify-content:center;flex:1 1 auto;min-height:0;margin-right:60px;margin-top:30px}.weather-display .main.ground-view.main .ground-view-image{display:block;max-width:300px;max-height:150px;object-fit:contain;border:2px solid hsla(0,0%,100%,.35);background:#000}.weather-display .main.ground-view.main .ground-view-label,.weather-display .main.ground-view.main .ground-view-empty{width:100%;text-align:center;font-family:"Star4000";font-size:15pt;color:#ff0;padding:10px;margin-right:80px;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .main.ground-view.main .ground-view-empty{flex:1 1 auto;display:flex;align-items:center;justify-content:center}#regional-forecast-html.weather-display{background-image:var(--theme-background-5)}.weather-display .main.regional-forecast{position:relative;overflow:hidden;z-index:0}.weather-display .main.regional-forecast .map{position:absolute;inset:0}.weather-display .main.regional-forecast .leaflet-map{height:100%;width:100%;background:#061f3e}.weather-display .main.regional-forecast .leaflet-container{background:#061f3e;font-family:inherit}.weather-display .main.regional-forecast .radar-base-layer,.weather-display .main.regional-forecast .radar-base-layer .leaflet-tile{filter:grayscale(0.35) brightness(0.58) contrast(1.1) saturate(0.2)}.weather-display .main.regional-forecast .radar-boundary-layer,.weather-display .main.regional-forecast .radar-boundary-layer .leaflet-tile{filter:grayscale(0.8) brightness(0.7) contrast(1.3) saturate(0.1)}.weather-display .main.regional-forecast .leaflet-control-container,.weather-display .main.regional-forecast .leaflet-control-attribution,.weather-display .main.regional-forecast .leaflet-control-zoom{display:none}.weather-display .main.regional-forecast .location-marker{background:#ff0;border:2px solid #000;border-radius:50%}.weather-display .main.regional-forecast .nearby-weather-marker{background:rgba(0,0,0,0);border:0}.weather-display .main.regional-forecast .nearby-weather-marker .nearby-weather-marker-inner{display:inline-flex;flex-direction:column;align-items:center;min-width:72px;color:#fff;text-align:center;opacity:.75}.weather-display .main.regional-forecast .nearby-weather-marker .city{display:none}.weather-display .main.regional-forecast .nearby-weather-marker .details{display:flex;align-items:center;gap:2px}.weather-display .main.regional-forecast .nearby-weather-marker .temp{font-family:"Star4000";font-size:21pt;line-height:1;color:#ff0;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .main.regional-forecast .nearby-weather-marker img{width:auto;height:32px}#almanac-html.weather-display{background-image:var(--theme-background-3)}.weather-display .main.almanac{font-family:"Star4000";font-size:24pt;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .main.almanac .sun{display:grid;grid-template-columns:auto auto auto;grid-template-rows:auto auto auto;gap:0px 90px;margin:3px auto 5px auto;width:fit-content;line-height:30px}.weather-display .main.almanac .sun .grid-item{width:auto;height:auto;padding:0;margin:0;position:relative}.weather-display .main.almanac .sun .grid-item.header{color:#ff0;text-align:center}.weather-display .main.almanac .sun .grid-item.row-label{text-align:right}.weather-display .main.almanac .sun .grid-item.time{text-align:center}.weather-display .main.almanac .moon{position:relative;padding:7px 50px;line-height:36px}.weather-display .main.almanac .moon .title{color:#ff0;padding-left:13px}.weather-display .main.almanac .moon .day{display:inline-block;text-align:center;width:132px}.weather-display .main.almanac .moon .day .icon{padding-left:10px}.weather-display .main.almanac .moon .day .date{position:relative;top:-10px}#hazards-html.weather-display{background-image:url("../images/backgrounds/7.png")}.weather-display .main.hazards.main{overflow-y:hidden;height:480px;background-color:#702323}.weather-display .main.hazards.main .hazard-lines{min-height:400px;padding-top:10px}.weather-display .main.hazards.main .hazard-lines .hazard{font-family:"Star4000";font-size:24pt;color:#fff;text-shadow:0px 0px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;position:relative;text-transform:uppercase;margin-top:10px;margin-left:80px;margin-right:80px;padding-bottom:10px}.wide.hazards #container{background:url(../images/backgrounds/7-wide.png)}.media{display:none}#ToggleMediaContainer{display:none;position:relative}#ToggleMediaContainer.available{display:inline-block}#ToggleMediaContainer.available img.on{display:none}#ToggleMediaContainer.available img.off{display:block}#ToggleMediaContainer.available.playing img.on{display:block}#ToggleMediaContainer.available.playing img.off{display:none}#ToggleMediaContainer .volume-slider{display:none;position:absolute;top:0px;transform:translateY(-100%);width:100%;background-color:#000;text-align:center;z-index:100}@media(prefers-color-scheme: dark){#ToggleMediaContainer .volume-slider{background-color:#303030}}#ToggleMediaContainer .volume-slider input[type=range]{writing-mode:vertical-lr;direction:rtl;margin-top:20px;margin-bottom:20px}#ToggleMediaContainer .volume-slider.show{display:block}#spc-outlook-html.weather-display{background-image:var(--theme-background-6)}.weather-display .spc-outlook .container{position:relative;top:0px;margin:0px 10px;box-sizing:border-box;height:300px;overflow:hidden}.weather-display .spc-outlook .risk-levels{position:absolute;left:206px;font-family:"Star4000 Small";font-size:32px;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .spc-outlook .risk-levels .risk-level{position:relative;top:-14px;height:20px}.weather-display .spc-outlook .risk-levels .risk-level:nth-child(1){left:100px}.weather-display .spc-outlook .risk-levels .risk-level:nth-child(2){left:80px}.weather-display .spc-outlook .risk-levels .risk-level:nth-child(3){left:60px}.weather-display .spc-outlook .risk-levels .risk-level:nth-child(4){left:40px}.weather-display .spc-outlook .risk-levels .risk-level:nth-child(5){left:20px}.weather-display .spc-outlook .risk-levels .risk-level:nth-child(6){left:0px}.weather-display .spc-outlook .days{position:absolute;top:120px}.weather-display .spc-outlook .days .day{height:60px}.weather-display .spc-outlook .days .day .day-name{position:absolute;font-family:"Star4000";font-size:24pt;width:200px;text-align:right;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;padding-top:20px}.weather-display .spc-outlook .days .day .risk-bar{position:absolute;width:150px;height:40px;left:210px;margin-top:20px;border:3px outset hsl(0,0%,70%);background:linear-gradient(0deg, hsl(0, 0%, 40%) 0%, hsl(0, 0%, 60%) 50%, hsl(0, 0%, 40%) 100%)}#server-observations-html.weather-display .header .title.single{font-size:20pt}.weather-display .server-observations.main{height:auto !important;min-height:250px}.weather-display .server-observations .container{position:relative;top:15px;box-sizing:border-box;height:250px;overflow:hidden}.weather-display .server-observations .server-output{position:relative;font-family:"Star4000";font-size:20pt;line-height:32px;color:#fff;text-transform:uppercase;text-align:center;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000}.weather-display .server-observations .server-output .server-page{height:250px;padding:0 8px;box-sizing:border-box;margin-top:16px;padding-right:30px}.weather-display .server-observations .server-output .server-line{white-space:normal;overflow-wrap:anywhere;word-break:break-word;margin-bottom:6px}.weather-display .linux-news.main{height:auto !important;min-height:250px}.weather-display .linux-news .container{position:relative;top:15px;margin:0px 10px;box-sizing:border-box;height:250px;overflow:hidden}.weather-display .linux-news .news-output{position:relative}.weather-display .linux-news .news-output .news-page{height:250px;box-sizing:border-box;padding:0 8px;display:flex;flex-direction:column;justify-content:space-between}.weather-display .linux-news .news-output .story{height:116px;overflow:hidden;margin-top:20px}.weather-display .linux-news .news-output .headline{font-family:"Star4000";font-size:17pt;line-height:22px;color:#ff0;text-transform:uppercase;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;margin-bottom:4px}.weather-display .linux-news .news-output .blurb{font-family:"Star4000";font-size:12pt;line-height:10pt;color:#fff;text-shadow:3px 3px 0 #000,-1.5px -1.5px 0 #000,0 -1.5px 0 #000,1.5px -1.5px 0 #000,1.5px 0 0 #000,1.5px 1.5px 0 #000,0 1.5px 0 #000,-1.5px 1.5px 0 #000,-1.5px 0 0 #000;overflow:hidden;padding-right:30px}.scanlines{position:relative;overflow:hidden;isolation:isolate}.scanlines #container{position:relative;z-index:1;transform:translateZ(0);will-change:filter;filter:blur(0.6px) saturate(1.12) contrast(1.02) brightness(1)}.scanlines #container::before,.scanlines #container::after{content:"";position:absolute;inset:0;pointer-events:none;z-index:3}.scanlines #container::before{background:linear-gradient(to right, rgba(255, 0, 0, 0.05) 0%, rgba(255, 0, 0, 0.01) 15%, rgba(255, 0, 0, 0) 50%, rgba(255, 0, 0, 0.01) 85%, rgba(255, 0, 0, 0.05) 100%);transform:translateX(-1px);filter:blur(1.8px);mix-blend-mode:screen}.scanlines #container::after{background:linear-gradient(to right, rgba(0, 140, 255, 0.05) 0%, rgba(0, 140, 255, 0.01) 15%, rgba(0, 140, 255, 0) 50%, rgba(0, 140, 255, 0.01) 85%, rgba(0, 140, 255, 0.05) 100%);transform:translateX(1px);filter:blur(1.8px);mix-blend-mode:screen}.scanlines:before,.scanlines:after{display:block;pointer-events:none;content:"";position:absolute;left:0;right:0}.scanlines:before{height:var(--scanline-thickness, 1px);z-index:2147483650;background:rgba(0,0,0,.3);opacity:.75;animation:scanline 6s linear infinite}.scanlines:after{top:0;bottom:0;z-index:2147483648;background:repeating-linear-gradient(to bottom, transparent 0, transparent var(--scanline-thickness, 1px), rgba(0, 0, 0, 0.3) var(--scanline-thickness, 1px), rgba(0, 0, 0, 0.3) calc(var(--scanline-thickness, 1px) * 2));animation:none}.scanlines{box-shadow:inset 0 0 80px rgba(0,0,0,.16),inset 0 0 18px hsla(0,0%,100%,.08)}.scanlines.crt-panels-only #container{filter:none}.scanlines.crt-panels-only .weather-display{filter:blur(0.6px) saturate(1.12) contrast(1.02) brightness(1);transform:translateZ(0)}.scanlines .header,.scanlines .main,.scanlines .scroll,.scanlines .date-time,.scanlines .city,.scanlines .temp,.scanlines .condition,.scanlines .location,.scanlines .label,.scanlines .value,.scanlines .title{text-shadow:0 0 1px hsla(0,0%,100%,.18),0 0 2px hsla(0,0%,100%,.06)}@keyframes scanline{0%{transform:translate3d(0, 200000%, 0)}}@keyframes scanlines{0%{background-position:0 50%}} \ No newline at end of file diff --git a/src/windy-webcams.mjs b/src/windy-webcams.mjs new file mode 100644 index 0000000..7f137cc --- /dev/null +++ b/src/windy-webcams.mjs @@ -0,0 +1,150 @@ +import { readFile } from 'fs/promises'; + +let cachedApiKey = null; + +const SEARCH_RADII_KM = [20, 50, 100]; + +const truncateTitle = (title, maxLength = 21) => { + if (!title || title.length <= maxLength) return title; + return `${title.slice(0, maxLength - 3)}...`; +}; + +const loadWindyApiKey = async () => { + if (cachedApiKey !== null) return cachedApiKey; + const key = (await readFile('./windy-api-key.txt', 'utf8')).trim(); + if (!key) { + throw new Error('Missing Windy API key'); + } + cachedApiKey = key; + return cachedApiKey; +}; + +const buildWindyNearbyUrl = (lat, lon, radiusKm, limit = 10, offset = 0) => { + const params = new URLSearchParams({ + lang: 'en', + limit: String(limit), + offset: String(offset), + nearby: `${lat},${lon},${radiusKm}`, + include: 'location,images,player', + }); + return `https://api.windy.com/webcams/api/v3/webcams?${params.toString()}`; +}; + +const fetchWindyWebcamsNearby = async (lat, lon, radiusKm, apiKey, options = {}) => { + const url = buildWindyNearbyUrl(lat, lon, radiusKm, options.limit ?? 10, options.offset ?? 0); + const response = await fetch(url, { + headers: { + accept: 'application/json', + 'x-windy-api-key': apiKey, + }, + }); + if (!response.ok) { + throw new Error(`Windy webcams request failed: ${response.status} ${response.statusText}`); + } + return response.json(); +}; + +const extractWindyWebcamCoordinates = (webcam) => { + const lat = webcam?.location?.latitude; + const lon = webcam?.location?.longitude; + if (typeof lat !== 'number' || typeof lon !== 'number') return null; + return { lat, lon }; +}; + +const extractWindyWebcamTitle = (webcam) => webcam?.title?.trim() || 'Nearby Webcam'; + +const extractWindyWebcamMedia = (webcam) => { + const imageCandidates = [ + webcam?.images?.current?.preview, + webcam?.images?.daylight?.preview, + webcam?.images?.current?.thumbnail, + webcam?.images?.daylight?.thumbnail, + webcam?.images?.current?.icon, + webcam?.images?.daylight?.icon, + ].filter(Boolean); + + const timelapseCandidates = [ + webcam?.player?.day, + webcam?.player?.month, + webcam?.player?.year, + webcam?.player?.lifetime, + ].filter(Boolean); + + const imageUrl = imageCandidates[0] ?? null; + const timelapseUrl = timelapseCandidates[0] ?? null; + + return { + imageUrl, + timelapseUrl, + mediaType: imageUrl ? 'image' : 'none', + }; +}; + +const isUsableWindyWebcam = (webcam) => { + if ((webcam?.status ?? '').toLowerCase() !== 'active') return false; + if (!extractWindyWebcamCoordinates(webcam)) return false; + return Boolean(extractWindyWebcamMedia(webcam).imageUrl); +}; + +const calculateDistanceKm = (lat1, lon1, lat2, lon2) => { + const R = 6371; + const toRadians = (degrees) => (degrees * Math.PI) / 180; + const dLat = toRadians(lat2 - lat1); + const dLon = toRadians(lon2 - lon1); + const a = Math.sin(dLat / 2) ** 2 + + Math.cos(toRadians(lat1)) * Math.cos(toRadians(lat2)) * Math.sin(dLon / 2) ** 2; + const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); + return R * c; +}; + +const normalizeWindyWebcam = (webcam, city, sourceLat, sourceLon) => { + const coords = extractWindyWebcamCoordinates(webcam); + if (!coords) return null; + const media = extractWindyWebcamMedia(webcam); + const title = truncateTitle(extractWindyWebcamTitle(webcam)); + return { + id: String(webcam.webcamId ?? ''), + title, + city, + label: `${title} - ${city}`, + distanceKm: calculateDistanceKm(sourceLat, sourceLon, coords.lat, coords.lon), + imageUrl: media.imageUrl, + timelapseUrl: media.timelapseUrl, + mediaType: media.mediaType, + location: { + city: webcam?.location?.city ?? '', + region: webcam?.location?.region ?? '', + country: webcam?.location?.country ?? '', + }, + lastUpdatedOn: webcam?.lastUpdatedOn ?? null, + }; +}; + +const pickBestWindyWebcam = (webcams, city, sourceLat, sourceLon) => webcams + .filter(isUsableWindyWebcam) + .map((webcam) => normalizeWindyWebcam(webcam, city, sourceLat, sourceLon)) + .filter(Boolean) + .sort((a, b) => a.distanceKm - b.distanceKm)[0] ?? null; + +const findNearestWindyWebcam = async (lat, lon, city, apiKey) => { + for (const radiusKm of SEARCH_RADII_KM) { + const result = await fetchWindyWebcamsNearby(lat, lon, radiusKm, apiKey); + const webcam = pickBestWindyWebcam(result?.webcams ?? [], city, lat, lon); + if (webcam) return webcam; + } + return null; +}; + +export { + loadWindyApiKey, + buildWindyNearbyUrl, + fetchWindyWebcamsNearby, + extractWindyWebcamCoordinates, + extractWindyWebcamTitle, + extractWindyWebcamMedia, + isUsableWindyWebcam, + calculateDistanceKm, + normalizeWindyWebcam, + pickBestWindyWebcam, + findNearestWindyWebcam, +}; diff --git a/views/index.ejs b/views/index.ejs index 5c0b3a8..8c5bf60 100644 --- a/views/index.ejs +++ b/views/index.ejs @@ -139,6 +139,9 @@
<%- include('partials/radar.ejs') %>
+
+ <%- include('partials/ground-view.ejs') %> +
<%- include('partials/hazards.ejs') %>
diff --git a/views/partials/ground-view.ejs b/views/partials/ground-view.ejs new file mode 100644 index 0000000..27bee31 --- /dev/null +++ b/views/partials/ground-view.ejs @@ -0,0 +1,8 @@ +<%- include('header.ejs', {titleDual:{ top: 'Ground' , bottom: 'View' }, hasTime: true}) %> +
+ + + +