The mark is a quantized climb through the temperature-band tokens
(cold -> cool -> normal -> warm -> hot -> very-hot) to a rec-hot
terminal at a dashed record line, on the dark rounded tile.
- favicon.svg (canonical) + favicon-16/32/48.png fallbacks; the 16px
raster drops the whisper strata and record dash so the climb survives
- apple-touch-icon.png 180 rendered edge-to-edge (iOS supplies rounding)
- maskable 192/512 scale the artwork 0.78 about center so every element
clears the 40%-radius circular safe zone; any-purpose 192/512 and the
512 OG logo.png regenerated; reused filenames cache-busted with ?v=2
- header lockup: inline glyph referencing var(--cold) etc so a palette
change propagates, beside a lowercase 650-weight wordmark
- PNGs are palette-quantized with the band/chrome hexes pinned exact;
full asset set is ~20 KB
Claude-Session: https://claude.ai/code/session_01KdTZCjpLeD26ZbXe6ApjpR
Make the app installable and deliver existing alert notifications as OS
push, alongside the in-app bell.
Backend:
- PushSubscription model (per-device endpoint + keys, owned by a user) and
register/unregister/test endpoints under /api/v2/push, cookie-auth scoped
to the user like the subscription routes.
- push.py: VAPID key management (env -> data/vapid.json -> generated) and a
pywebpush send helper that reports gone endpoints for pruning. No DB coupling.
- notify.py: after creating an in-app Notification, dispatch Web Push to the
user's devices (guarded — a push failure never affects the in-app write;
endpoints reported gone are pruned).
- Serve the .webmanifest with the correct media type.
Frontend:
- manifest.webmanifest + 192/maskable icons; <link rel="manifest"> on all pages.
- sw.js: push + notificationclick handlers (push-only; no fetch caching, so it
doesn't fight the existing IndexedDB cache). Registered globally in nav.js in
secure contexts.
- push-client.js + a "Notifications on this device" toggle and test-send on the
/alerts page, subscribing through the existing cookie-aware apiFetch.
Push and service workers require a secure context, so this is active over HTTPS
(or http://localhost) and cleanly no-ops on a plain-HTTP LAN origin.