V1.0 Final Release

Fixed Stripe Issues
New Mobile Menu
Improved Lens Graphic
This commit is contained in:
fraxle
2026-05-16 11:42:20 +01:00
parent 4f16a5c784
commit 7eba879acf
6 changed files with 382 additions and 46 deletions
+104
View File
@@ -18,6 +18,8 @@ header {
padding: 18px 32px;
gap: 28px;
border-bottom: 1px solid #e0d0a8;
position: relative;
z-index: 100;
}
header a {
@@ -34,6 +36,108 @@ header a {
header a:hover { color: #c8922a; border-bottom-color: rgba(200,146,42,0.5); }
header a.active { color: #c8922a; }
/* Desktop: nav-links renders inline as part of header flex row */
.nav-links {
display: contents; /* participates in header's flex layout transparently */
}
/* ── Burger button ──────────────────────────────────────────────────── */
.utci-burger {
display: none;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 5px;
width: 36px;
height: 36px;
background: none;
border: none;
cursor: pointer;
padding: 4px;
flex-shrink: 0;
}
.utci-burger span {
display: block;
width: 22px;
height: 2px;
background: #7a5c2a;
border-radius: 2px;
transition: transform 0.25s ease, opacity 0.2s ease;
transform-origin: center;
}
.nav-overlay {
display: none;
}
/* ── Mobile nav drawer ──────────────────────────────────────────────── */
@media (max-width: 640px) {
header {
padding: 14px 20px;
}
.utci-burger {
display: flex;
}
.nav-links {
position: fixed;
top: 0;
right: -100%;
width: 220px;
height: 100vh;
background: #f5edd6;
border-left: 1px solid #e0d0a8;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
padding: 80px 32px 32px;
gap: 28px;
transition: right 0.28s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: -8px 0 24px rgba(100,70,20,0.08);
z-index: 105;
}
.nav-links.is-open {
right: 0;
}
.nav-links a {
font-size: 15px;
}
/* Burger → X animation */
header.nav-open .utci-burger span:nth-child(1) {
transform: translateY(7px) rotate(45deg);
}
header.nav-open .utci-burger span:nth-child(2) {
opacity: 0;
transform: scaleX(0);
}
header.nav-open .utci-burger span:nth-child(3) {
transform: translateY(-7px) rotate(-45deg);
}
/* Overlay */
.nav-overlay {
display: block;
position: fixed;
inset: 0;
background: rgba(30,18,8,0.35);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s ease;
z-index: 104;
}
header.nav-open .nav-overlay {
opacity: 1;
pointer-events: auto;
}
}
.logo {
margin-right: auto;
font-family: 'Fraunces', serif;
+165 -25
View File
@@ -66,30 +66,6 @@ body {
overflow-x: clip;
}
/* Two big soft glows in the corners — pure decoration.
Comment one out to remove that side's glow. */
.lens-bloom-a {
position: fixed;
top: -200px;
left: -200px;
width: 700px;
height: 700px;
border-radius: 50%;
background: radial-gradient(ellipse, rgba(255, 210, 60, 0.18) 0%, transparent 65%);
pointer-events: none;
z-index: 0;
}
.lens-bloom-b {
position: fixed;
top: -80px;
right: -140px;
width: 520px;
height: 420px;
background: radial-gradient(ellipse, rgba(200, 146, 42, 0.13) 0%, transparent 68%);
pointer-events: none;
z-index: 0;
}
/* The centred container that holds everything. */
.utci-shell {
@@ -107,7 +83,7 @@ body {
top: 0;
left: 0;
right: 0;
z-index: 10;
z-index: 100;
display: flex;
align-items: center;
justify-content: flex-end;
@@ -131,3 +107,167 @@ body {
color: #c8922a; /* brass on hover */
border-bottom-color: #c8922a80; /* faint underline */
}
/* Desktop: nav-links renders inline as part of nav flex row */
.utci-topnav .nav-links {
display: contents; /* participates in nav's flex layout transparently */
}
/* Nav logo — hidden on desktop, shown on mobile */
.nav-logo {
display: none;
}
.nav-logo-sun {
font-family: Fraunces, serif;
font-weight: 900;
font-size: 2.1rem;
color: #c8922a;
letter-spacing: -0.02em;
}
.nav-logo-scope {
font-family: Fraunces, serif;
font-style: italic;
font-weight: 300;
font-size: 2.1rem;
color: #1e1208;
letter-spacing: -0.02em;
}
.nav-logo-tag {
display: block;
font-family: Fraunces, serif;
font-style: italic;
font-size: 11px;
color: #7a5c2a;
margin-top: 2px;
}
/* Desktop: overlay hidden */
.utci-topnav .nav-overlay {
display: none;
}
/* ── Burger button (hidden on desktop) ─────────────────────────────── */
.utci-burger {
display: none;
flex-direction: column;
justify-content: center;
align-items: center;
gap: 5px;
width: 36px;
height: 36px;
background: none;
border: none;
cursor: pointer;
padding: 4px;
z-index: 110;
flex-shrink: 0;
}
.utci-burger span {
display: block;
width: 22px;
height: 2px;
background: #7a5c2a;
border-radius: 2px;
transition: transform 0.25s ease, opacity 0.2s ease;
transform-origin: center;
}
/* ── Mobile nav drawer ──────────────────────────────────────────────── */
@media (max-width: 640px) {
.utci-app {
padding: 88px 16px 24px; /* 88px = 72px nav height + 16px breathing room */
}
/* Show wordmark in nav bar, hide it from main content */
.nav-logo {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
position: absolute;
left: 50%;
transform: translateX(-50%);
text-align: center;
pointer-events: none;
}
.nav-logo-wordmark {
display: block;
line-height: 1;
}
.utci-title,
.utci-tagline {
display: none;
}
.utci-topnav {
position: fixed;
padding: 10px 20px;
height: 72px;
background: #f5edd6;
border-bottom: 1px solid #e0d0a8;
}
.utci-burger {
display: flex;
}
/* Hide the links by default on mobile — shown in drawer */
.utci-topnav .nav-links {
position: fixed;
top: 0;
right: -100%;
width: 220px;
height: 100vh;
background: #f5edd6;
border-left: 1px solid #e0d0a8;
display: flex;
flex-direction: column;
align-items: flex-start;
justify-content: flex-start;
padding: 80px 32px 32px;
gap: 28px;
transition: right 0.28s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: -8px 0 24px rgba(100,70,20,0.08);
z-index: 105;
}
.utci-topnav .nav-links.is-open {
right: 0;
}
.utci-topnav .nav-links a {
font-size: 15px;
}
/* Burger → X animation */
.utci-topnav.nav-open .utci-burger span:nth-child(1) {
transform: translateY(7px) rotate(45deg);
}
.utci-topnav.nav-open .utci-burger span:nth-child(2) {
opacity: 0;
transform: scaleX(0);
}
.utci-topnav.nav-open .utci-burger span:nth-child(3) {
transform: translateY(-7px) rotate(-45deg);
}
/* Overlay behind drawer */
.utci-topnav .nav-overlay {
display: block;
position: fixed;
inset: 0;
background: rgba(30,18,8,0.35);
opacity: 0;
pointer-events: none;
transition: opacity 0.25s ease;
z-index: 104;
}
.utci-topnav.nav-open .nav-overlay {
opacity: 1;
pointer-events: auto;
}
}
+33 -2
View File
@@ -94,13 +94,15 @@
color: #1e1208;
}
/* The lat/lon next to the location */
/* The lat/lon below the location */
.utci-loc-coords {
font-family: JetBrains Mono, monospace;
font-style: normal;
font-size: 11px;
color: #b09870;
margin-left: 10px;
display: block;
margin-top: 4px;
margin-left: 18px;
letter-spacing: 0.08em;
}
@@ -195,3 +197,32 @@
letter-spacing: 0.14em;
text-transform: uppercase;
}
/* ── Mobile header layout ────────────────────────────────────────────── */
@media (max-width: 640px) {
.utci-header {
grid-template-columns: 1fr; /* single column — stack title, dial, search */
gap: 20px;
padding-top: 16px; /* extra breathing room below fixed nav */
padding-bottom: 24px;
margin-bottom: 24px;
}
.header-right {
align-items: flex-start; /* left-align location on mobile */
}
.utci-title {
font-size: clamp(40px, 12vw, 60px);
}
.scope-ring {
width: 160px;
height: 160px;
margin: 0 auto; /* centre the dial */
}
.utci-search-wrap {
width: 100%; /* full-width search box */
}
}
+25 -15
View File
@@ -680,30 +680,40 @@ export function UTCIForecast() {
// • "utci-footer" — the "reading the table" note
return html`
<div class="utci-app">
<nav class="utci-topnav">
<a href="./index.html">Forecast</a>
<a href="./about.html">About</a>
<a href=${isPro
? 'https://billing.stripe.com/p/login/9B63cw7vl8k15Ei9DQd7q00'
: 'https://buy.stripe.com/9B63cw7vl8k15Ei9DQd7q00'}
target="_blank" rel="noopener noreferrer">Account</a>
<nav class="utci-topnav" id="site-nav">
<div class="nav-overlay" onClick=${() => { const n = document.getElementById('site-nav'); n.classList.remove('nav-open'); document.getElementById('nav-drawer').classList.remove('is-open'); }}></div>
<div class="nav-logo" aria-hidden="true">
<span class="nav-logo-wordmark"><span class="nav-logo-sun">SUN</span><span class="nav-logo-scope">Scope</span></span>
<span class="nav-logo-tag">See the sun the way your body does.</span>
</div>
<div class="nav-links" id="nav-drawer">
<a href="./index.html">Forecast</a>
<a href="./about.html">About</a>
<a href=${isPro
? 'https://billing.stripe.com/p/login/9B63cw7vl8k15Ei9DQd7q00'
: 'https://buy.stripe.com/9B63cw7vl8k15Ei9DQd7q00'}
target="_blank" rel="noopener noreferrer">Account</a>
</div>
<button class="utci-burger" aria-label="Open menu" aria-expanded="false" id="burger-btn"
onClick=${() => {
const nav = document.getElementById('site-nav');
const drawer = document.getElementById('nav-drawer');
const btn = document.getElementById('burger-btn');
const open = nav.classList.toggle('nav-open');
drawer.classList.toggle('is-open', open);
btn.setAttribute('aria-expanded', open ? 'true' : 'false');
}}>
<span></span><span></span><span></span>
</button>
</nav>
<div class="lens-bloom-a"></div>
<div class="lens-bloom-b"></div>
<main class="utci-shell">
<div class="utci-header">
<div>
<h1 class="utci-title">
<span class="title-sun">SUN</span><span class="title-scope">Scope</span>
<sub class="title-beta">beta</sub>
</h1>
<div class="utci-tagline">See the sun the way your body does.</div>
<div class="utci-subtitle">
<a href="https://utci.org/" target="_blank" rel="noopener noreferrer" class="utci-subtitle-link">Universal Thermal Climate Index</a>
· Bröde 2012 · Open-Meteo · SunScope soak-factor
</div>
<div class="utci-current-loc">
${location.name}
<span class="utci-loc-coords">
+34
View File
@@ -498,6 +498,29 @@ export function ScopeReticle({ value, cat, loading, elev = 0, dt = new Date(), g
<stop offset="0%" stop-color=${glowColor} stop-opacity="0.12" />
<stop offset="100%" stop-color=${glowColor} stop-opacity="0" />
</radialGradient>
<!-- 3D lens: inner rim shadow — dark around the edge, transparent centre -->
<radialGradient id="scope-rim-shadow" cx="50%" cy="50%" r="50%">
<stop offset="72%" stop-color="transparent" />
<stop offset="100%" stop-color="rgba(20,10,0,0.38)" />
</radialGradient>
<!-- 3D lens: specular highlight — bright crescent top-left -->
<radialGradient id="scope-specular" cx="36%" cy="28%" r="42%">
<stop offset="0%" stop-color="rgba(255,255,255,0.28)" />
<stop offset="55%" stop-color="rgba(255,255,255,0.06)" />
<stop offset="100%" stop-color="rgba(255,255,255,0)" />
</radialGradient>
<!-- 3D lens: subtle bottom-right counter-glow for depth -->
<radialGradient id="scope-depth" cx="72%" cy="76%" r="38%">
<stop offset="0%" stop-color="rgba(180,120,20,0.10)" />
<stop offset="100%" stop-color="rgba(180,120,20,0)" />
</radialGradient>
<!-- Specular glint: feathered radial gradient, top-left -->
<radialGradient id="scope-glint" gradientUnits="userSpaceOnUse"
cx="62" cy="52" r="24" fx="62" fy="50">
<stop offset="0%" stop-color="rgba(255,255,255,0.80)" />
<stop offset="30%" stop-color="rgba(255,255,255,0.30)" />
<stop offset="100%" stop-color="rgba(255,255,255,0)" />
</radialGradient>
<clipPath id="scope-lens-clip">
<circle cx=${cx} cy=${cy} r=${lensR} />
</clipPath>
@@ -574,6 +597,17 @@ export function ScopeReticle({ value, cat, loading, elev = 0, dt = new Date(), g
</>`}
<circle cx=${cx} cy=${cy} r="5.5" fill="#f5edd6" stroke="#c8922a" stroke-width="1.5" />
<circle cx=${cx} cy=${cy} r="2.5" fill="#c8922a" />
<!-- 3D lens overlays: rim shadow + specular highlight + depth, all on top -->
<circle cx=${cx} cy=${cy} r=${lensR} fill="url(#scope-rim-shadow)" pointer-events="none" />
<circle cx=${cx} cy=${cy} r=${lensR} fill="url(#scope-specular)" pointer-events="none" />
<circle cx=${cx} cy=${cy} r=${lensR} fill="url(#scope-depth)" pointer-events="none" />
<!-- Specular glint: smooth feathered glow top-left, like light on curved glass -->
<circle cx="62" cy="52" r="24" fill="url(#scope-glint)" pointer-events="none" />
<!-- Outer brass bezel ring with a subtle 3D bevel -->
<circle cx=${cx} cy=${cy} r="97" fill="none"
stroke="rgba(255,220,140,0.45)" stroke-width="1.5" pointer-events="none" />
<circle cx=${cx} cy=${cy} r="99" fill="none"
stroke="rgba(80,40,0,0.25)" stroke-width="1.5" pointer-events="none" />
${loading
? html`<text x=${cx} y=${cy + 5} text-anchor="middle"
fill=${readoutMutedColor} font-size="11" font-family="monospace">· · ·</text>`