2.7
New profile tabs Moved Solar model to Why box
This commit is contained in:
+18
-8
@@ -7,21 +7,15 @@
|
||||
.utci-header {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr auto 1fr; /* left | dial | why */
|
||||
grid-template-areas:
|
||||
"hleft dial hright"
|
||||
". env .";
|
||||
grid-template-areas: "hleft dial hright";
|
||||
column-gap: 32px;
|
||||
row-gap: 6px;
|
||||
align-items: center;
|
||||
padding-bottom: 6px;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
/* Side columns occupy only the dial's row, so they vertically centre against
|
||||
the dial itself; the environment selector drops to its own row beneath. */
|
||||
.header-left { grid-area: hleft; }
|
||||
.scope-col { grid-area: dial; }
|
||||
.scope-env-bar { grid-area: env; }
|
||||
.header-right { grid-area: hright; }
|
||||
|
||||
/* Left column — wordmark, tagline, search and location stacked together */
|
||||
@@ -276,10 +270,15 @@
|
||||
}
|
||||
|
||||
.scope-env-row .cs-panel,
|
||||
.scope-env-row .cs-option {
|
||||
.scope-env-row .cs-option,
|
||||
.insight-env-footer .cs-panel,
|
||||
.insight-env-footer .cs-option {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.insight-env-footer .cs-btn.on:hover .cs-btn-overlay { opacity: 1; }
|
||||
.insight-env-footer .cs-btn .cs-btn-label { position: relative; z-index: 1; }
|
||||
|
||||
/* Per-environment gradient themes — !important keeps the gradient fixed
|
||||
under the brass hover overlay (ui.css :hover rules load later). */
|
||||
.scope-env-row[data-env="open"] .cs-btn.on { background: linear-gradient(135deg, #5a8a28 0%, #b8d860 100%) !important; border-color: #4a7818 !important; color: #fff; }
|
||||
@@ -291,8 +290,19 @@
|
||||
.scope-env-row[data-env="openwater"] .cs-btn.on { background: linear-gradient(135deg, #0c4878 0%, #2898d8 100%) !important; border-color: #0c4878 !important; color: #fff; }
|
||||
.scope-env-row[data-env="urban"] .cs-btn.on { background: linear-gradient(135deg, #4a5568 0%, #a89880 100%) !important; border-color: #3a4455 !important; color: #fff; }
|
||||
|
||||
/* Duplicate gradient rules for the Solar model button inside the insight panel */
|
||||
.insight-env-footer[data-env="open"] .cs-btn.on { background: linear-gradient(135deg, #5a8a28 0%, #b8d860 100%) !important; border-color: #4a7818 !important; color: #fff; }
|
||||
.insight-env-footer[data-env="forest"] .cs-btn.on { background: linear-gradient(135deg, #1b5e3b 0%, #57bf8a 100%) !important; border-color: #1b5e3b !important; color: #fff; }
|
||||
.insight-env-footer[data-env="alpine"] .cs-btn.on { background: linear-gradient(135deg, #a8cfe8 0%, #dff0fa 100%) !important; border-color: #6aaedd !important; color: #1a3a58; }
|
||||
.insight-env-footer[data-env="beach"] .cs-btn.on { background: linear-gradient(135deg, #2a8fc8 0%, #e8d87a 100%) !important; border-color: #1e78b0 !important; color: #fff; }
|
||||
.insight-env-footer[data-env="river"] .cs-btn.on { background: linear-gradient(135deg, #2e6e5e 0%, #78c4a8 100%) !important; border-color: #2e6e5e !important; color: #fff; }
|
||||
.insight-env-footer[data-env="desert"] .cs-btn.on { background: linear-gradient(135deg, #b86820 0%, #e8c458 100%) !important; border-color: #a05818 !important; color: #fff; }
|
||||
.insight-env-footer[data-env="openwater"] .cs-btn.on { background: linear-gradient(135deg, #0c4878 0%, #2898d8 100%) !important; border-color: #0c4878 !important; color: #fff; }
|
||||
.insight-env-footer[data-env="urban"] .cs-btn.on { background: linear-gradient(135deg, #4a5568 0%, #a89880 100%) !important; border-color: #3a4455 !important; color: #fff; }
|
||||
|
||||
/* On hover the brass overlay covers the interior — force white text for contrast */
|
||||
.scope-env-row .cs-btn.on:hover { color: #fff; }
|
||||
.insight-env-footer .cs-btn.on:hover { color: #fff; }
|
||||
|
||||
/* The search box on the right of the header */
|
||||
.utci-search-wrap {
|
||||
|
||||
+51
-4
@@ -11,7 +11,7 @@
|
||||
/* Wrapper that hosts the scrollable strip, edge fades, and chevrons. */
|
||||
.utci-day-tabs-wrap {
|
||||
position: relative;
|
||||
margin-top: 14px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
/* Edge fade gradients — only show when there's content to scroll to. */
|
||||
@@ -132,7 +132,7 @@
|
||||
flex-direction: column;
|
||||
gap: 0;
|
||||
margin-bottom: 0;
|
||||
padding: 0 14px 0;
|
||||
padding: 0px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
@@ -160,8 +160,8 @@
|
||||
justify-content: safe center; /* fall back to flex-start if it overflows */
|
||||
overflow-x: auto;
|
||||
overflow-y: auto; /* forced by browser when overflow-x is non-visible */
|
||||
padding-bottom: 400px; /* extends clip boundary so the tallest dropdown (Activities) isn't cut off */
|
||||
margin-bottom: -400px; /* cancel that extra space in the layout */
|
||||
padding-bottom: 6px;
|
||||
margin-bottom: 0;
|
||||
scrollbar-width: none;
|
||||
-ms-overflow-style: none;
|
||||
padding-right: 4px; /* breathing room at right edge */
|
||||
@@ -173,6 +173,12 @@
|
||||
.profile-scroll-wrap {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-top:8px;
|
||||
padding-bottom:4px;
|
||||
background-color: #fffcf2;
|
||||
border-left: 1.5px solid #c9b08a;
|
||||
border-right: 1.5px solid #c9b08a;
|
||||
border-bottom: 1.5px solid #c9b08a;
|
||||
}
|
||||
|
||||
.profile-scroll-wrap::before,
|
||||
@@ -285,6 +291,47 @@
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Tab strip above the profile scroll row */
|
||||
.profile-tabs {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
border-bottom: 1.5px solid #c9b08a;
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
.profile-tab {
|
||||
flex: 1;
|
||||
padding: 7px 4px;
|
||||
background: transparent;
|
||||
border: none;
|
||||
border-bottom: 2.5px solid transparent;
|
||||
border-left: 1.5px solid #f5edd6;
|
||||
border-right: 1.5px solid #f5edd6;
|
||||
border-top: 1.5px solid #f5edd6;
|
||||
margin-bottom: -1.5px;
|
||||
font-family: Manrope, sans-serif;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.06em;
|
||||
color: #9a7a4a;
|
||||
cursor: pointer;
|
||||
transition: color 0.15s, border-color 0.15s;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.profile-tab:hover {
|
||||
color: #c8922a;
|
||||
background: #f0e4c4;
|
||||
}
|
||||
.profile-tab.active {
|
||||
color: #c8922a;
|
||||
background-color: #fffcf2;
|
||||
border-bottom-color: #fffcf2;
|
||||
border-left: 1.5px solid #c9b08a;
|
||||
border-right: 1.5px solid #c9b08a;
|
||||
border-top: 1.5px solid #c9b08a;
|
||||
border-radius:7px 7px 0px 0px
|
||||
}
|
||||
|
||||
|
||||
/* ── 6. COLUMN TOGGLES (the "Columns: Hour Air RH ..." bar) ────────── */
|
||||
|
||||
|
||||
+54
-1
@@ -1176,12 +1176,64 @@
|
||||
Sits between the header dial and the day tabs.
|
||||
════════════════════════════════════════════════════════════════════════ */
|
||||
|
||||
/* "Why it feels like this" — sits in the header's right column, beside the dial */
|
||||
/* "Why it feels like this" - sits in the header's right column, beside the dial */
|
||||
.insight-panel--why {
|
||||
width: 100%;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
/* Solar model selector inside the Why it feels like panel */
|
||||
.insight-env-footer {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
border-bottom: 1px solid #ede4cc;
|
||||
padding-bottom: 8px;
|
||||
margin-bottom: 6px;
|
||||
gap: 4px;
|
||||
}
|
||||
.insight-env-footer-label {
|
||||
font-family: Manrope, sans-serif;
|
||||
font-size: 10px;
|
||||
font-weight: 700;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.1em;
|
||||
color: #b09870;
|
||||
text-align: center;
|
||||
}
|
||||
.insight-env-footer .scope-env-row {
|
||||
width: 100%;
|
||||
border-top: none;
|
||||
padding: 0;
|
||||
align-items: stretch;
|
||||
}
|
||||
.insight-env-footer .cs-wrap,
|
||||
.insight-env-footer .cs-btn {
|
||||
width: 100%;
|
||||
}
|
||||
.insight-env-footer .cs-btn {
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
isolation: isolate;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.insight-env-footer .cs-btn .cs-btn-overlay {
|
||||
display: block;
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
border-radius: inherit;
|
||||
background: #c8922a;
|
||||
opacity: 0;
|
||||
transition: opacity 0.35s ease;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
.insight-env-footer .cs-arrow {
|
||||
position: absolute;
|
||||
right: 12px;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
/* Hourly table paired with the "Today at a glance" rail on its right */
|
||||
.table-with-rail {
|
||||
display: grid;
|
||||
@@ -1222,6 +1274,7 @@
|
||||
letter-spacing: 0.1em;
|
||||
color: #b09870;
|
||||
margin-bottom: 10px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Each factor row */
|
||||
|
||||
+14
-17
@@ -441,26 +441,23 @@ export function UTCIForecast() {
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="scope-env-bar">
|
||||
<div class="scope-env-row" data-env=${utciEnv}>
|
||||
<span class="scope-env-label">Environment</span>
|
||||
<${CustomSelect}
|
||||
value=${utciEnv}
|
||||
isOn=${true}
|
||||
noHide=${true}
|
||||
grpClass="grp-felt"
|
||||
options=${Object.entries(UTCI_ENVIRONMENTS).map(([k, v]) => ({
|
||||
value: k,
|
||||
label: v.label,
|
||||
}))}
|
||||
onChange=${(v) => setUtciEnv(v)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="header-right">
|
||||
${forecast && currentRow && whyFeelsLike && html`
|
||||
<div class="insight-panel insight-panel--why">
|
||||
<div class="insight-env-footer" data-env=${utciEnv}>
|
||||
<span class="insight-env-footer-label">Solar model</span>
|
||||
<${CustomSelect}
|
||||
value=${utciEnv}
|
||||
isOn=${true}
|
||||
noHide=${true}
|
||||
grpClass="grp-felt"
|
||||
options=${Object.entries(UTCI_ENVIRONMENTS).map(([k, v]) => ({
|
||||
value: k,
|
||||
label: v.label,
|
||||
}))}
|
||||
onChange=${(v) => setUtciEnv(v)}
|
||||
/>
|
||||
</div>
|
||||
<div class="insight-panel-title">Why it feels like this</div>
|
||||
${[
|
||||
{ label: 'Sun and sky', icon: '☀', value: whyFeelsLike.sunAndSky },
|
||||
|
||||
+98
-125
@@ -42,7 +42,7 @@
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
import { h, Fragment } from '../../vendor/preact.js';
|
||||
import { useRef, useEffect } from '../../vendor/preact-hooks.js';
|
||||
import { useRef, useEffect, useState } from '../../vendor/preact-hooks.js';
|
||||
import htm from '../../vendor/htm.js';
|
||||
import { confidenceBand } from '../utils.js';
|
||||
import { FREE_DAYS, FILTER_PROFILES, OUTDOORS_VARIANTS, profileButtonOrder, activityVariantKeys, placeVariantKeys, workVariantKeys } from '../config.js';
|
||||
@@ -96,6 +96,17 @@ export function DayTabs({
|
||||
const profileScrollRef = useRef(null);
|
||||
const profileWrapRef = useRef(null);
|
||||
|
||||
// Active tab: common - places - activities - work.
|
||||
// Auto-derived from the current profile on mount and on change.
|
||||
const getTab = () => {
|
||||
if (activeProfile === 'outdoors' && placeVariantKeys.includes(outdoorsVariant)) return 'places';
|
||||
if (activeProfile === 'outdoors' && activityVariantKeys.includes(outdoorsVariant)) return 'activities';
|
||||
if (activeProfile === 'farming' || (activeProfile === 'outdoors' && workVariantKeys.includes(outdoorsVariant))) return 'work';
|
||||
return 'common';
|
||||
};
|
||||
const [activeTab, setActiveTab] = useState(getTab);
|
||||
useEffect(() => { setActiveTab(getTab()); }, [activeProfile, outdoorsVariant]);
|
||||
|
||||
useEffect(() => {
|
||||
const el = profileScrollRef.current;
|
||||
const wrap = profileWrapRef.current;
|
||||
@@ -155,139 +166,98 @@ export function DayTabs({
|
||||
<${Fragment}>
|
||||
|
||||
<div class="filter-profiles">
|
||||
<div class="profile-tabs">
|
||||
${[['common','Common'],['places','Places'],['activities','Activities'],['work','Work']].map(([key, label]) => html`
|
||||
<button
|
||||
key=${key}
|
||||
class=${`profile-tab${activeTab === key ? ' active' : ''}`}
|
||||
onClick=${() => setActiveTab(key)}
|
||||
>${label}</button>
|
||||
`)}
|
||||
</div>
|
||||
<div class="profile-scroll-wrap" ref=${profileWrapRef}>
|
||||
<div class="profile-scroll" ref=${profileScrollRef}>
|
||||
${profileButtonOrder.slice(0, 3).map((key) => {
|
||||
const profile = FILTER_PROFILES[key];
|
||||
const locked = profile.proOnly && !isPro;
|
||||
return html`
|
||||
|
||||
${activeTab === 'common' && profileButtonOrder.map((key) => {
|
||||
const profile = FILTER_PROFILES[key];
|
||||
const locked = profile.proOnly && !isPro;
|
||||
return html`
|
||||
<button
|
||||
key=${key}
|
||||
class=${`profile-btn${activeProfile === key ? ' active' : ''}${locked ? ' locked' : ''}`}
|
||||
style=${{ opacity: locked ? 0.6 : 1, cursor: locked ? 'not-allowed' : 'pointer', position: 'relative' }}
|
||||
title=${locked ? `Profile ${profile.label} is part of SunScope Extra` : ''}
|
||||
onClick=${() => {
|
||||
if (locked) { setProPromptSource(`profile:${key}`); setProPromptDay(0); return; }
|
||||
activateProfile(key);
|
||||
}}
|
||||
>
|
||||
<span class="profile-btn-scene" style=${{ background: profile.scene }}>${profile.scene.includes('url(') ? null : profile.icon}</span>
|
||||
<span class="profile-btn-label">${profile.label}${locked ? ' locked' : ''}</span>
|
||||
</button>`;
|
||||
})}
|
||||
|
||||
${activeTab === 'places' && placeOptions.map((opt) => html`
|
||||
<button
|
||||
key=${key}
|
||||
class=${`profile-btn${activeProfile === key ? ' active' : ''}${locked ? ' locked' : ''}`}
|
||||
style=${{ opacity: locked ? 0.6 : 1, cursor: locked ? 'not-allowed' : 'pointer', position: 'relative' }}
|
||||
title=${locked ? `🔒 ${profile.label} is part of SunScope Extra` : ''}
|
||||
key=${opt.value}
|
||||
class=${`profile-btn${placeValue === opt.value ? ' active' : ''}${opt.disabled ? ' locked' : ''}`}
|
||||
style=${{ opacity: opt.disabled ? 0.6 : 1, cursor: opt.disabled ? 'not-allowed' : 'pointer', position: 'relative' }}
|
||||
onClick=${() => {
|
||||
if (locked) {
|
||||
setProPromptSource(`profile:${key}`);
|
||||
setProPromptDay(0);
|
||||
return;
|
||||
}
|
||||
activateProfile(key);
|
||||
if (opt.disabled) { setProPromptSource(`variant:${opt.value}`); setProPromptDay(0); return; }
|
||||
try { localStorage.setItem('sunscope_profile', 'outdoors'); } catch(e) {}
|
||||
setActiveProfile('outdoors');
|
||||
setOutdoorsVariantAndSave(opt.value);
|
||||
setVisibleCols({ ...OUTDOORS_VARIANTS[opt.value].cols });
|
||||
setIndoorMode('off');
|
||||
setIndoorManaged(false);
|
||||
}}
|
||||
>
|
||||
<span class="profile-btn-scene" style=${{ background: profile.scene }}>${profile.scene.includes('url(') ? null : profile.icon}</span>
|
||||
<span class="profile-btn-label">${profile.label}${locked ? ' 🔒' : ''}</span>
|
||||
</button>`;
|
||||
})}
|
||||
<span class="profile-divider" aria-hidden="true"></span>
|
||||
<${CustomSelect}
|
||||
key="places"
|
||||
value=${placeValue}
|
||||
isOn=${activeProfile === 'outdoors' && placeVariantKeys.includes(outdoorsVariant)}
|
||||
noHide=${true}
|
||||
hideLabel="Places"
|
||||
buttonLabel=${placeValue === 'off' ? 'Places' : `${placeLabel}`}
|
||||
grpClass="profile-card"
|
||||
sceneStyle=${{ background: placeValue !== 'off' && OUTDOORS_VARIANTS[placeValue]?.scene ? OUTDOORS_VARIANTS[placeValue].scene : "url('assets/images/profiles/places.png') center / cover no-repeat, linear-gradient(160deg,#98ccc0,#c4e4dc)" }}
|
||||
sceneContent=${null}
|
||||
options=${placeOptions}
|
||||
onChange=${(v) => {
|
||||
if (v === 'off') { activateProfile('basic'); return; }
|
||||
if (OUTDOORS_VARIANTS[v]?.proOnly && !isPro) {
|
||||
setProPromptSource(`variant:${v}`);
|
||||
setProPromptDay(0);
|
||||
return;
|
||||
}
|
||||
try { localStorage.setItem('sunscope_profile', 'outdoors'); } catch (e) { /* ignore */ }
|
||||
setActiveProfile('outdoors');
|
||||
setOutdoorsVariantAndSave(v);
|
||||
setVisibleCols({ ...OUTDOORS_VARIANTS[v].cols });
|
||||
setIndoorMode('off');
|
||||
setIndoorManaged(false);
|
||||
}}
|
||||
/>
|
||||
<${CustomSelect}
|
||||
key="activities"
|
||||
value=${activityValue}
|
||||
isOn=${activeProfile === 'outdoors' && activityVariantKeys.includes(outdoorsVariant)}
|
||||
noHide=${true}
|
||||
hideLabel="Activities"
|
||||
buttonLabel=${activityValue === 'off' ? 'Activities' : `${activityLabel}`}
|
||||
grpClass="profile-card"
|
||||
sceneStyle=${{ background: activityValue !== 'off' && OUTDOORS_VARIANTS[activityValue]?.scene ? OUTDOORS_VARIANTS[activityValue].scene : "url('assets/images/profiles/activities.png') center / cover no-repeat, linear-gradient(160deg,#e0c0a0,#f0d8c0)" }}
|
||||
sceneContent=${null}
|
||||
options=${activityOptions}
|
||||
onChange=${(v) => {
|
||||
if (v === 'off') { activateProfile('basic'); return; }
|
||||
if (OUTDOORS_VARIANTS[v]?.proOnly && !isPro) {
|
||||
setProPromptSource(`variant:${v}`);
|
||||
setProPromptDay(0);
|
||||
return;
|
||||
}
|
||||
try { localStorage.setItem('sunscope_profile', 'outdoors'); } catch (e) { /* ignore */ }
|
||||
setActiveProfile('outdoors');
|
||||
setOutdoorsVariantAndSave(v);
|
||||
setVisibleCols({ ...OUTDOORS_VARIANTS[v].cols });
|
||||
setIndoorMode('off');
|
||||
setIndoorManaged(false);
|
||||
}}
|
||||
/>
|
||||
<${CustomSelect}
|
||||
key="work"
|
||||
value=${workValue}
|
||||
isOn=${activeProfile === 'farming' || (activeProfile === 'outdoors' && workVariantKeys.includes(outdoorsVariant))}
|
||||
noHide=${true}
|
||||
hideLabel="Work"
|
||||
buttonLabel=${workValue === 'off' ? 'Work' : `${workLabel}`}
|
||||
grpClass="profile-card"
|
||||
sceneStyle=${{ background: workValue === 'farming' ? FILTER_PROFILES.farming.scene : workValue !== 'off' && OUTDOORS_VARIANTS[workValue]?.scene ? OUTDOORS_VARIANTS[workValue].scene : "url('assets/images/profiles/work.png') center / cover no-repeat, linear-gradient(160deg,#b0b0d4,#d0d0e8)" }}
|
||||
sceneContent=${null}
|
||||
options=${workOptions}
|
||||
onChange=${(v) => {
|
||||
if (v === 'off') { activateProfile('basic'); return; }
|
||||
if (v === 'farming') { activateProfile('farming'); return; }
|
||||
if (OUTDOORS_VARIANTS[v]?.proOnly && !isPro) {
|
||||
setProPromptSource(`variant:${v}`);
|
||||
setProPromptDay(0);
|
||||
return;
|
||||
}
|
||||
try { localStorage.setItem('sunscope_profile', 'outdoors'); } catch (e) { /* ignore */ }
|
||||
setActiveProfile('outdoors');
|
||||
setOutdoorsVariantAndSave(v);
|
||||
setVisibleCols({ ...OUTDOORS_VARIANTS[v].cols });
|
||||
if (v === 'office') {
|
||||
setBuildingType('office');
|
||||
setIndoorMode('on');
|
||||
setIndoorManaged(false);
|
||||
} else {
|
||||
setIndoorMode('off');
|
||||
setIndoorManaged(false);
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<span class="profile-divider" aria-hidden="true"></span>
|
||||
${profileButtonOrder.slice(3).map((key) => {
|
||||
const profile = FILTER_PROFILES[key];
|
||||
const locked = profile.proOnly && !isPro;
|
||||
return html`
|
||||
<span class="profile-btn-scene" style=${{ background: opt.scene }}></span>
|
||||
<span class="profile-btn-label">${opt.name}</span>
|
||||
</button>
|
||||
`)}
|
||||
|
||||
${activeTab === 'activities' && activityOptions.map((opt) => html`
|
||||
<button
|
||||
key=${key}
|
||||
class=${`profile-btn${activeProfile === key ? ' active' : ''}${locked ? ' locked' : ''}`}
|
||||
style=${{ opacity: locked ? 0.6 : 1, cursor: locked ? 'not-allowed' : 'pointer', position: 'relative' }}
|
||||
title=${locked ? `🔒 ${profile.label} is part of SunScope Extra` : ''}
|
||||
key=${opt.value}
|
||||
class=${`profile-btn${activityValue === opt.value ? ' active' : ''}${opt.disabled ? ' locked' : ''}`}
|
||||
style=${{ opacity: opt.disabled ? 0.6 : 1, cursor: opt.disabled ? 'not-allowed' : 'pointer', position: 'relative' }}
|
||||
onClick=${() => {
|
||||
if (locked) {
|
||||
setProPromptSource(`profile:${key}`);
|
||||
setProPromptDay(0);
|
||||
return;
|
||||
}
|
||||
activateProfile(key);
|
||||
if (opt.disabled) { setProPromptSource(`variant:${opt.value}`); setProPromptDay(0); return; }
|
||||
try { localStorage.setItem('sunscope_profile', 'outdoors'); } catch(e) {}
|
||||
setActiveProfile('outdoors');
|
||||
setOutdoorsVariantAndSave(opt.value);
|
||||
setVisibleCols({ ...OUTDOORS_VARIANTS[opt.value].cols });
|
||||
setIndoorMode('off');
|
||||
setIndoorManaged(false);
|
||||
}}
|
||||
>
|
||||
<span class="profile-btn-scene" style=${{ background: profile.scene }}>${profile.scene.includes('url(') ? null : profile.icon}</span>
|
||||
<span class="profile-btn-label">${profile.label}${locked ? ' 🔒' : ''}</span>
|
||||
</button>`;
|
||||
})}
|
||||
<span class="profile-btn-scene" style=${{ background: opt.scene }}></span>
|
||||
<span class="profile-btn-label">${opt.name}</span>
|
||||
</button>
|
||||
`)}
|
||||
|
||||
${activeTab === 'work' && workOptions.map((opt) => html`
|
||||
<button
|
||||
key=${opt.value}
|
||||
class=${`profile-btn${workValue === opt.value ? ' active' : ''}${opt.disabled ? ' locked' : ''}`}
|
||||
style=${{ opacity: opt.disabled ? 0.6 : 1, cursor: opt.disabled ? 'not-allowed' : 'pointer', position: 'relative' }}
|
||||
onClick=${() => {
|
||||
if (opt.disabled) { setProPromptSource(`variant:${opt.value}`); setProPromptDay(0); return; }
|
||||
if (opt.value === 'farming') { activateProfile('farming'); return; }
|
||||
try { localStorage.setItem('sunscope_profile', 'outdoors'); } catch(e) {}
|
||||
setActiveProfile('outdoors');
|
||||
setOutdoorsVariantAndSave(opt.value);
|
||||
setVisibleCols({ ...OUTDOORS_VARIANTS[opt.value].cols });
|
||||
if (opt.value === 'office') { setBuildingType('office'); setIndoorMode('on'); setIndoorManaged(false); }
|
||||
else { setIndoorMode('off'); setIndoorManaged(false); }
|
||||
}}
|
||||
>
|
||||
<span class="profile-btn-scene" style=${{ background: opt.scene }}></span>
|
||||
<span class="profile-btn-label">${opt.name}</span>
|
||||
</button>
|
||||
`)}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -363,7 +333,10 @@ export function DayTabs({
|
||||
style=${{
|
||||
background: isActive ? wBgActive : wBg,
|
||||
color: wText,
|
||||
border: '1px solid #c9b08a',
|
||||
borderTop: '1px solid #c9b08a',
|
||||
borderRight: '1px solid #c9b08a',
|
||||
borderBottom: '1px solid #c9b08a',
|
||||
borderLeft: i === 0 ? '1px solid #c9b08a' : 'none',
|
||||
opacity: locked ? 0.5 : 1,
|
||||
cursor: locked ? 'not-allowed' : 'pointer',
|
||||
position: 'relative',
|
||||
|
||||
@@ -117,9 +117,11 @@ export function buildHourlyRows({ forecast, airQuality, location, vehicleType, v
|
||||
// Suggested multipliers (tune with real data):
|
||||
// Resting: 1.0 (no change)
|
||||
// Walking: 0.85
|
||||
// Cycling: 0.75
|
||||
// Running: 0.60
|
||||
// Sport: 0.50
|
||||
// Cycling: 0.75 *This will need a airflow calc as going at speed is cooling
|
||||
// Running: 0.60 *This will need a airflow calc as going at speed is cooling
|
||||
// Sport: 0.50 *This will need a airflow calc as going at speed is cooling
|
||||
//
|
||||
// *Danny Notes: These faster speed activities might need intergrating into the future "Vehicle Speed" calcs somehow?
|
||||
//
|
||||
// heatStressLevel - a simple label: 'Low' | 'Moderate' | 'High' | 'Very High'
|
||||
// Derived from UTCI + activity heat load. A runner at
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 121 KiB After Width: | Height: | Size: 53 KiB |
Reference in New Issue
Block a user