Drafted from the last month of real work - OpenShift fleet hardening, edge AI
and GPU, network policy enforcement, supply-chain security, automation and
observability - weighted toward the work stack the site advertises. Each has
a photo prompt in the house palette and a diagram brief.
Adds BLOG-LEDGER.md, generated from disk so it cannot drift, listing every
live and drafted idea in one place. Search it before drafting; claim the idea
when the folder is created rather than at publish, so two sessions cannot
collide. Expands the series from three arcs to six now the catalogue is deep
enough to split edge AI, automation and observability out of PLATFORM.
The inline SVG thumbs were placeholder-grade next to real art. Cards now use
a 640px mini of the same cover (9-22KB), object-fit cover with a slow hover
zoom, and a corner scrim so the DOSSIER tag stays legible over the brighter
frames. Falls back to the sketch for any project without a cover.
Six generated covers wired in via an optional cover key - 2528x1696 sources
optimised to 1600px webp, 49-144KB each. They sit on the page background
rather than the diagrams' light card, since the art is already generated in
the site palette on near-black. Cover carries the story at a glance, the
diagram still carries the architecture below it.
gpu-as-code and global-infra-modernisation pointed at diagrams that do not
exist, rendering a dead image box on a light card. Removing the key is
better than a broken frame until real art lands. Prompt pack for all six
covers ships alongside, in the blog-hero visual language.
The hero and full-row tiles stretched to their grid height with nothing
between blurb and chips - reading as missing body. Adds a points field
rendered as accent-dashed proof lines (flagship gets four, observability
three), each grounded in the running estate. Category titles step up to
23px/700 with the flagship at 30px so the hierarchy holds.
Extends the lone security thread into three arcs that read as skill pillars -
SECURITY (6), NETWORKING (5), PLATFORM (7) - so every post carries a numbered
badge and the catalogue advertises three areas of depth rather than a loose
list. Adds the badge to the post detail header too, styled to match the cards.
The five carried the hero only as an inline markdown image, but the index
cards and the post hero figure render from hero/heroAlt frontmatter - so
the cards showed no image and the post pages lost the hero treatment.
Moved each hero into frontmatter and dropped the inline line (the body
keeps only the diagram), matching every earlier post.
npm audit fix - 13 advisories incl. astro high and tar critical, all
non-breaking, gates re-run green locally. The dist secret scan greps
password-adjacent patterns, so the post now says htpasswd file (which the
registry auth actually is), credential vault, and lock-of-its-own.
Landscape phone (720-1000px band) put the parent grid at 2 columns while the
tile spans (hero span-2, full span-3) only reset at 720px. A span-3 tile in a
2-col grid forced a phantom 3rd column and collapsed the 1fr tracks to
58px 58px 760px, squishing the flagship card to ~132px off the visible area.
Move the span reset to the same 1000px breakpoint the grid drops to 2 columns:
now 446px 446px, hero full-width. Desktop 4-col + portrait 1-col unchanged.
Content: shipping-this-site + init-gating-gpu-readiness get their edited-workshop bodies (from the
NAS Published/ pass) - site frontmatter (hero/heroAlt) kept, only prose replaced.
Dossiers 004-006 (gpu-as-code, global-infra-modernisation, network-automation-fleet) were all
rendering the same generic pipeline thumbnail - added 3 tailored DossierThumb motifs (gpu die +
passthrough lanes, region globe, switch-fleet grid) via a new diagram: frontmatter field. Also
closed the dead-space gap above the tag row on short-argument cards with a faint on-theme grid
fill (::before, masked fade) instead of a flat void.
New post: broker-pattern ('An agent should never hold the key it's using') - SECURITY SERIES 02,
between secret-zero and workload-least-privilege. Hero de-watermarked from the NAS source (resized
to the calibrated 1600x1073 coordinate space, Jacobi-diffused out the Nano Banana sparkle at its
known center) via scripts/dewatermark-broker-pattern.mjs (reusable for the next 3 drafts). New
diagram/broker-pattern.svg in the house light-card palette. Inline top-of-body hero stripped per
the astro-static-site skill guard (frontmatter hero: is now the only render path).
The links were display:none under 720px (desktop-only nav). Phones now wrap the bar: brand +
theme toggle row 1, the five tabs row 2 (mono 0.78rem, overflow-x scroll, hidden scrollbar).
Media block placed AFTER the base rules - same-specificity source order was eating the first attempt.
Promotes the blog art identity to the whole site. Fleet constellation hero (six real nodes,
dual-trace packets, typing fleet facts), Chakra Petch + JetBrains Mono self-hosted, bento
capabilities, dossier work cards, security-series badges, featured blog index, GitOps receipt
footer, native view transitions, light blueprint theme. CSP strict throughout; blog content
byte-identical.
Astro scopes compound selectors, so [data-theme=light] .hero__scrim inside a component style
block could never match html[data-theme] - the light theme rendered dark scrims (embossed
low-contrast copy). Wrap the theme prefix in :global() in Hero, ProjectCard and the blog index.
Light 'blueprint' hero now reads clean.
Breadcrumb eyebrow (~/jwright/blog), 'Notes from the build.' H1, featured card for the series
opener (text left / hero art scrimmed right, series badge, eager-loaded), remaining posts in the
image-led responsive grid with series badges + reading time. Post CONTENT untouched (index only).
02 Capabilities becomes a 4-col bento (Edge AI 2x2 flagship w/ magenta corner radial, Platform
wide, magenta-flavoured security tile, Observability row-completer) driven by span/flavor fields
in skills.ts. 03 Selected Work becomes dossier cards: house-style SVG thumbnails (per-project
motif via DossierThumb), DOSSIER/00N corner tags, 'the argument' line from the outcome field,
chip tags, mono meta footer. 04 Writing cards gain the security-series badge (data/series.ts)
and sit three across. Section numbers glow cyan; a circuit divider (gradient hairlines + two
diamonds) closes the hero. Footer opens with the GitOps receipt - git sha + build date baked at
build time via lib/build-info.ts, zero runtime calls.
Chakra Petch site-wide (300-700) + JetBrains Mono variable, all self-hosted (CSP zero-external
holds). Token layer gains the mock-exact text tiers, glass panel, cyan-tinted borders, glows,
tighter radii; light 'blueprint' variants included. New Constellation.astro: the six real nodes
as IC chips on dual-trace PCB paths (cyan out / magenta back, dasharray packets), radar ring on
the cave hub, vias at bends - pure SVG + scoped CSS keyframes, full reduced-motion fallback.
Hero rebuilt: scrim stack + masked grid over the board art, two-line gradient nameplate, typing
bar cycling six real fleet facts via the external site.js (facts from src/data/fleet.ts), scroll
indicator. Facts wrap, never clip.
On pages with no scroll-spy sections, activeIdx stays -1 and indexOf(link)
is also -1, so the pointerleave 'keep lit if active' check matched every
hovered tab. Guard with activeIdx >= 0 so a hovered tab always retracts when
nothing is active.
- Projects/Blog had no data-nav-id so the scroll-spy skipped them entirely;
point them at the homepage #projects/#writing sections so all five animate
- Drive the underline declaratively (re-assert all on each change) so none can
be left stranded mid-spring
- Slide follows scroll direction: activating underline grows from its trailing
edge while the rest collapse toward the leading edge (down→right, up→left)
- Nav underline highlighted the wrong section at the bottom (the short last
section never crossed the threshold) — force the last section when scrolled
to the bottom so Contact lights up instead of Skills
- Scroll-progress bar: compute progress from scrollY/maxScroll so it spans a
true 0→1 (Motion's default topped out ~0.89 at the bottom)
- Writing section: add blog tag chips linking to /blog/tags/<tag>/
- Scroll-progress bar: drive scaleX from scroll progress via callback form;
the animation form snapped the bar to 0 at the bottom (progress=1 boundary)
- Nav active-underline: recompute active section from scroll position each
frame so it tracks upward scroll too (inView-enter died on the way back up)
- Add a Writing section to the homepage with recent posts + a blog CTA
- New projects tagline + open-minded lead
- Split the combined infra project: keep the estate/cloud modernisation,
add a dedicated Network Automation at Fleet Scale case (Unimus + NetBox,
config backup, bulk push, vaulted+rotated credentials) from the Linde role
- Add the secret-zero bootstrap-chain diagram (was a broken image ref)
- Remove the corner sparkle artifact from all six blog hero images
- Bump hero/diagram cache-bust params so the updated assets serve
outbound-only-exposure (corrected sealed-vault image) + secret-zero (the seed-
key circuit-tree, _R revision). Both verified against their prompts, optimized
to webp (17KB / ~120KB). Completes hero coverage for all 6 posts.
Add an optional hero field to the blog schema; render it on the post page and
as the index card thumbnail. Ship 4 verified Nano Banana hero images (shipping/
init-gating/authentik/workload) as optimized webp (64-134KB). Publish the
secret-zero post. (outbound hero pending — its file was a dup of shipping;
secret-zero hero pending generation.)
Themed thin scrollbar (cyan→magenta thumb, Firefox + WebKit), an on-brand glow
halo on the keyboard focus ring (solid outline keeps WCAG visibility), and a 1px
inner top highlight so cards read raised at rest. CSS-only, token-pure.
Unify scroll reveals, hero entrance, nav active-underline, button springs,
the scroll-progress bar and backdrop parallax under one Motion system
(src/scripts/anim.ts), bundled into a self-hosted /_astro module so CSP
script-src 'self' holds. Progress bar + parallax now scroll()-driven and
cross-browser (was Chromium-only animation-timeline). Fail-open, reduced-motion
aware, content fully visible with JS off.
The tab logo and social preview were still on the old teal palette. Retone both
to the electric cyan→magenta theme matching the hero nameplate:
- favicon.svg: jw monogram + prompt-line + cursor on a cyan→magenta gradient.
- og.png (gen-og.mjs): name in the cyan→violet→magenta hero gradient, cyan
eyebrow/separators, magenta URL, cyan→magenta top bar + dual corner glows.
- bump ?v=2 on both refs (public/ assets are CF-edge-cached + social platforms
cache OG hard) so the new art shows immediately.
The hero name was a mostly---text gradient with a thin accent sweep; in light
mode it rested on low-contrast tones and the body text-halo washed it to a ghost.
Replace with a full cyan→violet→magenta fill driven by the theme-aware accent
tokens (deep on light, bright on dark) so it's unmissable in either mode, plus a
tight dark edge-shadow for legibility, the electric glow, and a one-shot light
sheen that parks off-screen. Opt the title out of the light text-halo.
Light mode: revert the heavy white-out scrim back to a moderate wash so the
board stays visible, and earn legibility with a soft light text-halo behind
running text instead (invisible on the solid cards, only works over the image).
Pairs with the darker light-mode text tokens.
Also append ?v=2 to every /diagrams/*.svg reference so the recoloured diagrams
bypass Cloudflare's 4h edge cache and show immediately (the SVG URLs aren't
content-hashed like the CSS bundle).
Body text washed out in light mode — the secondary/tertiary text tokens were
too light against the bright sandstone board image. Darken light --text-dim and
--text-faint, and heavily strengthen the light-theme scrim so the board drops
to a faint texture behind content. Dark mode unchanged.
The diagrams still used the old teal/green/blue scheme. Remap all six to the
electric theme: teal #0d9488 → cyan #0c8fce (primary), blue #3b6fb0 → violet
#7c3aed (containers/boundaries), green #2e8b57 → magenta #c026d3 (services).
Neutral slate + navy kept for lines and text; light card unchanged so the
fixed-colour art stays legible in both themes.
CSS animation-timeline: scroll() is Chromium-only, so the backdrop didn't move
for non-Chromium browsers. Replace it with a tiny rAF-throttled scroll handler
in site.js (the existing same-origin script — CSP stays strict, no inline JS)
that drives a --bg-shift CSS var off scroll position. The board now pans
vertically in lock-step with scroll and sits still at rest, in all browsers.
The backdrop's only motion was the scroll-driven pan (animation-timeline:
scroll()), which is Chromium-only — so in Firefox/Safari the board sat dead
still. Add a continuous timed top↔bottom drift (48s, alternates) as the
universal baseline; keep the scroll-coupled pan as a Chromium enhancement that
overrides it. Motion-safe: both gated behind prefers-reduced-motion.
The S→E→SE→NE loop wandered back near its start, so on a long page the
per-screen movement was imperceptible (worse once the image was zoomed to
215%). Replace it with a single continuous vertical pan (~40% travel) so the
board obviously drifts downward as the page scrolls.
- Backdrop image scaled 168% → 215% so the Bat-Computer board reads bigger
and more immersive, with more room for the scroll-pan.
- Retitle the least-privilege post "The most secure credential is the one
you never mount" → "Every pod holds a key to a door it never opens" so it
no longer mirrors the "...inbound port is the one you never open" post.
Bumps astro to 7.0.3 and @astrojs/check to the matching release. Vite 8
under the hood; @tailwindcss/vite/sitemap/rss already compatible, Node 22
satisfied. Verified end-to-end: typecheck (0 errors), build, dist scan,
audit gate, and visual render (hero + blog + Shiki) all clean under the new
Rust compiler + Markdown pipeline.