Files
Pete/internal/web/templates/layout.html
T
prosolis a15d67e210 web: the muted text that was never muted, and the stylesheet fetched every time
Every text-[color:var(--ink)]/50, border-[color:var(--ink)]/10 and
bg-[color:var(--ink)]/5 in the templates compiled to nothing. Tailwind v3
cannot put an opacity on an arbitrary var() colour, so all 538 of them were
dropped from output.css without a word. Meta text rendered at full ink, card
borders fell back to Tailwind's default grey (bright white rings at night),
and the washes, backdrops and hover pills simply weren't there, on the news
site, the casino and the adventure pages alike.

The palette is now written once per phase and casino room as bare rgb
channels (--ink-rgb and friends), the plain vars are built from them, and
tailwind.config.js exposes ink, muted, accent, warn and card as real colours.
The classes are text-ink/80, border-ink/10, bg-accent/20 now, and they compile.

Restoring the intended opacities as-is would have traded flat for illegible:
ink at 50% measures 2.7 to 2.9:1 on the lit phases. So secondary text gets
its own colour, --muted, per phase and per casino room, taken as faint as it
can go while still clearing 4.5:1 on both the card and the page. Everything
that was ink at 35 to 70% is text-muted. The 20-30% ones are separators and
empty-cell dashes and stay faint.

The bg-theme-* chips carry white text everywhere they appear, and eight of
them measured 2.5 to 3.2:1. Same hues, taken just dark enough to pass (4.6 to
4.7:1). eu, lego and adventure already passed and are unchanged, and the
underline and border colours keep the old pastels since they carry no text.

Static files came out of the embedded FS with no Last-Modified, no ETag and
no Cache-Control, so every page view downloaded the stylesheet and every
script in full, and the service worker's "cheap 304" never happened. Each
file is hashed once now. Templates link through {{asset}}, which appends
?v=<hash>; a request naming the current hash is served immutable for a year,
and everything else gets the hash as its ETag and revalidates to a 304. The
service worker serves versioned URLs cache-first, drops older versions of the
same file as new ones land, and moves to cache v6.
2026-09-22 22:54:50 -07:00

357 lines
22 KiB
HTML

{{define "layout"}}<!doctype html>
<html lang="en" data-phase="{{if .Phase}}{{.Phase}}{{else}}day{{end}}"{{if .Phase}} data-phase-lock{{end}} data-season="{{.Weather.Season}}" data-weather="{{.Weather.Variant}}" data-intensity="{{.Weather.Intensity}}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{block "title" .}}{{.SiteTitle}}{{end}}</title>
{{if .NoIndex}}<meta name="robots" content="noindex">{{end}}
{{if .OGImage}}<meta property="og:image" content="{{.OGImage}}"><meta name="twitter:card" content="summary_large_image">{{end}}
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Fredoka:wght@400;500;600;700&family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="{{asset "/static/css/output.css"}}">
<link rel="icon" href="{{asset "/static/img/pete.avif"}}" type="image/avif">
<link rel="manifest" href="/manifest.webmanifest">
<meta name="theme-color" content="#fbf3e3">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="Pete">
<link rel="apple-touch-icon" href="{{asset "/static/img/apple-touch-icon.png"}}">
<link rel="alternate" type="application/rss+xml" title="{{.SiteTitle}} — all stories" href="/feed.xml">
<link rel="alternate" type="application/feed+json" title="{{.SiteTitle}} — all stories" href="/feed.json">
{{if .Active}}{{if ne .Active "bookmarks"}}
<link rel="alternate" type="application/rss+xml" title="{{.SiteTitle}} — {{.Active}}" href="/{{.Active}}/feed.xml">
{{end}}{{end}}
<script>
// Pick a palette phase from the browser clock and update it each minute.
(function () {
function phase(h) {
if (h >= 5 && h < 8) return "dawn";
if (h >= 8 && h < 17) return "day";
if (h >= 17 && h < 20) return "dusk";
return "night";
}
function apply() {
if (document.documentElement.hasAttribute("data-phase-lock")) return;
document.documentElement.dataset.phase = phase(new Date().getHours());
}
apply();
setInterval(apply, 60000);
})();
</script>
</head>
<body class="min-h-screen bg-[color:var(--bg)] text-ink transition-colors duration-1000">
<div class="pointer-events-none fixed inset-0 -z-10 bg-[color:var(--bg-grad)] transition-colors duration-1000"></div>
<canvas id="pete-weather" class="pointer-events-none fixed inset-0 -z-[5] h-full w-full" aria-hidden="true"></canvas>
<header class="mx-auto max-w-6xl px-4 pt-8 pb-4 sm:pt-12">
<div class="flex items-center justify-between gap-3 sm:gap-4">
<a href="/" class="flex items-center gap-3 group min-w-0">
<img src="{{asset "/static/img/pete.avif"}}" alt="Pete" width="48" height="48"
class="h-12 w-12 shrink-0 rounded-2xl bg-card shadow-pete border-2 border-ink/10 object-cover group-hover:-rotate-6 transition-transform">
<span class="font-display text-3xl font-bold tracking-tight">{{.SiteTitle}}</span>
<span class="hidden sm:inline rounded-full bg-accent/20 px-3 py-1 text-xs font-semibold uppercase tracking-wider text-muted" data-phase-label>day</span>
</a>
{{if .AuthEnabled}}
{{if .User}}
<a href="/auth/logout" data-account
title="Signed in as {{.User.Display}}{{if .User.Email}} · {{.User.Email}}{{end}} — sign out"
class="inline-flex shrink-0 items-center gap-2 rounded-full bg-card px-2.5 py-1.5 text-sm font-semibold shadow-pete border-2 border-ink/10 hover:bg-ink/5 transition">
<span class="grid h-6 w-6 place-items-center rounded-full bg-accent text-white text-xs font-bold">{{.User.Initial}}</span>
<span class="hidden sm:inline max-w-[7rem] truncate">{{.User.Display}}</span>
</a>
{{else}}
<a href="/auth/login?next={{.Path}}" data-signin
title="Sign in to sync your preferences"
class="inline-flex shrink-0 items-center gap-1.5 rounded-full bg-card px-3 py-2 text-sm font-semibold shadow-pete border-2 border-ink/10 hover:bg-ink/5 transition">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4 text-muted">
<path d="M15 3h4a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2h-4"></path>
<polyline points="10 17 15 12 10 7"></polyline>
<line x1="15" y1="12" x2="3" y2="12"></line>
</svg>
<span class="hidden sm:inline">Sign in</span>
</a>
{{end}}
{{end}}
</div>
<div class="mt-3 flex flex-wrap items-center gap-2 sm:gap-3">
<button type="button" data-settings-trigger
title="Feed settings"
class="inline-flex shrink-0 items-center justify-center rounded-full bg-card p-2 shadow-pete border-2 border-ink/10 hover:bg-ink/5 transition">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5">
<circle cx="12" cy="12" r="3"></circle>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33h0a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51h0a1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82v0a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
</svg>
<span class="sr-only">Feed settings</span>
</button>
<button type="button" data-reader-open
title="Feed mode — read one story at a time (press f)"
class="inline-flex shrink-0 items-center justify-center rounded-full bg-card p-2 shadow-pete border-2 border-ink/10 hover:bg-ink/5 transition">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5">
<path d="M2 4h6a3 3 0 0 1 3 3v13a2.5 2.5 0 0 0-2.5-2.5H2z"></path>
<path d="M22 4h-6a3 3 0 0 0-3 3v13a2.5 2.5 0 0 1 2.5-2.5H22z"></path>
</svg>
<span class="sr-only">Feed mode</span>
</button>
{{if .Weather.Variant}}
<button type="button" data-weather-toggle aria-pressed="true"
title="Toggle weather animation"
class="inline-flex shrink-0 items-center justify-center rounded-full bg-card p-2 shadow-pete border-2 border-ink/10 hover:bg-ink/5 transition">
<svg aria-hidden="true" viewBox="0 0 24 24" class="h-5 w-5 transition-colors"
data-weather-star fill="currentColor" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round">
<polygon points="12,2.7 14.85,9.27 21.95,10.05 16.6,14.83 18.18,21.8 12,18.13 5.82,21.8 7.4,14.83 2.05,10.05 9.15,9.27"/>
</svg>
<span class="sr-only">Toggle weather animation</span>
</button>
{{end}}
<button type="button" data-weather-loc
title="Set your location"
class="inline-flex shrink-0 items-center gap-1.5 rounded-full bg-card px-3 py-2 text-sm font-semibold shadow-pete border-2 border-ink/10 hover:bg-ink/5 transition">
<span data-weather-chip class="tabular-nums">📍 Weather</span>
</button>
<span data-aqi-chip
class="hidden shrink-0 items-center gap-1.5 rounded-full bg-card px-3 py-2 text-sm font-semibold shadow-pete border-2 border-ink/10 tabular-nums"></span>
<a href="/status" data-status-link
title="Source health"
class="inline-flex shrink-0 items-center justify-center rounded-full bg-card p-2 shadow-pete border-2 border-ink/10 hover:bg-ink/5 transition{{if eq .Active "status"}} bg-accent/20{{end}}">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5">
<path d="M3 12h4l2 6 4-14 2 8h6"></path>
</svg>
<span class="sr-only">Source health</span>
</a>
{{if .User}}
<a href="/bookmarks" data-bookmarks-link
title="Your bookmarks"
class="inline-flex shrink-0 items-center justify-center rounded-full bg-card p-2 shadow-pete border-2 border-ink/10 hover:bg-ink/5 transition{{if eq .Active "bookmarks"}} bg-accent/20{{end}}">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" class="h-5 w-5">
<path d="M6 2h12a1 1 0 0 1 1 1v18l-7-4-7 4V3a1 1 0 0 1 1-1z"></path>
</svg>
<span class="sr-only">Bookmarks</span>
</a>
{{end}}
<div class="flex basis-full items-center gap-2 min-w-0 max-w-full sm:basis-auto sm:ml-auto">
<button type="button" data-search-trigger title="Search (⌘K)"
class="flex shrink-0 items-center gap-2 rounded-full bg-card px-3 py-2 text-sm shadow-pete border-2 border-ink/10 hover:bg-ink/5 transition">
<svg aria-hidden="true" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"
stroke-linecap="round" stroke-linejoin="round" class="h-4 w-4 text-muted">
<circle cx="11" cy="11" r="7"></circle>
<line x1="21" y1="21" x2="16.65" y2="16.65"></line>
</svg>
<span class="hidden sm:inline text-muted font-semibold">Search</span>
<kbd class="hidden sm:inline-flex items-center rounded-md bg-ink/5 px-1.5 py-0.5 text-[10px] font-mono font-semibold text-muted">⌘K</kbd>
</button>
<nav class="pete-channel-nav flex min-w-0 items-center gap-1 sm:gap-2 overflow-x-auto rounded-full bg-card p-1 shadow-pete border-2 border-ink/10">
{{$active := .Active}}
{{if .User}}
<a href="/for-you"
title="Your personalized feed"
class="shrink-0 rounded-full px-3 py-2 text-sm font-semibold transition
{{if eq $active "for-you"}}bg-accent text-[#1c1305] shadow-sm{{else}}hover:bg-ink/5{{end}}">
<span aria-hidden="true" class="mr-1">✨</span><span class="hidden sm:inline">For you</span>
</a>
{{end}}
{{range .Channels}}
<a href="/{{.Slug}}"
class="shrink-0 rounded-full px-3 py-2 text-sm font-semibold transition
{{if eq $active .Slug}}bg-theme-{{.Theme}} text-white shadow-sm{{else}}hover:bg-ink/5{{end}}">
<span aria-hidden="true" class="mr-1">{{.Emoji}}</span><span class="hidden sm:inline">{{.Title}}</span>
</a>
{{end}}
</nav>
</div>
</div>
</header>
<div id="pete-search" class="hidden fixed inset-0 z-50 bg-ink/40 backdrop-blur-sm flex items-start justify-center pt-[10vh] px-4" aria-modal="true" role="dialog">
<div class="w-full max-w-2xl rounded-3xl bg-card shadow-pete-lg border-2 border-ink/10 overflow-hidden">
<div class="flex items-center gap-3 px-5 py-4 border-b border-ink/10">
<kbd class="hidden sm:inline-flex items-center rounded-md bg-ink/5 px-1.5 py-0.5 text-[10px] font-mono font-semibold text-muted">⌘K</kbd>
<input type="search" data-search-input autocomplete="off" spellcheck="false"
placeholder="Search headlines…"
class="flex-1 bg-transparent text-lg outline-none placeholder:text-muted">
</div>
<div data-search-list class="max-h-[60vh] overflow-y-auto p-3 space-y-1"></div>
<div class="px-5 py-3 border-t border-ink/10 flex items-center justify-between text-xs text-muted">
<span><kbd class="font-mono">↑↓</kbd> navigate · <kbd class="font-mono">⏎</kbd> open · <kbd class="font-mono">esc</kbd> close</span>
<span data-search-meta></span>
</div>
</div>
</div>
<div id="pete-settings" class="hidden fixed inset-0 z-50 bg-ink/40 backdrop-blur-sm flex items-start justify-center pt-[10vh] px-4" aria-modal="true" role="dialog">
<div class="w-full max-w-md rounded-3xl bg-card shadow-pete-lg border-2 border-ink/10 overflow-hidden">
<div class="flex items-center justify-between gap-3 px-5 py-4 border-b border-ink/10">
<h2 class="font-display text-lg font-bold">Feed settings</h2>
<button type="button" data-settings-close class="rounded-full px-2 py-1 text-sm text-muted hover:bg-ink/5">esc</button>
</div>
{{if .PushEnabled}}{{if .User}}
<div data-push-section hidden class="px-5 pt-4 pb-1">
<div class="flex items-center justify-between gap-3 rounded-2xl bg-ink/5 px-4 py-3">
<div class="min-w-0">
<div class="text-sm font-bold">🔔 Notifications</div>
<div data-push-note class="text-xs text-muted">Get a nudge when new stories land.</div>
</div>
<button type="button" data-push-toggle aria-pressed="false"
class="shrink-0 rounded-full bg-accent px-3 py-2 text-sm font-semibold text-[#1c1305] shadow-sm hover:brightness-105 transition disabled:opacity-50 disabled:cursor-not-allowed">
Turn on notifications
</button>
</div>
{{if .AdvEnabled}}
<!-- Adventure alerts. Revealed by pwa.js only once a subscription exists,
because a category toggle with no subscription behind it is a switch
wired to nothing. Every box starts unchecked: turning on news
notifications is not consent to be told about the game. -->
<div data-adv-push hidden class="mt-2 rounded-2xl bg-ink/5 px-4 py-3">
<div class="text-sm font-bold">⚔️ Adventure alerts</div>
<div class="text-xs text-muted">Pick what's worth a buzz. Off unless you say so.</div>
<div class="mt-2 space-y-1.5">
<label class="flex items-start gap-2 text-xs cursor-pointer">
<input type="checkbox" data-adv-cat="siege" class="mt-0.5 accent-accent">
<span><span class="font-semibold">The Siege</span>: when a world boss camps outside town, and when it's settled.</span>
</label>
<label class="flex items-start gap-2 text-xs cursor-pointer">
<input type="checkbox" data-adv-cat="run" class="mt-0.5 accent-accent">
<span><span class="font-semibold">Your expeditions</span>: cleared, backed out, or worse.</span>
</label>
<label class="flex items-start gap-2 text-xs cursor-pointer">
<input type="checkbox" data-adv-cat="departure" class="mt-0.5 accent-accent">
<span><span class="font-semibold">Wandering off</span>: your adventurer got bored and left without you.</span>
</label>
<label class="flex items-start gap-2 text-xs cursor-pointer">
<input type="checkbox" data-adv-cat="contract" class="mt-0.5 accent-accent">
<span><span class="font-semibold">Contracts on you</span>: somebody paid to have something sent after you.</span>
</label>
</div>
<div data-adv-push-note class="mt-2 text-xs text-muted"></div>
</div>
{{end}}
</div>
{{end}}{{end}}
<p class="px-5 pt-3 text-xs text-muted">Uncheck a feed to hide its stories. <span data-storage-note>Saved in this browser.</span></p>
<div data-settings-list class="max-h-[55vh] overflow-y-auto p-3 space-y-1"></div>
<div class="px-5 py-3 border-t border-ink/10 flex items-center justify-between text-xs">
<button type="button" data-settings-reset class="text-muted hover:text-ink font-semibold">Enable all</button>
<span data-settings-meta class="text-muted"></span>
</div>
</div>
</div>
<div id="pete-reader" class="hidden fixed inset-0 z-50" aria-modal="true" role="dialog" aria-label="Feed mode reader">
<div class="pete-reader-backdrop" data-reader-backdrop></div>
<div class="pete-reader-shell">
<div class="pete-reader-bar">
<span class="pete-reader-progress tabular-nums" data-reader-progress>—</span>
<div class="pete-reader-nav">
<button type="button" data-reader-prev class="pete-reader-btn" title="Previous (←)" aria-label="Previous article">←</button>
<button type="button" data-reader-next class="pete-reader-btn" title="Next (→)" aria-label="Next article">→</button>
<button type="button" data-reader-listen class="pete-reader-btn" style="display:none" title="Read aloud (r)" aria-label="Read this story aloud" aria-pressed="false">🔊 Listen</button>
<button type="button" data-reader-share class="pete-reader-btn" style="display:none" title="Share (s)" aria-label="Share this story">Share</button>
<div class="pete-reader-type">
<button type="button" data-reader-type class="pete-reader-btn" title="Text options (t)" aria-label="Text options" aria-expanded="false"><span style="font-size:1.05rem">A</span><span style="font-size:.8rem">a</span></button>
<div class="pete-reader-typemenu" data-reader-typemenu hidden>
<div class="pete-reader-typerow">
<span class="pete-reader-typelabel">Size</span>
<div class="pete-reader-typebtns" data-reader-size>
<button type="button" data-size="s" title="Small">S</button>
<button type="button" data-size="m" title="Medium">M</button>
<button type="button" data-size="l" title="Large">L</button>
<button type="button" data-size="xl" title="Extra large">XL</button>
</div>
</div>
<div class="pete-reader-typerow">
<span class="pete-reader-typelabel">Font</span>
<div class="pete-reader-typebtns" data-reader-font>
<button type="button" data-font="sans">Sans</button>
<button type="button" data-font="serif" style="font-family:Georgia,'Times New Roman',serif">Serif</button>
</div>
</div>
<div class="pete-reader-typerow">
<span class="pete-reader-typelabel">Paper</span>
<div class="pete-reader-typebtns" data-reader-paper>
<button type="button" data-paper="default">Default</button>
<button type="button" data-paper="sepia">Sepia</button>
</div>
</div>
<div class="pete-reader-typerow pete-reader-voicerow" data-reader-voicerow hidden>
<span class="pete-reader-typelabel">Voice</span>
<select class="pete-reader-voice" data-reader-voice aria-label="Read-aloud voice"></select>
</div>
</div>
</div>
<button type="button" data-reader-bookmark class="pete-reader-btn" style="display:none" title="Bookmark (b)" aria-label="Bookmark this story" aria-pressed="false">🔖 Save</button>
<a data-reader-link target="_blank" rel="noopener noreferrer" class="pete-reader-btn pete-reader-btn-open" title="Open original (o)">Open&nbsp;↗</a>
<button type="button" data-reader-close class="pete-reader-btn" title="Close (esc)" aria-label="Close reader">esc</button>
</div>
</div>
<div class="pete-reader-scroll" data-reader-scroll tabindex="-1">
<article class="pete-reader-article" data-reader-article></article>
<div class="pete-reader-related" data-reader-related hidden></div>
</div>
<div class="pete-reader-hint">
<span><kbd>←</kbd> <kbd>→</kbd> navigate · <kbd>o</kbd> open · <kbd>s</kbd> share · <kbd>t</kbd> text · <kbd>u</kbd> unread · <kbd>esc</kbd> close</span>
</div>
</div>
</div>
<main class="mx-auto max-w-6xl px-4 pb-24">
{{block "main" .}}{{end}}
</main>
<footer class="mx-auto max-w-6xl px-4 pb-10 text-center text-sm text-muted">
<img src="{{asset "/static/img/pete.avif"}}" alt="" width="20" height="20"
class="inline-block h-5 w-5 rounded-full object-cover align-[-4px] mr-1">
served by <span class="font-semibold">Pete</span> · also a Matrix bot · <span data-clock>—</span>
</footer>
<script>
// Tiny clock + phase label updater for the header pill / footer, plus Pete's
// time-of-day greeting on the home hero (whichever of these exist).
(function () {
function greeting(h) {
if (h >= 5 && h < 8) return "Early start? Here's what came in overnight.";
if (h >= 8 && h < 12) return "Morning! Here's what's fresh.";
if (h >= 12 && h < 17) return "Afternoon. Here's what's new.";
if (h >= 17 && h < 21) return "Evening. Here's what you missed today.";
return "Late one? Here's the quiet-hours roundup.";
}
function tick() {
var now = new Date();
var hh = String(now.getHours()).padStart(2, "0");
var mm = String(now.getMinutes()).padStart(2, "0");
var el = document.querySelector("[data-clock]");
if (el) el.textContent = hh + ":" + mm;
var label = document.querySelector("[data-phase-label]");
if (label) label.textContent = document.documentElement.dataset.phase;
var greet = document.querySelector("[data-pete-greeting]");
if (greet) greet.textContent = greeting(now.getHours());
}
tick();
setInterval(tick, 30000);
})();
</script>
<script>
window.PETE_SOURCES = {{.AllSources}};
window.PETE_USER = {{if .User}}{ name: {{.User.Display}}, email: {{.User.Email}} }{{else}}null{{end}};
window.PETE_PREFS = {{.UserPrefs}};
window.PETE_PUSH = {{if .PushEnabled}}{ enabled: true, publicKey: {{.PushPublicKey}} }{{else}}null{{end}};
window.PETE_TTS = {{.TTS}};
</script>
<script src="{{asset "/static/js/prefs.js"}}" defer></script>
<script src="{{asset "/static/js/weather-2d.js"}}" defer></script>
<script src="{{asset "/static/js/weather-gl.js"}}" defer></script>
<script src="{{asset "/static/js/weather.js"}}" defer></script>
<script src="{{asset "/static/js/weather-forecast.js"}}" defer></script>
<script src="{{asset "/static/js/search.js"}}" defer></script>
<script src="{{asset "/static/js/settings.js"}}" defer></script>
<script src="{{asset "/static/js/reader.js"}}" defer></script>
<script src="{{asset "/static/js/pwa.js"}}" defer></script>
{{block "scripts" .}}{{end}}
</body>
</html>{{end}}