Promoted 14 new tokens (--accent-hover, --signal-{good,bad,warn}-hover,
--accent-bright, --thumb-black, --overlay, --shadow, --ease-out-{quart,expo},
--dur-{fast,normal,slide}, --z-topbar) and substituted every raw oklch /
cubic-bezier / hardcoded z-index occurrence in the 12 primitive files.
cubic-bezier appearances dropped from 8 files to 0 (only in tokens.css).
Bundle byte count: 138 KB -> 139 KB. Visual regression: zero (smoke page
still renders identically).
Three bugs found during task 20 verify, all fixed:
1. Tailwind CLI does NOT read postcss.config.js. Switched Dockerfile to
npx postcss + postcss-cli so the postcss plugin chain actually runs.
2. postcss-import was not installed but app.css uses @import for the
primitive component files. Added postcss-import + cssnano (for prod
minification under --env production).
3. @import statements must come BEFORE any other rules per CSS spec.
app.css had @tailwind base/components ABOVE @import, so postcss-import
silently skipped every component @import. Moved all @imports to the
top, @tailwind directives below. Bundle went from 121KB with 0 wd-*
classes to 138KB with 116 wd-* classes.
Also added tailwind safelist for wd-/is-/nav-dev-badge so the wave-2
migration of HTML files cannot accidentally tree-shake primitives.
Fonts: Inter 400/500/600 + JetBrains Mono 400/600 (woff2 from rsms/inter and JetBrains/JetBrainsMono github).
Dockerfile: two-stage build. Stage 1 (node:20-alpine) runs tailwindcss --minify to emit public/dist/app.css. Stage 2 (nginx:alpine) ships the static result.
NOTE on task 19 (nginx caching for /dist /fonts): SKIPPED. The existing nginx.conf already caches *.css and *.woff2 for 1y immutable via the generic location ~* \\.(css|...|woff2|...)$ regex. Adding explicit /dist/ and /fonts/ blocks would be redundant.
Detailed 22-task plan for the foundation wave (build pipeline + theme
port + every CSS primitive, no page migration yet). Smoke-test page at
_primitives-smoke.html is the wave-1 QA gate. Waves 2-4 will get their
own plan documents after each prior wave ships.
Self-review caught an ordering ambiguity in the responsive section: 1280x800
is the fully-supported minimum, tablet 768-1279 is best-effort. Rewording
so the tiers list top-down by viewport size.
Full design spec for the flyon-ui-based shell rework. All 7 design
sections (build system, sidebar, topbar, cards, forms/slide-panel,
states/motion, a11y/responsive/rollout) approved by user during
brainstorming. Next step is the implementation plan via writing-plans.
Flex-child overflow footgun: .slide-panel-body had flex:1 and overflow-y:auto
but without min-height:0 it never shrank below content height, so the new
Master/Proxy codec blocks overflowed past the panel bottom and the footer
(Cancel / Probe / Save buttons) was unreachable. Lock the panel to 100vh,
add min-height:0 to the body. Also drop redundant margin-bottom on
.codec-block since the body already has gap spacing.
The timeline editor isn't ready yet. Replace the 49 KB prototype page
with a clean construction screen (still rendering the standard sidebar
so users can navigate away). The 'IN DEV' badge on the sidebar nav item
is injected by auth-guard.js across all pages.
Adds a tiny CSS rule + DOM patch that walks .nav-item links on every
page and appends an 'IN DEV' badge to those matching a known in-dev
page (currently just editor.html). Avoids touching all 13 HTML files
for the same single-line nav change.
- Replace flat codec dropdowns with Master/Proxy blocks, each with
Video/Audio/Container tabs.
- Replace BM1/BM2 device dropdown with cluster-node picker plus
inline BMDCards.render(...) SVG -- click a port to set device_index.
- Wire full codec field set (video bitrate, framerate, audio codec/
bitrate/channels, container) end-to-end to /api/v1/recorders.
- Auto-hide bitrate input for profile-driven codecs (ProRes, DNxHR,
PCM, FLAC); show for H.264/265/NVENC, AAC, AC-3, Opus, DNxHD.
- Resolve SDI source display in cards via /cluster/devices/blackmagic
(hostname + model + port) instead of raw device index.
Finishes the pending item from
docs/superpowers/plans/2026-05-21-cluster-codec-revamp.md.
Captures the full commit list, current cluster state, the 172.18 LAN topology gotcha, the remaining recorders.html UI rewrite, and how the next agent should pick it up.
mam-api self-heartbeat now reads NODE_HOSTNAME so primary rows survive container restarts instead of resurrecting with the random container ID. test-cluster.sh rewritten to use jq (the python f-strings had a parse bug that silently passed the IP check) and limited the docker-bridge alarm to 172.17.x since the user LAN occupies 172.18.0.0/16.
Resolves the host's primary LAN address with `ip route get` so the
node-agent reports it in the heartbeat instead of a docker bridge IP.
Adds BMD_MODEL forwarding so the recorder UI knows which card to draw,
and reads back the agent's /health response to confirm the resolved IP.
Renders an inline SVG of the detected card (Duo 2 / Quad 2 / Mini
Recorder / Mini Monitor / UltraStudio 4K Mini, with a generic fallback)
showing each connector in its real physical position. Click to select.
Used by recorders.html for SDI source selection.
Adds a VIDEO_CODECS / AUDIO_CODECS / CONTAINER catalogue and a
buildEncodeArgs() that composes -c:v / -c:a / -b:v / -b:a / -r / -ac / -f
from the recorder's saved settings. Master and proxy each get their own
codec stack, both honour the container format chosen in the UI, and the
S3 keys now use the actual container extension instead of hardcoded mov/mp4.