"Terribly high CPU usage, the MacBook gets really hot" - a fanless MacBook
Air on a 144Hz screen. Measured on that machine with headed engines
(process CPU plus the per-process GPU time macOS keeps): an idle Aero
library cost LibreWolf ~110% CPU and ~230ms/s of GPU, the study feed ~130%,
Chromium up to ~57% CPU and ~350ms/s of GPU, Safari ~220-250ms/s of
WindowServer GPU. Priced one by one:
- the gradient mask on .ice-floats re-rendered the animated layer offscreen
on every frame (~75 points of Gecko CPU). The floats now rise through a
plain rectangular band clear of the header, the bottom chrome and the
rail, and fade in and out inside it, so the clip never cuts a visible one;
- the overlay grain forced a backdrop readback per frame for a texture that
measured a mean <=0.11/255 difference: removed;
- the invisible blob drift made every frame a full-screen redraw: the blobs
are painted once;
- static glass (answer tiles, feedback panel, chips, inputs, dropzones,
lists, segmented controls, Aero hint chips) re-blurred the moving scene
every frame: only floating glass blurs now, and Aero's baked chat chrome
drops a live blur that never showed under the bake;
- the scene also parks while the window has no focus and under any dialog
or sheet scrim (.scene-pause); the loading icon is a compositor-run CSS
animation instead of a framer-motion main-thread loop.
After, focused and animating: LibreWolf library 109% -> 33% CPU, study
134% -> 34%, chat 130% -> 42%; Chromium GPU 2.5-4.4x lower with 7-22 points
less CPU; Safari's WindowServer GPU -20-26% in Aero, about half in dark.
Unfocused or under a sheet: nothing. bubbles-test sweeps every float through
41 phases against the band and pins the parking and the cost contract;
tests/heat-probe.mjs (macOS) repeats the measurement; the suites find
Playwright's Chromium on macOS.
All 15 suites pass, lint clean, the contrast scan lost a finding. Version
0.6.7, SW v89.
Co-authored-by: Cursor <cursoragent@cursor.com>
The header and composer were full-bleed bands (flush to the top/right/bottom
viewport edges, composer corners off-screen) next to a rail that floats with a
12px margin. The new --layout-gutter (12px) spaces the rail, header, empty-state
card and composer identically on every side; --radius-panel/-control/-chip
(-pill) replace the ad-hoc radii across the shared classes, so every panel is
rounded on all four corners and shared controls match in every theme. The
desktop-only ::after hairlines and the left-corner-only radius are gone.
Aero: the rail and the chat chrome (answers included) share one even recipe -
the wallpaper extra-blurred into a hue-free wash (background-glass.webp) under a
uniform ~50% white veil and a whisper of gloss. Measured within ~10 RGB units
across the rail top->bottom and the card/composer left->right; the blue->green
smear and the teal composer are gone. The composer's field is a clean even tile
with no wallpaper gradient inside it.
Chat fixes: send is a filled primary tile shaped exactly like attach (dimmed
while the input is empty), suggestion/context chips use the shared .chip token
(40/44px target, visible rim, full-contrast text, raw model id in the tooltip),
the AI note reads at 12px --text-muted (.ai-disclaimer), the selected sidebar
row carries the accent border + accent icon, the theme toggle is back in Aero
(the moon flips to Dark; Settings brings Aero back), and the empty state is
centered exactly between header and composer.
Outside CSS: the wallpaper's top-left HUD-circle decoration (it peeked through
the 12px gutters on wide windows) was healed out of background.webp and
background-blur.webp; the orbs/bubbles ride one masked .ice-floats layer that
fades them under the chrome bands and the rail strip, so none is sliced by a
panel edge (still parked under reduced motion).
Version 0.6.0, SW -> freezmo-v82. Tests: chat suite pins the new geometry (12px
gutter, 24px corners, even wash on all four panels, send-vs-attach shape,
chips/input radii); mobile pins the phone chrome and the Aero toggle; bubbles
pins the floats layer + its reduced-motion opt-out.
Bubbles: ten -> eighteen (interleaved, 18px to 96px, 10-23s runs), with a
tighter specular hotspot, a reflected-light glint low on the sphere, a 1px
glass edge and a brighter inner rim on both the bubbles and the drifting
orbs. Bodies stay translucent with capped opacity, so wallpaper text stays
readable; bubble-perf shows no added frame cost.
Chat: the conversation scrolls up to the glass header, and a line clipped
exactly at that edge read as a rendering bug over the busy wallpaper - the
scroller now carries a 14px top mask in Aero, shorter than the column's
16px top padding so a fresh conversation shows no dimming.
Contrast 0 findings, axe clean, chat 101/101, mobile 123/123 (bubble count
pinned to 18), regression 44/44, i18n 13/13, native 33/33.
Version 0.3.7, SW -> freezmo-v66.
Frutiger Aero, closer to the mock:
- The scene is water now: sun shafts through a bright surface, caustic
patches, reeds on the left, a city silhouette on the right, and ten soap
bubbles that actually rise (own keyframes, varied sizes/speeds/sway; the
spans are display:none outside aero so they cost nothing elsewhere, and
they sit still under reduced motion).
- Pale blue glass at 0.8: still reads as glass, but dark navy text clears
4.5:1 over the water. The phone tab bar mixes the sheet glass instead of
the page — in aero the page *is* the water, which left its labels at 3.9:1.
- The aero page-level text (muted/subtle) is much darker for the same reason.
- The PWA status bar follows the new water blue (#2fa7e6).
The cookie notice is floating glass now (blurred backdrop, rim, shadow): as a
plain card it read as part of the page background instead of a notice.
Czech: "Tvoje mraznička" -> "Tvůj mrazák" (library title), test updated.
Contrast: 0 below-minimum findings and 0 axe violations across every screen,
all three themes. Version 0.2.1, SW -> freezmo-v55.
- Orbs rise from below the bottom edge to above the top edge, each with
its own 36-68s pass (slower on phones), a negative delay (scene already
populated on load), own horizontal position, a gentle sway and a slight
scale drift; opacity fades in/out at the edges so the loop never pops.
- Only transform + opacity animate, each orb on its own composited layer
(will-change: transform); no filter/backdrop-filter, nothing animates
size/offsets/shadow. The glacier blobs are untouched.
- Opacity lowered ~38% in both themes: 0.55->0.34 light, 0.8->0.5 dark,
phone overrides 0.32->0.2 / 0.42->0.26.
- prefers-reduced-motion: rise removed, orbs return to the old static
placements (a paused-from-start CSS animation is not applied in Chrome
and would park them below the fold); tab hidden: a visibility listener
parks the whole scene (.ice-paused), blob drift included.
- Measured with a CDP perf trace on a 4x throttled phone profile: p95
33.4ms, 0 frames over 34ms, no long tasks - identical to the pre-change
baseline; ~0.6% extra main-thread CPU. Desktop stays compositor-bound
from the pre-existing blob drift, as before. tests/bubble-perf.mjs
keeps it repeatable.
- New bubbles suite (34 checks) in the repo harness; the axe contrast
matrix is now 16 runs (360/390/1280 x dark/light x EN/CS) and passes.
- SW cache -> freezmo-v31; README decision 46.
Full suite: 11/11 suites, 424 checks.
Rebuilds the look around the spec: an ice scene, one shared glass
recipe, icy accents and glass on every large surface. Visual only —
layout, behaviour, copy and functionality are untouched.
Background
- .ice-scene: fixed, z-index -1, pointer-events none. Navy base
(#070b16 to #0c1426) in dark, pale (#f4f9ff to #e6f1fb) in light,
four drifting glacier blobs (#38bdf8/#60a5fa/#818cf8, 30-60s
transform-only loops), five glossy ice orbs (40-220px radial
gradients with a highlight at 30% 25% and a blue shadow) placed so the
glass blurs them, and an SVG feTurbulence grain at 4-5%.
- prefers-reduced-motion stops the drift; phones get two blobs and two
orbs fewer.
Glass
- One shared recipe (.glass-panel, .card, .sidebar-surface,
.study-chrome, .tabbar, .glass, .gate-card): rgba(255,255,255,.06)
dark / .55 light, blur(24px) saturate(140%) (16px on phones), a bright
1px rim, an inset top highlight and a blue-tinted outer shadow.
- Nested glass (chips, inputs, secondary buttons) is more opaque and
never adds a second blur layer. @supports fallback for no
backdrop-filter. Radii: 24-28px cards/sheets, 14px inputs/buttons,
pills for chips. Cards hover brighter + cyan rim + 2px lift, 180ms.
Colour and type
- Accent is glacier cyan (#38bdf8) to ice blue (#0ea5e9); progress bars
and selected states use the gradient; the active sidebar item is a
frosted highlight with a cyan rim.
- Primary CTA: icy gradient — white to #bfe3ff with ink text in dark,
deep blue in light (biased so the label sits on the darker half and
keeps 5.6:1).
- Leftover slate/black greys replaced with navy-tinted frost tokens.
- Headings are semibold with tighter tracking.
Screens
- Dashboard: the sidebar is one tall inset rounded glass panel; deck
cards are glass.
- Gate: one centred glass card over the scene, orbs visible behind it.
- Sheets/modals: glass over the dimmed, lightly blurred backdrop.
- Study view: question card more opaque (.glass-solid); top bar and
pager are slim glass pills.
Perf: measured on a phone viewport with 4x CPU throttling. The animated
blobs' filter: blur() was the whole cost (150ms vs 33ms per frame), so
they are soft radial gradients with no filter now; idle 60fps, ~17ms
median while scrolling.
Tests updated for the new tokens: contrast 18/18 AA, deck chrome 15/15,
key gate 49/49, install sheet 64/64, deck fixes 15/15, retry 14/14,
zoom 6/6, final ux 12/12, security 8/8, pwa 16/16.