Commit Graph
18 Commits
Author SHA1 Message Date
jwinterm 887b59629d feat(splash): the pillar signature, and no more flash on refresh
Two separate problems, one of them a real bug.

THE FLASH. The splash is server-rendered, so on a refresh the browser painted
it and only then did hydration read sessionStorage, decide this visitor had
already entered, and remove it. That one painted frame is the flash, and React
cannot prevent it: even useLayoutEffect runs after the server HTML is painted.

So the decision moved before paint. A tiny synchronous script in <head> reads
the session flag and prefers-reduced-motion and stamps the root element; a
plain CSS rule hides the overlay in the frame it would otherwise appear in.
The overlay is `fixed`, so hiding it shifts no layout. Verified with a
Playwright run: after reload the root is already stamped at readyState
"interactive", the overlay computes to display:none, and the splash is not
visible in any of twelve sampled frames.

THE SPLASH ITSELF. It is now only the pillar signature: four dots become the
four pillar symbols -- commerce, crypto, games, education -- become the word
SUCH, then the page takes over. One generated asset (morph_splash*.svg) whose
CSS timeline is scaled to land exactly on 1337ms, from such-graphics 628fcc1.
SPLASH_MS here and motion.splash.ms there are the same number by construction:
a gate that closes early cuts the last letter mid-draw.

Everything else on the splash is gone -- tagline, supporting line, the four
corner nav links, the chevron, the water-refraction filter and its pre-warm.
The corner links were unreachable inside a gate this short anyway, and the
header carries them. splash-logo-data.ts goes with them, now orphaned.

Perf, measured on a local production build: mobile 97, desktop 98, unchanged
from what production scores today. CLS stays ~0. LCP moves 0.9s -> 1.2s on
mobile, because the mark is now a 12KB request where the old logo was an
inlined data URI. Both theme variants are fetched (3.7KB each gzipped) since
`dark:hidden` hides but does not prevent the download, so half of that is
waste. Fixable by inlining as data URIs the way splash-logo-data.ts did, at
the cost of ~30KB in the bundle; not done here because the score did not move.
2026-08-17 12:11:36 -04:00
jwinterm 1a66e2217a feat(splash): enter the site on its own, and stop the intro costing 26 points
The splash now runs its water-ripple enter 1.5s after paint instead of waiting
to be clicked. A click, scroll, or arrow key still fast-forwards it, and the
once-per-session flag and reduced-motion skip are unchanged.

Auto-advancing on its own would have cost the Lighthouse score badly: mobile
100 -> 73, desktop 97 -> 73. The cause was not LCP, which never moved. It was
CLS going 0 -> 1.0, and the reason is worth writing down.

The splash was `relative min-h-[100svh]`, a full viewport of height IN document
flow, and only became `fixed` on the way out. Dismissing it therefore pulled a
whole screen out of the flow and shoved the hero up: a layout shift of almost
exactly 1.0. Clicking hid that from CLS, because shifts within 500ms of user
input are excused. Nothing else did.

Which means this was already shipping to real people. Visitors with
prefers-reduced-motion, and anyone returning within a session, never click, so
they took the shift in full: production measured 74 on mobile with CLS 1.0.
The click-gate was masking a live bug, not preventing one.

The splash is now a fixed overlay from its first paint, so it is never in flow
and dismissing it cannot reflow anything. Measured on a local production build:

                          before -> after
  mobile, auto-advance     73 -> 98   (CLS 1.0   -> 0.005)
  mobile, reduced-motion   74 -> 98   (CLS 1.0   -> 0.005)
  desktop, auto-advance    73 -> 98   (CLS 0.702 -> 0.011)

Mobile gives up 2 points against today's click-gated 100, entirely in Speed
Index (1.9s -> 4.4s), which is the unavoidable cost of holding a cover at all.
Desktop ends up ahead of where it started. The hold is one constant if you want
a different beat.

The scroll-past-to-dismiss IntersectionObserver is gone: an overlay never
leaves the viewport, so it could not fire. Scroll intent still enters, via the
wheel/touch/key handlers already in the splash, and the shell now locks page
scroll while the cover is up.
2026-08-17 09:13:29 -04:00
jwinterm 261cb04667 feat(site): one voice, brand gradients, a lighter splash gate, denser portfolio
- Voice unified on the title-tag register: the homepage h1 now says what we
  sell (Custom E-commerce, Apps & Crypto Tools); 'Precision engineering for
  everyone' is demoted to the supporting line. The structured-data and
  manifest descriptions now match the OG description instead of carrying
  their own registers.
- The off-palette cyan/blue gradients are gone: .hero-title and the new
  .brand-gradient-text use the four-colour system (light palette on light,
  dark palette on dark, per the pack). Splash cover text, services and
  portfolio headings, the hero aura, and button shadows all follow.
- The splash now dismisses once 40% scrolled past instead of requiring the
  whole screen to leave the viewport (it was already once-per-session and
  reduced-motion-skipping).
- Portfolio: heading 'Portfolio', subtitle 'Products & projects', meta title
  to match; grid grows to three columns on xl in a max-w-6xl container.
- anim3_matrix slimmed: single-layer glow, unfiltered crackle and orbs, and
  the border cycles stroke colour instead of animating filter:hue-rotate --
  the filter animation forced a full filter re-render per frame and pegged a
  core on the page hosting it.
2026-08-16 00:04:28 -04:00
jwinterm c73b9f1c04 feat(brand): regenerate every mark and animation from the such-graphics pack
All branding and animation assets now come from packs/such-software/pack.json
(such-graphics feat/brand-full-blast) instead of hand-maintained files:

- anim1_pulse / anim3_matrix / anim_type_v4 (both themes): regenerated with
  the corrected U bowl, measured pulse timing (the hand-authored node delays
  ran ~17% fast), and CSS animation with real prefers-reduced-motion support.
- clean marks, lockups (clean_*_w_text), favicons (svg + 192px png), and the
  OG banner: regenerated; the OG card is the pack's 1200x630 projection.
- splash-such: rebuilt from the pack -- the old one carried the divergent U,
  an off-palette cyan frame, and the pre-correction radius. Now theme-aware:
  two inlined data URIs (hero-splash shows the light palette on light, the
  dark palette on dark) since the pack's dark hues are tuned for near-black.
- new assets available: games_pulse (the launch arch with the rocket, wired
  into /apps), morph, ambient_{atom,mesh,leaf}, favicon_dots, favicon_live,
  icon-192/512 + maskable-512 (wired into manifest.json), /favicon.ico.
- animation embeds switched <img> -> <object>: Chrome freezes CSS animations
  inside SVG-as-img (the old files animated because they were SMIL); an
  object hosts a real SVG document where CSS animation and the
  reduced-motion query both work. Verified live in Chrome.
2026-08-15 23:18:44 -04:00
jwinterm 62fee8b83a home: bento above What We Do; splash enters on scroll intent; pre-warm the water filter
The page after the splash was two screens of text before the first image. Selected
Works (the bento, with the live barns vignette) now leads and the services copy
supports it below, wrapped in the same scroll-reveal.

Splash: the first wheel tick, 24px touch drag, or scroll key now plays the SAME
water-ripple enter as a tap (scrolling past used to hard-cut after a full screen).
Listeners are passive, the entering guard dedupes, reduced-motion still gets the
instant dismiss.

Pre-warm: on requestIdleCallback (post-LCP, invisible to Lighthouse) a 1px element
paints through the splash-water filter so the graph compiles before first use (the
first-tap stutter), and the logo layer gets will-change ahead of the zoom.

Verified with Playwright against the production build: wheel/key/touch all enter,
session flag set, bento renders above What We Do.
2026-07-11 10:45:50 -04:00
jwinterm 128d154b0e Trim homepage JS: drop framer-motion from the first load; cursor lead-in blinks
- Remove framer-motion from the two components that pulled it into the homepage's
  initial bundle: HeroSplash (click-to-enter wash -> CSS keyframe; reduced-motion
  check moved into the handler) and ScrollReveal (whileInView -> IntersectionObserver
  + CSS transition). framer now only lives in the lazy nebula chunk, which never loads
  during the splash. First Load JS on / : 174 kB -> 138 kB.
- Monitor terminal: blink ~7 times before typing 'such' (so it's catchable even when
  the splash is skipped on refresh), then type/hold/backspace/blink/fade as before.

Desktop behavior unchanged; both converted components still respect reduced-motion.
2026-06-07 16:12:17 -04:00
jwinterm 065cabc38c Mobile LCP: inline splash logo as data URI; revert harmful image preload
- The high-priority <link rel=preload as=image> stole bandwidth from render-
  blocking CSS on 4G, pushing FCP 1.1s -> 2.2s and the score 82 -> 70. Removed.
- TBT is ~30ms (not main-thread bound), so the FCP->LCP gap was the logo's
  network fetch. Inline the mark as a data URI: zero request, paints with FCP.
  LCP should drop toward FCP and stop swinging run-to-run.
2026-06-06 19:03:38 -04:00
jwinterm 980bd963fc Splash logo: drop framer wrapper + CSS filter (cleaner LCP); nudge tracer
- Logo is a plain <img fetchPriority=high> in a plain div (no framer-motion,
  no drop-shadow filter) so it is a clean, statically-painted LCP candidate.
  Water filter + a CSS zoom apply only on enter.
- Tracer target nudged to computerX/Y +1.5 (between the low-right +2.5 and the
  high-left +0.5).
2026-06-06 18:36:58 -04:00
jwinterm c85383eff2 Mobile LCP: render splash logo as <img> (valid LCP element); faster blended transition; recenter tracer
- Inline <svg> is not an LCP candidate; on mobile the tagline is hidden so the
  logo was the only large element -> NO_LCP. Serve the mark as
  /images/branding/splash-such.svg and render it via <img> (a valid LCP
  candidate, painted from SSR HTML).
- Transition: start the cross-fade on click (shimmer plays as the splash fades
  into the content) and shorten durations 1.2s -> 0.8s; snappier and blended.
- Tracer target back to the midpoint (computerX+0.5, computerY+0.5).
2026-06-06 18:05:33 -04:00
jwinterm ea012b63fe Splash: remove non-composited glow rings (fix mobile NO_LCP); cross-fade to content; nudge tracer target
- Drop the two animated glow rings: stroke-dashoffset is non-composited, so
  they repainted every frame next to the logo and prevented Lighthouse from
  settling on an LCP (mobile NO_LCP). The static box + pulsing glow remain.
- Splash now cross-fades into the content: on enter it becomes a fixed overlay
  and fades opacity to 0 over the revealed page, instead of a hard cut.
- Nebula tracer target moved up-and-left (computerX-1, computerY-1.5) so the
  streamers converge on the monitor screen, not below-right of it.
- Removed the now-unused splash-* keyframes.
2026-06-06 17:50:42 -04:00
jwinterm fad3223af3 Restore tuned nebula; make splash logo static for stable mobile LCP
- Revert the nebula density/parallax change that disturbed the carefully
  tuned streamer convergence (it gave no perf benefit; the nebula is behind
  the splash and not in the load trace).
- Logo is now fully static (no float/breath) so it's a stable LCP candidate
  on mobile; the rings, glow pulse, and staged button fade-ins still carry
  the motion. (Desktop TBT win from the framer->CSS conversion is retained.)
2026-06-06 17:31:43 -04:00
jwinterm a5815e20ee Cut desktop TBT: splash idle animations framer-motion -> CSS; lighten nebula
- The splash's always-on animations (logo float/breath, the two traveling
  glow rings) move from framer-motion (JS rAF every frame on the main thread)
  to CSS keyframes (browser/compositor). framer now only drives the one-shot
  click-to-enter water exit.
- Nebula: density 8 -> 4 and dropped the scroll parallax (the Forced-reflow
  source); reduced-motion still gets the static background.
2026-06-06 17:16:13 -04:00
jwinterm c9b6803a77 Splash entrance (B): instant logo + staged CSS fade-in around it
The logo stays painted at first render (LCP-safe); the tagline, corner
buttons, and Explore hint now stagger-fade in via Tailwind animate-in
(CSS/compositor, no JS main-thread cost). motion-safe only.
2026-06-06 16:55:45 -04:00
jwinterm 567edaed0d Fix mobile LCP: paint the splash logo at first render
The splash logo (the LCP element) started at opacity:0 and only faded in
after framer-motion hydrated, pushing mobile LCP to ~7s. Render it visible
immediately; the float/breath animations still layer on top.
2026-06-06 15:33:02 -04:00
jwinterm a4be0baa3b Splash: replace ring "broadcast" with real water-refraction ripple
Drop the expanding circles. Clicking the logo now runs an SVG
feTurbulence + feDisplacementMap filter over the mark, ramped by a rAF
loop, so it ripples like a disturbed liquid surface and settles, with a
soft Cherenkov wash + backdrop blur, then enters. Reduced-motion still
enters instantly.
2026-06-06 00:02:10 -04:00
jwinterm d371762d15 Splash: drop tagline on mobile, fade content into a waterier wave
- Hide the cover tagline and subheader on mobile (logo + corner icons carry
  it); only the labeled pills/tagline show at sm+.
- On enter, fade the logo, tagline, buttons, and Explore cue out while the
  click-triggered wave plays.
- Waterier transition: backdrop-blur wash plus four expanding Cherenkov
  ripples; reduced-motion still enters instantly.
2026-06-05 23:55:48 -04:00
jwinterm 614a8c09e0 Splash: icon-only corner buttons on mobile, click-to-enter wave
- Mobile: corner nav buttons become icon-only (Portfolio/Apps/Services/About)
  with aria-labels; subheader hidden on mobile so nothing overlaps the logo.
- More ambient motion: logo float plus a second counter-rotating glow ring.
- Clicking the logo (or Explore) triggers a Cherenkov wave (wash + expanding
  rings) before entering; reduced-motion enters instantly.
- Beveled glass buttons.
2026-06-05 23:42:51 -04:00
jwinterm ed07bb5681 Add splash intro, nuclear games + consulting, Cherenkov hero
- Splash: full-viewport animated SUCH logo cover with four corner nav
  buttons; reveals the site once "entered" (scroll-through or Explore) and
  does not resurrect on scroll-up. Original hero restored beneath it
  (title-anchored monitor pulse + Cherenkov-blue background).
- Logo: traveling Cherenkov glow ring + gentle letter breath (SVG/Framer,
  reduced-motion safe); beveled glass corner buttons.
- Services: new "Nuclear & Radiation Modeling" section + "Consulting"
  umbrella (payments, crypto, nuclear), MCNP Google Scholar link, and a
  not-a-licensed-PE clarification.
- About/founder bio: LLNL credibility + accessibility, art101.io link.
- Products: add Barns & Neutrons, Alpha Doc, Decay Theory (detail pages,
  OG cards, SVG icons); client-side sortable grid with coming-soon at the
  bottom. New Neroswap + Custom Websites icons (de-duplicated).
- SEO: game pages added to sitemap.
- Copy: dial back em-dashes to colons/commas/parens on home + services.
2026-06-05 22:40:14 -04:00