{"schema_version":1,"slug":"website-revamp","title":"The website revamp, a technical record","project":{"title":"Vibe-Coding a Research Portfolio","url":"/projects/website-revamp/","source":"_projects/website-revamp.md"},"record_url":"/projects/website-revamp/record/","json_url":"/assets/json/design-records/website-revamp.json","repository":"https://github.com/DylanTao/dylantao.github.io","branch":"main","updated":"2026-10-15","summary":"How the redesign of this site works underneath, and how it changed from February to October 2026: the type and reading column, the time-of-day palettes and tokens, the motion rules, the structure of the homepage and projects, the separate Human and AI routes, the visual QA harness, and the written design memory that carries judgment from one session to the next.","scope":"The site is an al-folio v1 fork that owns its identity, layouts, styles, scripts and content. The stages below are design history, not an A/B test; where a move was measured, the entry gives the number and its conditions. Visitor comprehension has not been studied.","credit":"Compiled on October 15, 2026 by Anthropic's Claude from the code, the living heuristics, the experiment backlog, the roadmap, the October studio log and the Git history, for Sirui Tao to review and extend.","read_first":[{"path":"WEBSITE_DESIGN_HEURISTICS.md","why":"The canonical design memory. Decision order, the spine, type roles, motion, content and QA."},{"path":"docs/design-experiment-backlog.md","why":"Every experiment's hypothesis, evidence, my decision and its revisit trigger, including the removed ones."},{"path":"docs/material-lite-revamp.md","why":"The token grammar and the brand-mark rules."},{"path":"docs/site-experience-roadmap.md","why":"The September staging, the Lighthouse baseline and what is still open."},{"path":"docs/studio-log-2026-10.md","why":"The October moves in full, with dimensions, backtalk and concepts."},{"path":"docs/BOUNDARIES.md","why":"What this fork owns and what belongs to the al-folio gems."},{"path":"AGENTS.md","why":"The validated commands, the parallel-work contract and proportional visual QA."}],"constraints":["Decision order. Research meaning, credit, accessibility and a working route come before first-glance clarity, which comes before proof proximity, interaction state, and finally atmosphere and delight.","Keep the signature copy exact, including “Making AI tools that sharpen design judgment.” and “Design, Evaluate, Situate.”","Brand orange is identity only. Links, controls and focus use the active time-of-day accent, and the four palettes carry contrast.","Every effect is a complete still page under reduced motion, Pause motion, without a fine pointer, and under automation unless the URL carries `?cinematic=live`.","No scroll or wheel hijacking, cursor trails or ambient loops; parallax only between two layers that belong together.","Human and AI routes stay separate surfaces. AI routes are compact, source-linked and motion-free, and the switch between them is an ordinary link.","Research figures stay whole and uncropped, with declared dimensions.","When a review calls an element confusing, remove it rather than tune it.","New font sizes use a type role; the raw `font-size` ceiling in the style contract stays at 333.","Iterate on one route, state and viewport; checkpoint explicit routes at four viewports; run the full matrix only for a release.","Render-cost and weight changes must not change the pixels a visitor sees."],"tests":[{"path":"test/style_contract.js","covers":"Starter wiring, type roles and the raw font-size ceiling, plugin and visual-workflow contracts.","command":"npm.cmd run lint:style-contract"},{"path":"test/visual/sitewide.spec.js","covers":"Public routes at four viewports, one h1, the skip link first, alt text on every image, overflow and console errors.","command":"npm.cmd run test:visual:checkpoint"},{"path":"test/visual/iteration.config.js","covers":"The one-screenshot iteration lane on an already running preview.","command":"npm.cmd run test:visual:iterate"},{"path":"test/validate_publication_outputs.py","covers":"The AI profile, llms.txt and the structured catalogs.","command":"python test/validate_publication_outputs.py _site"},{"path":"test/test_site_experiments.py","covers":"Site-experiment case studies, their chronology, cards and reproduction guides.","command":"python -m unittest test.test_site_experiments"},{"path":"test/test_design_records.py","covers":"These technical records, their paths, sources, commits and links.","command":"python -m unittest test.test_design_records"}],"systems":[{"id":"type-reading","name":"Type, reading column and image weight","summary":"One family, five sizes and one measure. Prose sits in a column of about 68 characters, and images are served at the size they are shown.","techniques":[{"id":"type-roles","name":"Inter in five type roles","status":"shipped","since":"2026-09-14","what":"Every page uses Inter in five roles (page title, section title, subsection, body, metadata and controls), each a computed size token.","how":"`_sass/_themes.scss` defines `--type-display`, `--type-heading`, `--type-subheading`, `--type-reading` and `--type-compact` as clamped sizes and keeps older names as aliases. Inter's regular and italic variable files are self-hosted with the SIL OFL in `assets/fonts/inter/`, and only weights 400 to 700 are used. `test/style_contract.js` caps raw `font-size` declarations across `_sass` at 333 and requires the realignment layer to use type roles, so new styles reach for a role instead of a new size. Inter replaced the Newsreader display face on September 14.","files":[{"path":"_sass/_themes.scss","role":"Type role tokens and the four palettes."},{"path":"assets/fonts/inter/","role":"Self-hosted Inter, license and provenance."},{"path":"test/style_contract.js","role":"The font-size ceiling and type-role checks."}],"tests":["test/style_contract.js"],"sources":["inter-font"],"limits":["325 legacy raw sizes remain under the 333 ceiling; the cap stops growth rather than paying the debt down."],"next":[{"move":"Move the largest remaining raw sizes in the page-specific partials onto type roles.","readiness":"ready"}]},{"id":"reading-measure","name":"A 33.5rem reading column","status":"shipped","since":"2026-10-12","what":"Prose, headings and the post header share one centered column of about 68 characters, while figures and code reach the wider article width.","how":"`_sass/_reading.scss` sets `--reading-width: 33.5rem` for prose and `--reading-wide-width: 68ch` for figures and code, and the contents rail and trace pages follow it. The column is sized once in rem because `ch` is the width of \"0\" in each element's own font: the old `68ch` measured 91 to 95 characters of Inter and grew wider on headings. Bringhurst's 45 to 75 characters, 66 ideal, is the target.","files":[{"path":"_sass/_reading.scss","role":"Reading and wide widths for posts and case studies."}],"tests":["test/visual/sitewide.spec.js"],"sources":["css-values-4"],"limits":[],"next":[]},{"id":"display-size-images","name":"Pixels at display size","status":"shipped","since":"2026-10-12","what":"Pages request WebP variants sized to how large each image is actually shown, and long GIFs become posters that play on request.","how":"`_includes/responsive_variant.liquid` prints the URL of the 480, 800 or 1400-pixel WebP variant that the build's ImageMagick step generates, for gallery slides, thumbnails and teasers. The homepage record art points to 800 px variants with the originals as a fallback. `bin/make_gif_posters.py` writes a first-frame poster for each animated publication preview, and `_layouts/bib.liquid` shows the poster with a \"Play the animation\" control, which also serves reduced-motion visitors without a special case.","files":[{"path":"_includes/responsive_variant.liquid","role":"One WebP variant URL per raster."},{"path":"bin/make_gif_posters.py","role":"First-frame posters for animated previews."},{"path":"_layouts/bib.liquid","role":"Poster, zoom and play on request."},{"path":"assets/js/home.js","role":"Record art from 800 px variants with fallbacks."}],"tests":["test/test_publication_previews.py"],"sources":["wcag-pause-stop-hide"],"limits":["Local builds without ImageMagick 404 on variants that only CI generates; pages fall back to the originals."],"next":[]}]},{"id":"color-themes","name":"Color, time of day and tokens","summary":"Four time-of-day palettes change the atmosphere, never the research story. A small Material-inspired token set keeps surfaces, focus and motion consistent, and a procedural orange is the only identity color.","techniques":[{"id":"time-themes","name":"Four time-of-day palettes","status":"shipped","since":"2026-05-22","what":"Morning dawn rose, noon Pacific blue, afternoon sea glass and evening moonlit lavender default from the visitor's local time.","how":"`assets/js/theme.js` picks the mode from local time (boundaries at 5, 11, 15 and 20 o'clock) before first paint, remembers a manual choice for the session, and switches automatically at a boundary only if the visitor has not chosen. `_sass/_themes.scss` holds the four token sets; each one registers through the whole surface hierarchy, from the page field to cards, outlines, shadows and the footer. Accent ink for links and labels stays separate from lighter pastel fills, so contrast holds in every mode.","files":[{"path":"assets/js/theme.js","role":"Mode selection before first paint, and Pause motion."},{"path":"_sass/_themes.scss","role":"The four palettes."}],"tests":["test/sun-arc-math.test.mjs","test/test_theme_palette.py"],"sources":["wcag-contrast"],"limits":[],"next":[]},{"id":"material-lite","name":"Material-Lite tokens","status":"shipped","since":"2026-06-29","what":"A small set of radius, elevation, tonal-surface, state-layer, focus, spacing and motion tokens, borrowed from Material 3 ideas without its component system.","how":"`_sass/_material-lite.scss` defines `--md-lite-*` tokens, and the September foundations pass added spacing and motion durations and easings to them. Filled orange marks the primary action in a local cluster; tonal surfaces carry secondary actions, chips and quiet controls; neutral surfaces carry most of the page. PurgeCSS keeps every `--md-lite-` variable through its safelist.","files":[{"path":"_sass/_material-lite.scss","role":"The shared tokens."},{"path":"docs/material-lite-revamp.md","role":"Design intent and token use."},{"path":"purgecss.config.js","role":"Keeps the token variables in the deployed CSS."}],"sources":["material3"],"limits":["The homepage's own partial predates the tokens and still uses many literal values."],"next":[]},{"id":"brand-orange","name":"A procedural orange mark","status":"shipped","since":"2026-06-29","what":"The navbar mark is a flat 2D orange whose shape, rind gradient, pores, navel and leaves vary on each page load.","how":"`_includes/brand-orange.liquid` renders an inline SVG with a no-JavaScript fallback, and `assets/js/brand-orange.js` varies the shape within a fixed viewbox, the rind gradient, the pores, the calyx details and the leaves, with a small pointer response that stops under reduced motion. `?orangeSeed=` reproduces one mark for inspection. The home link owns the accessible label; the SVG is decorative.","files":[{"path":"_includes/brand-orange.liquid","role":"Inline SVG and fallback."},{"path":"assets/js/brand-orange.js","role":"Seeded variation and pointer response."},{"path":"_sass/_brand-orange.scss","role":"Mark styles."}],"sources":[],"limits":["Engraving ST into the fruit at navbar size made it read like a glossy app icon, so the mark carries no initials."],"next":[]},{"id":"sun-arc","name":"A sun you can drag","status":"shipped","since":"2026-10-13","what":"The theme panel is a day arc. Dragging the sun picks any minute, the theme snaps to that hour's mode, and the page's sky glow and the 3D scenes follow.","how":"`assets/js/sun-arc.mjs` maps minutes onto an arc from 5:00 to 20:00, with a flatter night arc for the moon (`sun-arc-math.mjs`). The four token sets still carry contrast; only the panel and a sky glow at the top of the page shift color continuously, the glow moving left to right with the sun. The footer coast blends its four authored looks by the minute, and the 3D home previews the hour when the drag ends, through a `siteTimeChange` event. Now hands everything back to the clock. A theme chosen anywhere else moves the sun but never sets a preview hour. This is direct manipulation in Shneiderman's sense: a continuous representation, physical action and immediate, reversible feedback.","files":[{"path":"assets/js/sun-arc.mjs","role":"The control and the siteTimeChange event."},{"path":"assets/js/sun-arc-math.mjs","role":"Minute to arc geometry and theme boundaries."},{"path":"_includes/theme_mode_control.liquid","role":"Panel markup and the four stops."},{"path":"_sass/_sun-arc.scss","role":"Panel and sky glow styles."}],"tests":["test/sun-arc-math.test.mjs"],"sources":["shneiderman1983direct"],"limits":["The four stops stay, because they are both the quick choice and the test contract."],"next":[]},{"id":"chroma-thumbnails","name":"Grayscale-until-hover thumbnails","status":"retired","since":"2026-09-14","what":"Fun-project thumbnails sat in grayscale until hover, after React Bits' Chroma Grid.","how":"A filter desaturated each Fun thumbnail at rest and restored color on hover or focus, as one of the approved reading enhancements.","files":[],"sources":["react-bits"],"limits":[],"next":[],"retired":{"date":"2026-09-28","commit":"1e0e67dbf","reason":"Mixed grayscale and color thumbnails made the project index inconsistent. All thumbnails now keep their original color in every state."}}]},{"id":"motion","name":"Motion","summary":"Motion explains a state, a comparison or a path, then settles into a readable still frame. A bounded cinematic layer, named springs and scroll-driven CSS carry it; one switch stills everything.","techniques":[{"id":"cinematic-layer","name":"A bounded cinematic layer","status":"shipped","since":"2026-09-05","what":"Pages that declare `cinematic` get scroll scenes, staggered entrances, a pointer spotlight and tilt on card grids, and a card-to-hero image morph.","how":"`assets/js/cinematic.js` runs on GSAP 3.15 and ScrollTrigger, vendored under `assets/vendor/gsap/3.15.0/` and loaded only on opted-in pages. Nothing takes over scrolling. Every effect is a still page under reduced motion, without a fine pointer and under automation (`navigator.webdriver`), unless the URL carries `?cinematic=live`, so visual captures stay deterministic. The clicked project image travels into the case page's hero through a cross-document view transition and back again.","files":[{"path":"assets/js/cinematic.js","role":"Scroll scenes, entrances, spotlight, tilt and the morph."},{"path":"_sass/_cinematic.scss","role":"Cinematic styles."},{"path":"assets/vendor/gsap/3.15.0/NOTICE.md","role":"Vendored library and license."}],"sources":["gsap","heer2007transitions","segel2010narrative"],"limits":["Cinematic captures need `?cinematic=live`; ordinary automated captures see the still page."],"next":[]},{"id":"research-sketch","name":"The Research Focus sketch","status":"shipped","since":"2026-05-22","what":"A semantic drawing explains Design, Evaluate and Situate, with a quiet Paper shader field moving underneath it.","how":"`assets/js/research-motion.js` draws each mode with a stable number of lines and dots and enough copy to read the metaphor before the motion. A pointer bends the field inside the canvas, easing in and decaying on exit. The Dot Orbit field comes from a vendored minimal closure of `@paper-design/shaders` 0.0.80 under Apache-2.0, with one WebGL2 context, at most 480,000 processed pixels and a pause offscreen and in hidden tabs. If WebGL fails, the 2D drawing keeps everything a visitor needs. On wide screens the sketch pins beside its three notes inside its own well, and the note in view picks the lens.","files":[{"path":"assets/js/research-motion.js","role":"The semantic sketch and its modes."},{"path":"_includes/home/research_motion.liquid","role":"Markup, notes and credits."},{"path":"assets/vendor/paper-shaders/","role":"The vendored shader closure with hashes."}],"sources":["paper-shaders","counterpoint2024","chartability"],"limits":["My first live review found the field perceptually absent; it was strengthened, and its current strength still awaits my judgment."],"next":[]},{"id":"springs","name":"Springs named by their damping","status":"shipped","since":"2026-10-12","what":"Buttons and menus answer a press with a damped spring instead of a fixed ease, from two named springs.","how":"`_sass/_springs.scss` defines a confident spring (ζ 0.8, settling in 0.48 s with 1.5 % overshoot) for buttons and menus and a playful one (ζ 0.55, 0.9 s, 12.6 %) for the desk toys. `bin/spring_easing.cjs` samples a damped oscillator's step response for any damping ratio and period into a CSS `linear()` curve, describing springs by bounce and duration as Apple's spring guidance does. The theme menu enters with `@starting-style`, and reading disclosures open through `::details-content`. Browsers without `linear()` keep a similar cubic Bézier.","files":[{"path":"_sass/_springs.scss","role":"The two named springs."},{"path":"bin/spring_easing.cjs","role":"Turns ζ and T into a `linear()` easing."}],"sources":["apple-springs"],"limits":["Springs stay on direct manipulation and discrete toggles; ambient motion keeps eases or stops."],"next":[]},{"id":"view-transitions","name":"Cross-document view transitions","status":"shipped","since":"2026-09-05","what":"Same-origin navigations crossfade the page root while the navbar and progress line hold still.","how":"`_includes/head.liquid` emits `@view-transition { navigation: auto }` on every page except those that manage their own arrival, such as the AI profile and the homepage. `_sass/_transitions.scss` times the root crossfade with the standard motion tokens and turns it into an instant swap under reduced motion, as do browsers without support. The visual harness is unaffected because it navigates with `page.goto` and disables animations.","files":[{"path":"_includes/head.liquid","role":"Emits the view-transition rule per document."},{"path":"_sass/_transitions.scss","role":"Root crossfade timing."}],"sources":["css-view-transitions-2"],"limits":["The root crossfade is still marked a prototype awaiting my live click-through in all four themes."],"next":[{"move":"A live click-through in all four themes, then keep or remove the root crossfade.","readiness":"needs-decision"}]},{"id":"revamp-laptop","name":"A scroll-driven laptop","status":"shipped","since":"2026-10-12","what":"On the revamp page a CSS laptop opens on the February homepage, then its screen wipes along an orange seam to an October capture of the same view.","how":"`_sass/_revamp-device.scss` maps scroll progress through the section to the lid angle and the wipe with `animation-timeline: view()`, which runs on the compositor with no scroll listener. It plays only when the cinematic layer is live and the browser supports scroll timelines; everywhere else the laptop stands open on today's page with a February button.","files":[{"path":"_sass/_revamp-device.scss","role":"The scroll-driven laptop."},{"path":"_projects/website-revamp.md","role":"Markup and the two captures."}],"sources":["css-scroll-animations"],"limits":["Worth repeating only where a single comparison carries the story."],"next":[]},{"id":"pause-motion","name":"One switch that stills everything","status":"shipped","since":"2026-10-13","what":"Pause motion in the theme panel makes the reduced-motion query match, so every surface that already honors reduced motion stills through the same path.","how":"`assets/js/theme.js` installs in the head, before any module asks, and lays the pause over `matchMedia(\"(prefers-reduced-motion: reduce)\")` while keeping each caller's own list and change events. The 3D home, duh, P, the footer coast, the research sketch and the cinematic scenes all still without a switch of their own, and CSS loops key off `html[data-motion]`.","files":[{"path":"assets/js/theme.js","role":"The pause laid over the reduced-motion query."}],"sources":["mq5-reduced-motion"],"limits":["A first version shared one native list among every caller, and P's playground then sometimes never heard reduced motion end."],"next":[]},{"id":"liquid-glass","name":"A glass lip under the navigation bar","status":"shipped","since":"2026-10-13","what":"A 10-pixel lip under the navigation bar refracts what scrolls beneath it, like the rounded edge of a glass slab.","how":"`assets/js/liquid-glass.js` builds a displacement map whose green channel encodes a vertical offset, so the lip's top rows show the page 4 px lower and a passing line stretches as it slides under. The lip belongs to the header and anchors to the bar's bottom edge, because the bar's `view-transition-name` makes it a backdrop root. Chromium draws SVG filters inside `backdrop-filter`; Firefox, Safari, reduced transparency, more contrast and forced colors keep the frosted bar.","files":[{"path":"assets/js/liquid-glass.js","role":"Displacement map and engine checks."},{"path":"_sass/_navbar.scss","role":"The bar, its frost and the lip."}],"sources":["filter-effects-2","filter-effects-1"],"limits":["Chromium only. A 14 px, 22-scale lip smeared text into a glitch, and a glass theme menu let other words show through, so the menu stays solid."],"next":[{"move":"Try glass where it floats over imagery or the 3D home rather than over paper.","readiness":"needs-decision"}]},{"id":"spider-verse-print","name":"A comic panel that prints in","status":"shipped","since":"2026-10-13","what":"The HCI Spooder-Man gallery brings each slide in as offset color plates that come into register on twos, with Ben-Day dots receding.","how":"`assets/js/spooder-project.js` splits the new slide into red, green and blue plates with an SVG filter and brings them into register over three held drawings at 12 per second (9, 5, then 2 px), while magenta and cyan dots recede from 0.85 to 0.25 (`_sass/_spooder-verse.scss`). A hard cut replaced the cross-fade, so two captions never show at once. Only this page loads it, and reduced motion gets the instant swap with no filter.","files":[{"path":"assets/js/spooder-project.js","role":"Plate offsets and timing."},{"path":"_sass/_spooder-verse.scss","role":"Print styles and dots."}],"tests":["test/test_purge_safe_selectors.py"],"sources":[],"limits":["Kept to slide changes; a print-in on scroll or idle would turn a signature into noise."],"next":[]},{"id":"side-threads","name":"Side threads, title drift and gradient fields","status":"retired","since":"2026-09-05","what":"Scroll-progress threads beside the homepage and publications, gradient fields behind page titles, titles drifting against the page, and a papers-first publications order.","how":"The first cinematic slices added a thread that filled with scroll and lit the section being read, a slow gradient field behind page titles, headings that drifted slower than the page, and a papers-first reorder of the publications page.","files":[],"sources":[],"limits":[],"next":[],"retired":{"date":"2026-09-05","commit":"74d6c7134","reason":"In my review they read as confusion rather than value. They moved without meaning anything, and the Wall of Rejection went back to the top of publications."}}]},{"id":"structure","name":"Structure and wayfinding","summary":"The homepage is an editorial thesis and routing hub, the projects index an evidence surface, and every site experiment tells its own story with a reproduction guide and, now, a technical record.","techniques":[{"id":"research-sequence","name":"The homepage research sequence","status":"shipped","since":"2026-10-05","what":"The homepage moves from the opening through the thesis, the full Research Focus, the selected publications and compact updates, then students and contact.","how":"`_pages/about.md` and the includes in `_includes/home/` render the sequence once: the thesis explained once, the complete focus section rendered once under `#focus`, the papers, then three dated updates. The thesis is fully legible text; an earlier scroll-controlled word opacity was removed. On October 12 a repeated fact strip left the hero.","files":[{"path":"_pages/about.md","role":"Homepage content, thesis and selected paper order."},{"path":"_layouts/home.liquid","role":"Homepage layout."},{"path":"_includes/home/hero.liquid","role":"The opening and the 2D | 3D desk."}],"tests":["test/test_selected_paper_order.py","test/test_design_realignment.py"],"sources":[],"limits":["On 1280 × 800 laptops the portrait column sets the hero's height, so the thesis still sits below the fold."],"next":[]},{"id":"selected-order","name":"An explicit, tested paper order","status":"shipped","since":"2026-10-12","what":"The homepage lists selected papers in a declared order, with DesignWeaver first, instead of bibliography file order.","how":"`selected_paper_order` in `_pages/about.md` is rendered with jekyll-scholar's `nocite` tag and `--cited_in_order` in `_includes/home/news_publications.liquid`, and `test/test_selected_paper_order.py` keeps it equal to the bibliography's selected entries. Order is a claim, because items at the start of a list are remembered best.","files":[{"path":"_pages/about.md","role":"The declared order."},{"path":"_includes/home/news_publications.liquid","role":"Renders papers in that order."}],"tests":["test/test_selected_paper_order.py"],"sources":["murdock1962serial"],"limits":[],"next":[]},{"id":"project-cards","name":"Project cards that open in place","status":"shipped","since":"2026-05-27","what":"A project card opens where it is with a cancelable FLIP transition and returns to the same place; on phones the site experiments become one swipeable row.","how":"`assets/js/project-cards.js` measures the card before and after opening and animates one cancelable clock of 430 ms, read from the shared motion token. A new click cancels stale motion, and reduced motion jumps to the final layout. Below 768 px the experiment grid becomes one scroll-snapped row of cards 84 % wide, so the next one peeks, with a count of the card most in view and arrow-key support; an expanded card widens to the full row.","files":[{"path":"assets/js/project-cards.js","role":"FLIP opening and the phone row count."},{"path":"_includes/projects.liquid","role":"Card markup, origin lines and outcomes."},{"path":"_data/project_cards.yml","role":"Card teasers, origin and evolution lines."},{"path":"_sass/_design-details.scss","role":"The phone row."}],"tests":["test/test_site_experiments.py"],"sources":["css-scroll-snap"],"limits":[],"next":[]},{"id":"command-palette","name":"A curated command palette","status":"shipped","since":"2026-10-12","what":"The ⌘K palette offers curated starting routes and Do actions that call the site's own controls.","how":"`_includes/search_data.liquid.js` is rendered inline on every page, with Start here routes, writing, projects, contact, and Do actions for the four themes, the 3D home, the AI-readable page, duh and Build Rhythm. Each action clicks or calls the existing control, so it can never disagree with it. The curated data had silently fallen back to the plugin's defaults after al-folio v1 stopped building `_scripts/`.","files":[{"path":"_includes/search_data.liquid.js","role":"Curated palette data and actions."}],"tests":["test/test_command_palette.py"],"sources":[],"limits":["It renders on the AI profile too, so its text is held to the publication validator's rules."],"next":[]},{"id":"case-studies","name":"Experiments that tell their own stories","status":"shipped","since":"2026-07-15","what":"Each site experiment has a case study with its question, change, evidence and limit, a reproduction guide, and a place in the projects index by debut date.","how":"The projects index groups site experiments once and sorts them by `debut_date`. Each case page leads with what it is and why, then before/after evidence and a technical disclosure, and offers a downloadable reproduction guide from `assets/downloads/site-experiments/`. `test/test_site_experiments.py` checks their chronology, cards, voice and guides.","files":[{"path":"_pages/projects.md","role":"The projects index and its groups."},{"path":"assets/downloads/site-experiments/","role":"Reproduction guides."},{"path":"test/test_site_experiments.py","role":"Chronology, card and story checks."}],"tests":["test/test_site_experiments.py","test/test_fun_project_voice_core_rewrite.py"],"sources":[],"limits":[],"next":[]},{"id":"origin-glint","name":"An origin glint on widgets","status":"retired","since":"2026-07-15","what":"A tiny mark beside seven widgets that was meant to stay quiet at rest and lead curious readers to each widget's origin story.","how":"The mark went through four versions: a dot and line, a hover disclosure, a `story ↗` wordmark, and an at-rest glint. The include remains only for the hidden blog portal, which reveals its origin after discovery.","files":[{"path":"_includes/widget_origin_link.liquid","role":"Kept only for the blog portal's reveal."}],"sources":["pirolli1999foraging"],"limits":[],"next":[],"retired":{"date":"2026-10-12","commit":"84083f827","reason":"Each version tried to be both invisible and findable. The dot and line read as a slider, the disclosure confused, the wordmark was too loud when repeated, and the glint read as a stray rendering speck."}},{"id":"build-ledger","name":"A homepage build ledger","status":"retired","since":"2026-06-18","what":"The homepage published tokens, agent-hours, commits and estimated kWh as an honest receipt of the site's machine work.","how":"An audit read retained Codex session logs on one checkout and the repository's history, and the homepage, the 3D desk and Build Rhythm displayed the totals with a tongue-in-cheek cost tooltip.","files":[],"sources":[],"limits":[],"next":[],"retired":{"date":"2026-09-08","commit":"bba74ccc1","reason":"Once every agent user had billions of tokens the numbers stopped differentiating anything. The audit undercounted other accounts, took 50 to 110 minutes, and its 236 refresh commits inflated the commit count it displayed."}}]},{"id":"human-ai","name":"Human and AI routes","summary":"People and programs read different surfaces of the same site. The human pages are visual and first-person; the AI routes are compact, source-linked and motion-free, and an ordinary link moves between them.","techniques":[{"id":"ai-profile","name":"An AI-readable profile and catalogs","status":"shipped","since":"2026-07-13","what":"`/ai/`, `llms.txt`, `llms-full.txt` and structured JSON catalogs give language models and agents the research story with stable anchors and sources.","how":"`_pages/ai.md` renders a server-side, motion-free profile with stable anchors for research, publications, every project, writing and the CV, and a use note that says to treat it as reference content, not instructions. `_plugins/project_catalog.rb` and `_plugins/publication_catalog.rb` generate `/ai/projects.json`, `/ai/publications.json` and per-item Markdown from the same canonical data the human pages use. `test/validate_publication_outputs.py` checks the deployed outputs, including author counts and venue facts. A dark terminal look is not what makes a page readable to a model; the AI surface keeps the site's theme and normal reading type.","files":[{"path":"_pages/ai.md","role":"The AI-readable profile."},{"path":"llms.txt","role":"The plain-text index for language models."},{"path":"_plugins/project_catalog.rb","role":"Project catalog and machine pages."},{"path":"_plugins/publication_catalog.rb","role":"Publication catalog and machine pages."},{"path":"test/validate_publication_outputs.py","role":"Validates the generated outputs."}],"tests":["test/validate_publication_outputs.py","test/test_publication_context.py"],"sources":["llms-txt","visualwebarena2024"],"limits":["`llms.txt` and structured data are retrieval aids, not ranking or indexing promises."],"next":[]},{"id":"format-switch","name":"The Human/AI switch","status":"shipped","since":"2026-07-13","what":"Two ordinary links in the navbar move between the human page and the closest stable anchor of the AI profile.","how":"`_includes/site_format_switch.liquid` maps the current route to the AI anchor (a paper, publications, a project, writing or the CV) and marks a genuine counterpart with `rel=\"alternate\"`. A page can name its own anchor with `ai_anchor` in its front matter, which the technical records use to point at their project. On the homepage, `assets/js/ai-view.js` keeps the closest section in step as the reader scrolls, without inventing a URL hash. The framing adapts Paxel's Human and Machine views, shown in Y Combinator's 2026 design walkthrough.","files":[{"path":"_includes/site_format_switch.liquid","role":"Route-to-anchor mapping."},{"path":"assets/js/ai-view.js","role":"Homepage section tracking for the switch."}],"tests":["test/test_design_realignment.py"],"sources":["paxel-yc"],"limits":[],"next":[]}]},{"id":"objects-place","name":"Objects, places and demos","summary":"Playful objects stay only when they point to real work or a real place: the coffee stain on the desk, the La Jolla coast along the footer, a paper texture on this story, and a scaffold a visitor can try.","techniques":[{"id":"coffee-stain","name":"A coffee stain painted like watercolour","status":"shipped","since":"2026-06-18","what":"The homepage's coffee stain is a raster painted fresh on every visit from a per-pixel coffee density, not a drawn outline.","how":"`assets/js/home-coffee-stain.js` paints the wet ring a mug's foot leaves (a dark pinned edge fading inward, tide lines, dry breaks, heavier where it pooled), sometimes a second placement, a puddle and spills that dry as washes with dark edges and backrun veins, under the paper's mottle. Density maps from amber to espresso and multiplies into the page; evening paints pale residue. It renders in time-boxed idle slices and is read back as a data URL, because the page's Content-Security-Policy admits `data:` images but not `blob:` ones. `?coffeeSeed=` reproduces one stain. The ring follows Deegan and colleagues' account of capillary flow, and the washes Curtis and colleagues' computer-generated watercolor.","files":[{"path":"assets/js/home-coffee-stain.js","role":"The seeded density painter."}],"sources":["deegan1997coffee","curtis1997watercolor"],"limits":["The stain is a desk cue; it must stay quiet enough that any text it crosses reads easily."],"next":[]},{"id":"la-jolla-footer","name":"La Jolla along the footer","status":"shipped","since":"2026-09-14","what":"A small Blender-authored coast with campus, home and village groups fills the footer and reveals its buildings left to right as it comes into view.","how":"`_includes/la-jolla-footer.liquid` loads the coast only near visibility, at most 30 fps with capped pixel density, and pauses offscreen and under reduced motion, with a composed fallback image. The buildings form an authored place collage above a sourced map atlas, and the light on the Design and Innovation Building's third floor is a personal detail, not an occupancy feed. Small screens can pan the coast instead of shrinking every landmark.","files":[{"path":"_includes/la-jolla-footer.liquid","role":"Footer scene and fallback."},{"path":"docs/la-jolla-footer.md","role":"Geometry, provenance and evidence."}],"tests":["test/visual/footer-coast.spec.js"],"sources":[],"limits":["The footer still uses the older full-image ambient-occlusion pass; replacing it changes pixels and needs a matched comparison."],"next":[{"move":"Move the footer to the horizon-based contact light the 3D home uses, judged with a matched A/B.","readiness":"needs-decision"}]},{"id":"paper-texture","name":"A static paper texture for this story","status":"shipped","since":"2026-08-16","what":"The revamp story sits on a seeded paper texture rendered once, with no runtime shader.","how":"`bin/generate_paper_texture.cjs` renders the texture from Paper Shaders' Paper Texture in `@paper-design/shaders` 0.0.80, and the recipe beside the asset records the seed, parameters, output hash, generator, license and vendor hashes. It is a story-specific accent, not a sitewide background.","files":[{"path":"bin/generate_paper_texture.cjs","role":"Deterministic texture generation."},{"path":"assets/img/website-revamp/paper-texture.recipe.json","role":"Seed, parameters and hashes."}],"tests":["test/test_design_realignment.py"],"sources":["paper-shaders"],"limits":[],"next":[]},{"id":"designweaver-scaffold","name":"A dimensional scaffold to try","status":"shipped","since":"2026-10-13","what":"The DesignWeaver page lets a visitor pick tags in four dimensions and watch a prompt rebuild as a fixed sentence, each phrase underlined in its dimension's color.","how":"`assets/js/designweaver-scaffold.mjs` reads the four dimensions and the tags from a study session in the CHI 2025 talk (`_data/designweaver_scaffold.yml`). It opens on that session's selection and the prompt DesignWeaver wrote, and any change rebuilds the prompt as a fixed sentence. The label changes from DesignWeaver's prompt to \"Your prompt, as a fixed sentence\", and the note says that the real system uses a language model and that nothing is sent or generated. Without JavaScript the session's selection still shows. Scaffolding here is Wood, Bruner and Ross's sense: support that lets a novice do what they could not alone.","files":[{"path":"assets/js/designweaver-scaffold.mjs","role":"The palette and the fixed-sentence prompt."},{"path":"_data/designweaver_scaffold.yml","role":"Dimensions and tags from the study session."},{"path":"_sass/_designweaver-scaffold.scss","role":"Palette styles."}],"tests":["test/test_designweaver_scaffold.py"],"sources":["wood1976scaffolding"],"limits":["A fixed sentence is terser than the model's prose, so the counter reports tags across dimensions rather than words."],"next":[]}]},{"id":"qa","name":"Visual QA and checks","summary":"Evidence matches the decision being made. One screenshot answers a taste question, a four-viewport checkpoint proves a direction, and the full matrix is for a release. Contracts and validators catch what screenshots miss.","techniques":[{"id":"qa-lanes","name":"Three visual QA lanes","status":"shipped","since":"2026-08-16","what":"A fast lane takes one screenshot on a running preview, a checkpoint lane runs explicit routes at four viewports, and the release lane runs everything.","how":"`test/visual/iteration.config.js` defaults to one route, one 1440 × 1000 light viewport, one worker and no trace, refuses a second route, and fails closed after a one-minute budget. `test/visual/checkpoint.config.js` refuses to run without an explicit `VISUAL_ROUTE_IDS` list and covers 1440 × 1000, 1280 × 800, 768 × 1024 and 390 × 1000 in light and dark. Captures go under `.jekyll-cache/visual-qa/` so Jekyll does not rebuild in response to its own QA output. Scene cases run in named groups with one worker (`bin/run_visual_scene_group.cjs`).","files":[{"path":"test/visual/iteration.config.js","role":"The one-screenshot lane."},{"path":"test/visual/checkpoint.config.js","role":"The explicit-route checkpoint lane."},{"path":"test/visual/public.config.js","role":"The public release suites."},{"path":"bin/run_visual_scene_group.cjs","role":"One-worker scene groups."}],"sources":["playwright"],"limits":["Linux Chromium can reject later captures after image-heavy and WebGL work share a process, so the public site and scene suites run as separate processes."],"next":[]},{"id":"capture-retry","name":"Retry after the page settles","status":"shipped","since":"2026-10-12","what":"A failed screenshot retries only after two animation frames and an idle callback, at 250, 750 and 1,500 ms.","how":"`test/visual/helpers.js` waits for the page's current work to finish before a capture, approximating a settled event loop with `requestAnimationFrame` followed by `requestIdleCallback`, then retries transient capture errors with backoff; any other error fails at once.","files":[{"path":"test/visual/helpers.js","role":"Settle-then-retry capture."}],"sources":["html-event-loop"],"limits":["The retry hides nothing structural, but a page that stalls a two-core runner also stalls a slow phone."],"next":[]},{"id":"route-invariants","name":"Invariants on every public route","status":"shipped","since":"2026-10-12","what":"Every public route keeps one h1, the skip link as the first tab stop, and alt text on every image.","how":"A one-off audit first checked that all 60 route-and-width pairs already met the rules; then three assertions in `exercisePublicRoute` in `test/visual/sitewide.spec.js` started keeping them. The skip link and main landmark came with the September foundations pass.","files":[{"path":"test/visual/sitewide.spec.js","role":"Public route assertions."},{"path":"_layouts/default.liquid","role":"The skip link and main landmark."}],"sources":["wcag-bypass-blocks","wcag-non-text"],"limits":["One h1 per page is a convention for heading navigation, not a WCAG requirement."],"next":[{"move":"Behavioral accessibility next, such as focus return after dialogs and disclosures.","readiness":"ready"}]},{"id":"contracts","name":"Contracts, validators and the override audit","status":"shipped","since":"2026-05-25","what":"A style contract, a publication-output validator, Python policy tests and the al-folio override audit guard what screenshots cannot see.","how":"`test/style_contract.js` checks starter wiring, plugin and visual-workflow contracts and the type ceiling. `test/validate_publication_outputs.py` checks the built AI outputs. The Python suite in `test/` checks content contracts such as the site experiments, selected paper order and math gating. Local overrides of gem-owned files are recorded with hashes in `.al-folio-overrides.yml` through `bundle exec al-folio upgrade overrides audit`, so upstream drift is visible after an update.","files":[{"path":"test/style_contract.js","role":"Starter and style contracts."},{"path":"test/validate_publication_outputs.py","role":"Built AI output checks."},{"path":".al-folio-overrides.yml","role":"Recorded local overrides of gem-owned files."}],"sources":["al-folio"],"limits":[],"next":[]}]},{"id":"memory","name":"Design memory","summary":"Judgment is carried across sessions and models in writing: living heuristics, an experiment backlog with my decisions, a studio log of moves, agent skills that point to those documents, and these technical records.","techniques":[{"id":"heuristics","name":"Living design heuristics","status":"shipped","since":"2026-05-22","what":"One human-readable file holds the decision order, the spine, type, color, motion, content and QA rules, and the lessons from my reviews.","how":"`WEBSITE_DESIGN_HEURISTICS.md` is the canonical source, published on the site as a teaching artifact. The August pass named the spine \"Less, but more Sirui\" and added the signature copy locklist; on September 5 the file went from 443 to 328 lines with every canonical heading kept. Agent skills read it by heading instead of copying it, and `test/test_agent_guidance.py` keeps the canonical headings in place.","files":[{"path":"WEBSITE_DESIGN_HEURISTICS.md","role":"The living heuristics."},{"path":"test/test_agent_guidance.py","role":"Canonical headings and skill links."}],"sources":["schon1983reflective"],"limits":[],"next":[]},{"id":"experiment-backlog","name":"An experiment backlog with decisions","status":"shipped","since":"2026-08-16","what":"Each visual or interaction idea gets a hypothesis, route, reference, licensing, budget, evidence, my decision and a revisit trigger; removed experiments stay on record.","how":"`docs/design-experiment-backlog.md` is the durable queue. An entry moves to Completed only with evidence and my decision, and removed ideas such as the origin glint and the build ledger keep their reasons, because knowing why an idea failed is part of the site's professional vision.","files":[{"path":"docs/design-experiment-backlog.md","role":"The experiment queue."}],"sources":[],"limits":[],"next":[]},{"id":"studio-log","name":"A studio log of moves","status":"shipped","since":"2026-10-12","what":"Each design move is a card with its frame, dimensions, move, backtalk, reframe and a concept with its primary source.","how":"`docs/studio-log-2026-10.md` keeps the loop from Schön's _The Reflective Practitioner_: make a move, let the material talk back, reframe. The dimensions name the variables being moved, which turns a vague request into something to reason about. The log is unpublished; the design histories in these records condense it.","files":[{"path":"docs/studio-log-2026-10.md","role":"The October studio cards."}],"sources":["schon1983reflective"],"limits":[],"next":[]},{"id":"agent-skills","name":"Agent skills that route to the living documents","status":"shipped","since":"2026-06-29","what":"Short repository skills tell coding agents which document and heading to read for critique, writing, the desk scene and upstream sync.","how":"`.codex/skills/` holds concise overlays such as `website-design-critique` and `portfolio-writing-voice`. They point to the heuristics, the backlog and the scene brief by heading and do not duplicate the long rules.","files":[{"path":".codex/skills/","role":"The repository skills."},{"path":"AGENTS.md","role":"Routing rules and the validated commands."}],"tests":["test/test_agent_guidance.py"],"sources":[],"limits":[],"next":[]},{"id":"design-records","name":"Technical records with a next-move brief","status":"shipped","since":"2026-10-15","what":"Each long-running project keeps its systems, techniques, design history, open problems and sources as data, rendered as a page and served as JSON.","how":"`_data/design_records/\u003cslug\u003e.yml` holds a record. `_includes/design_record.liquid` renders it at `/projects/\u003cproject\u003e/record/`, a Liquid page publishes it as JSON under `/assets/json/design-records/`, and `bin/design_record_brief.py \u003cslug\u003e` turns it into a brief for the next pass: the current state per system, known limits, candidate moves ranked by stated readiness, sources to read, and the constraints and tests to keep. `test/test_design_records.py` checks the schema, that every path is in the repository, that every source and system resolves, and that every commit exists in history.","files":[{"path":"_data/design_records/","role":"The records."},{"path":"_includes/design_record.liquid","role":"Renders a record as a page."},{"path":"assets/json/design-records/","role":"JSON endpoints."},{"path":"bin/design_record_brief.py","role":"The next-move brief."},{"path":"test/test_design_records.py","role":"Schema and integrity checks."}],"tests":["test/test_design_records.py"],"sources":[],"limits":["The records condense the studio log and the notes by hand; a move that is never written down is missing from the brief too."],"next":[]}]}],"history":[{"date":"2026-02-09","title":"The archive made the problem visible","system":"structure","frame":"My site listed projects and papers, but it did not tell visitors what I was actually studying.","dimensions":["what a visitor learns first","number of competing elements"],"move":"Kept the public homepage as Wayback served it on February 9, so the redesign could critique a concrete page instead of a memory. The capture entered the repository on May 23 (`e4f021520`).","backtalk":"The archive is competent and crowded. Papers, projects, biography and navigation all ask for attention at once.","reframe":"Make the research question the front door and the interface quieter.","sources":["wayback-2026-02-09"]},{"date":"2026-05-22","title":"The research question becomes the front door","system":"structure","frame":"A visitor should understand what I study before decoding my biography or the interface.","dimensions":["order of homepage sections","number of actions","where proof sits"],"move":"Rebuilt the homepage around the research claim, affiliation, proof routes and a few actions, with time-of-day themes (`f30191146`) and a research motion sketch for Design, Evaluate, Situate.","backtalk":"Paper, desk objects and motion stayed only where they helped a visitor understand how I design, evaluate and situate AI tools.","reframe":"Let the publications and experiments carry their own stories.","commit":"82ecde6ff"},{"date":"2026-05-27","title":"Project cards that open where they are","system":"structure","frame":"Sampling a project meant leaving the projects index and losing my place among the others.","dimensions":["where a card opens","what moves while it opens","how focus returns"],"move":"Project cards that expand in place with a FLIP transition and close back into the same spot.","backtalk":"People could follow which card opened, but scaling the whole card squeezed its text and images. Accents, takeaways, image zoom and keyframes added in July competed for attention and were removed, leaving one cancelable clock.","reframe":"Use one clock for opening and moving, and let reduced motion jump to the final layout.","commit":"15d94c048"},{"date":"2026-05-29","title":"Publications that tell stories","system":"structure","frame":"The publications page answered what I published, but hid the rejections and citation history behind the list.","dimensions":["what counts as an event","which views share one focus","whose clock a number follows"],"move":"A Steam-style Wall of Rejection with one receipt per rejection event, then a Scholar Lens (`089b29413`) whose focus travels between a bibliography row, its citation chip and its annual bars.","backtalk":"A badge can sit on several shelves without adding another event, so 38 Failure XP means four rejections. Lifetime totals and annual bars refresh on different dates, so both dates stay visible.","reframe":"Keep the citation list authoritative and let each view project from one record.","commit":"910f3c7f2"},{"date":"2026-06-29","title":"Material-Lite and an orange mark","system":"color-themes","frame":"Radii, shadows, focus rings and motion durations were one-off values across many partials.","dimensions":["token coverage","where orange may appear","mark legibility at 32 px"],"move":"Material 3-inspired tokens for radius, elevation, tonal surfaces, state layers, focus and motion, and a procedural 2D orange as the brand mark (`f08f22f19`).","backtalk":"Engraving ST into the fruit at navbar size made it read like a glossy app icon instead of a crisp site mark.","reframe":"Orange is identity only, and neutral surfaces carry most of the page.","commit":"1e3eb4f7d","sources":["material3"]},{"date":"2026-07-13","title":"A machine-readable research profile","system":"human-ai","frame":"Language models and agents read the same decorated pages people do.","dimensions":["route addressability","motion","source links","typography of the machine view"],"move":"An AI-readable profile at `/ai/`, `llms.txt` and a Human/AI switch in the navbar, built from the same canonical data as the human pages.","backtalk":"The machine view needed reading rules of its own, written into the heuristics with it. Server-rendered, source-linked and motion-free, in the site's normal theme and type, because a dark terminal look is not what makes a page readable to a model.","reframe":"Keep the two surfaces separate and connect them with ordinary links to the closest anchor.","commit":"d3f13be35","sources":["llms-txt"]},{"date":"2026-07-15","title":"Experiments with their own case studies","system":"structure","frame":"The design thinking behind each widget was hidden behind a history icon.","dimensions":["where an origin story lives","what a case page leads with","reproducibility"],"move":"Each site experiment got a case study with its question, change, evidence and limit, and a downloadable reproduction guide.","backtalk":"The projects index now leads from each experiment back to its origin, and a reader who follows it gets the question and the limit, not merely the commits.","reframe":"Origin stories belong to labeled places, the projects index and the case pages, not to marks on the work.","commit":"f05f02b41"},{"date":"2026-08-16","title":"Less, but more Sirui","system":"memory","frame":"The portfolio had collected containers, repeated explanations and equal-weight actions.","dimensions":["containers per section","type roles","repeated explanations"],"move":"Named the spine \"Less, but more Sirui\", locked the signature copy, set narrative type roles, separated Human and AI reading surfaces, and added the proportional QA lanes and the experiment backlog.","backtalk":"Fewer type levels, containers and actions made the remaining choices feel intentional, and the heuristics became a teaching artifact rather than a wall of rules.","reframe":"Record uncertain ideas with a hypothesis and a revisit trigger instead of freezing the style or shipping an effect without evidence.","commit":"4412a95ec"},{"date":"2026-09-05","title":"Skip link, tokens and a cinematic layer","system":"motion","frame":"I chose the cinematic tier. Walk a reader through one figure, let a card grid answer the pointer, and never take over scrolling.","dimensions":["which pages opt in","what plays once and what follows scroll","the still state under reduced motion"],"move":"A skip link, project navigation, spacing and motion tokens and a root view transition (`52e4b1bb7`), then GSAP-based scroll scenes, a spotlight and tilt on the projects grid, the DesignWeaver scroll scene, a homepage story thread and the card-to-hero morph.","backtalk":"My first review kept the projects index, the DesignWeaver scene, the marker and count-ups, the blog rows and the morph, and cut the side threads, the gradient field behind titles, the title drift and the papers-first reorder, because the design modification should be meaningful and add value instead of confusion (`74d6c7134`).","reframe":"When a review calls an element confusing, remove it rather than tune it.","commit":"10aea5f73","sources":["gsap"]},{"date":"2026-09-05","title":"A pinned Research Focus story","system":"motion","frame":"I called the homepage scroll a bit vanilla and pointed at pear.no as a reference.","dimensions":["what stays pinned","what the scroll position selects","depth between layers"],"move":"On wide screens the Research Focus sketch pins beside its three lens notes and the note in view picks the lens; the thesis arrived word by word, and shallow parallax separated layers that belong together.","backtalk":"The pinned sketch slid into the Exploring row and left its column half empty, because a sticky grid item can travel to the end of the whole grid; it now sits in its own well (`362100480`).","reframe":"Pinned stages need their own well, and any field behind a scene must fade before its box edge.","commit":"5acb08fe3","sources":["pear-no"]},{"date":"2026-09-08","title":"Retiring the build ledger","system":"structure","frame":"The homepage published what the site cost in machine work, tokens, agent-hours, commits and kWh, as an honest receipt.","dimensions":["what the number tells a reader","accounting cost","side effects on the history"],"move":"Removed the ledger, the desk tag, the Build Rhythm token surfaces and every tool behind them. One sentence now links this story and Build Rhythm.","backtalk":"Once every agent user had billions of tokens the numbers stopped differentiating anything. The audit read one machine's logs, and 236 refresh commits inflated the very commit count it displayed.","reframe":"Bring a number back only with an exact, privacy-safe source and a story in which the number changes what a reader understands.","commit":"bba74ccc1"},{"date":"2026-09-12","title":"A research studio, then one type family","system":"type-reading","frame":"The homepage needed concrete selected work before the narrative, and a calmer reading surface everywhere.","dimensions":["order of the homepage","display and reading faces","reading column"],"move":"The homepage led with the research question and selected work, a Newsreader display face sat over Inter, and the 3D desk became an optional inhabited home.","backtalk":"Two days later I replaced the mixed serif and sans system with Inter throughout, organized by spacing and a small hierarchy (`9c746dfe9`).","reframe":"Spend type contrast through size and weight within one family.","commit":"72ce1637d","sources":["inter-font"]},{"date":"2026-09-14","title":"La Jolla along the footer","system":"objects-place","frame":"I asked for a miniature La Jolla at the end of every reading route.","dimensions":["which places appear","how groups reveal","load timing","phone layout"],"move":"A Blender-authored coast that fills the footer and reveals its building groups left to right as it comes into view, with a static fallback.","backtalk":"On September 28 I rejected generic trees as widescreen filler and then the evenly spaced landmark lineup, because distinct names alone do not make a convincing place (`87e2fd4d7`).","reframe":"Group the campus, home and village through varied setbacks, continuous bluffs and a recessed Cove.","commit":"bb56e6af5"},{"date":"2026-09-28","title":"Thumbnails stay in color","system":"color-themes","frame":"Fun-project thumbnails sat in grayscale until hover, after an approved reading enhancement.","dimensions":["thumbnail color at rest","consistency across the index"],"move":"Removed the grayscale treatment; every thumbnail keeps its original color in every state.","backtalk":"Mixed grayscale and color thumbnails made the project index read as inconsistent.","reframe":"An interaction reference is not a reason to hide the work.","commit":"1e0e67dbf","sources":["react-bits"]},{"date":"2026-10-05","title":"Thesis, focus, papers, updates","system":"structure","frame":"The thesis and the Research Focus were split and repeated, and the thesis faded in word by word with scroll.","dimensions":["section order","repetition","legibility of the thesis"],"move":"One sequence (opening, thesis, the full focus once, selected publications, compact updates, students, contact), with the thesis as fully legible text.","backtalk":"The first published iteration ended its visual regression run with two failures, a stale manual-scroll direction and lazy print images that had not loaded before capture; the follow-up repaired both.","reframe":"Explain the thesis once and render the focus once.","commit":"c27bbc734"},{"date":"2026-10-12","title":"A studio log of moves","system":"memory","frame":"Design moves were scattered across commits, backlog entries and conversations, so the next session started from scratch.","dimensions":["fields per move","where the primary source sits","how a move links to code"],"move":"Started a studio log of cards with frame, dimensions, move, backtalk, reframe and a concept with its primary source.","backtalk":"Forty cards in four days, and the dimensions turned vague requests into variables someone could reason about.","reframe":"Turn the record itself into the design space that the next move starts from.","commit":"cd11ec53e","sources":["schon1983reflective"]},{"date":"2026-10-12","title":"The screenshot that ran out of memory","system":"type-reading","frame":"CI visual checkpoints had been red since the companion landed, because Linux Chromium could not capture the full HCI Spooder-Man page.","dimensions":["decoded bitmap bytes per page","image count","capture area"],"move":"The page serves the 480, 800 and 1400-pixel WebP variants CI already generates, sized to how large each image is shown.","backtalk":"Transfer fell from 33 MB to 5.2 MB. One 3,838 × 1,588 PNG decodes to about 24 MB of RGBA whatever its 4 MB file size, which is why the failure looked random. The fixed page passed 16 of 16 runs in the Linux Playwright image.","reframe":"File size and memory are different budgets; serve pixels at display size.","commit":"702030cda"},{"date":"2026-10-12","title":"Four tries at an invisible-but-findable mark, then removal","system":"structure","frame":"A tiny origin glint beside seven widgets read as a stray rendering speck in my October 12 review.","dimensions":["salience at rest against on engagement","repetition","target size","wording"],"move":"Removed the mark; the projects index line that already had a visible label became a plain text link.","backtalk":"The history is the evidence. A dot and line read as a slider, a hover disclosure confused, a story wordmark was too loud when repeated, and the glint read as an artifact. Each tried to be both invisible and findable.","reframe":"Route-finding belongs to labeled places, not to decorations on the work.","commit":"84083f827","sources":["pirolli1999foraging"]},{"date":"2026-10-12","title":"Sixty-eight characters, measured in the wrong unit","system":"type-reading","frame":"Posts and case studies ran about 95 characters per line at 1440 px.","dimensions":["characters per line","the font each element measures ch against","alignment with figures and the contents rail"],"move":"One centered 33.5rem column for prose, headings and the post header, with figures and code reaching 68ch.","backtalk":"The first try, max-width 50ch, narrowed paragraphs but not headings; a later rule also reset the margins, and the contents layout pinned prose to the left.","reframe":"Size the column once in rem and let every element inherit it; keep ch for single-font contexts such as code.","commit":"89155618a","sources":["css-values-4"]},{"date":"2026-10-12","title":"Order is a claim","system":"structure","frame":"DesignWeaver, the flagship paper, sat third on the homepage because the bibliography lists papers in file order.","dimensions":["display order against data order","one source of truth","test coverage"],"move":"A declared `selected_paper_order` rendered with `nocite` and `--cited_in_order`, with a test that keeps it equal to the selected entries.","backtalk":"`nocite` works inside a loop because jekyll-scholar resolves a key as a variable before treating it as a literal.","reframe":"Ordering is an explicit, tested decision instead of a side effect of file order.","commit":"c5974d7c7","sources":["murdock1962serial"]},{"date":"2026-10-12","title":"A poster frame for a moving figure","system":"type-reading","frame":"Two Physion previews were 2.2 MB and 6.9 MB GIFs that every homepage and publications visitor downloaded and watched loop.","dimensions":["bytes per listing page","motion by default against on request","descriptive alt text"],"move":"A first-frame poster through responsive WebP variants, which zooms into the original and offers to play the animation.","backtalk":"The publications page fell from 7.3 MB to 4.9 MB. A closing brace inside a Liquid output tag ended it early until the filter moved into an assign.","reframe":"Motion on request also serves reduced-motion visitors without a special case.","commit":"041558952","sources":["wcag-pause-stop-hide"]},{"date":"2026-10-12","title":"Retry after the page settles","system":"qa","frame":"Visual checkpoints failed with \"Unable to capture screenshot\" on a different route each run.","dimensions":["main-thread idleness at capture time","retry count and backoff","runner cores"],"move":"The harness waits for two animation frames and an idle callback, then retries at 250, 750 and 1,500 ms; any other error still fails at once.","backtalk":"The next run was green across every site and scene job, the first fully green visual checkpoints since the companion landed.","reframe":"Keep the retry and still make the page cheaper, because work that stalls a two-core runner also stalls a slow phone.","commit":"26a5e24ce","sources":["html-event-loop"]},{"date":"2026-10-12","title":"Record art 40 times larger than its frame","system":"type-reading","frame":"The homepage preloaded four 1024 px record-art PNGs (6.4 MB) for a disc never shown wider than about 340 CSS pixels.","dimensions":["device pixels needed","bytes","behavior when a variant is missing"],"move":"The record art points to 800 px WebP variants of 31 to 46 KB each, with the originals as a fallback.","backtalk":"The homepage fell from 12.3 MB to 6.1 MB on the local preview, and the 2D and 3D records behave as before.","reframe":"What remains is the 3D coast model, three.js, CSS and fonts.","commit":"b26b8c7dd"},{"date":"2026-10-12","title":"Test the invariants the site already keeps","system":"qa","frame":"Before adding assertions, check whether the site already meets them, so a test records reality instead of hope.","dimensions":["h1 count","the first tab stop","images without alt","30 routes at two widths"],"move":"A one-off audit, then three assertions on every public route.","backtalk":"All 60 route-and-width pairs already passed; the test now keeps them that way.","reframe":"The next accessibility work is behavioral, such as a global motion pause and focus return.","commit":"4692a2556","sources":["wcag-bypass-blocks","wcag-non-text"]},{"date":"2026-10-12","title":"Twelve cards, one row","system":"structure","frame":"On a 390 px phone the twelve site-experiment cards stacked into about 6,650 px of scrolling.","dimensions":["section height","how much of the next card shows","position feedback","an expanded card's width"],"move":"Below 768 px the grid becomes one scroll-snapped row with each card 84 % wide, a \"1 / 12\" count and arrow keys; an expanded card widens to the full row.","backtalk":"The section shrank to about 690 px, and the disclosure test still finds every story with no clipping or overflow.","reframe":"The desktop grid stays; a carousel pays off only where the column is one card wide.","commit":"a7a330409","sources":["css-scroll-snap"]},{"date":"2026-10-12","title":"A scroll-driven laptop, no script","system":"motion","frame":"This page told its before and after in captions, and the change itself was easy to miss.","dimensions":["scroll progress","lid angle","wipe position","which capture each moment shows"],"move":"A CSS laptop pinned while the reader scrolls, opening on the February capture and wiping along an orange seam to an October capture of the same view.","backtalk":"The first wipe showed the seam during the lid opening, because an animation's `both` fill applies its first keyframe early, and the base's extra width overflowed phones. Both were fixed.","reframe":"Use the pattern only where a single comparison carries the story.","commit":"9ff8c136a","sources":["css-scroll-animations"]},{"date":"2026-10-12","title":"Springs, named by their damping","system":"motion","frame":"Buttons and menus moved on fixed 180 ms eases, so a press felt like a fade rather than something physical.","dimensions":["damping ratio","period","which controls get a bouncier spring"],"move":"A confident spring for buttons and menus and a playful one for the desk toys, generated as CSS `linear()` curves.","backtalk":"Measured in the browser, one disclosure grows from 46 to 477 px in about 160 ms, overshoots to 481 and settles.","reframe":"Keep springs on direct manipulation and discrete toggles; ambient motion stays on eases or stops.","commit":"032a6facc","sources":["apple-springs"]},{"date":"2026-10-12","title":"A palette that had quietly lost its curation","system":"structure","frame":"The ⌘K palette offered light, dark and system themes this site does not have, and none of its curated starting routes.","dimensions":["which data the palette renders","what each action controls","what every page may say"],"move":"The curated data moved to an inline include, with Do actions that call the site's own controls.","backtalk":"The first push broke the deploy, because the palette now renders on the AI profile and its email action used an address the validator forbids, and a comma split one theme description into a nonsense fifth action.","reframe":"Run the production build and the output validator before pushing anything that renders on every page.","commit":"ed0e831f5"},{"date":"2026-10-13","title":"A comic panel that prints in","system":"motion","frame":"The Spooder-Man gallery cross-faded like any slideshow, though the page is a love letter to Spider-Verse.","dimensions":["plate offset","dot strength","drawings per second","caption overlap"],"move":"Each new slide comes in as red, green and blue plates that move into register over three held drawings at 12 per second, while halftone dots recede.","backtalk":"The first version also stepped the old cross-fade, and on its first drawing both captions showed at once; a hard cut keeps one caption.","reframe":"Keep the effect on change only.","commit":"eb20fea31"},{"date":"2026-10-13","title":"Glass that bends only where it can","system":"motion","frame":"I asked for liquid-glass chrome, and the bar was already frosted, so the question was what glass could add on a site that is mostly text on paper.","dimensions":["where refraction is visible","bevel height and displacement","legibility","engine support","scroll cost"],"move":"A 10 px lip under the navigation bar refracts what scrolls beneath it, in Chromium only.","backtalk":"A 14 px lip smeared text into a glitch, a glass theme menu let words show through, and moving the frost into a pseudo-element broke it outright, because the bar's view-transition name makes it a backdrop root. Scrolling stays at 60 fps with it on or off.","reframe":"Glass reads as glass over images and motion, not over paper.","commit":"90e2c7d19","sources":["filter-effects-2"]},{"date":"2026-10-13","title":"The scaffold, small enough to try","system":"objects-place","frame":"The DesignWeaver page described dimensional scaffolding in words and figures, and a visitor never got to feel what it does to a prompt.","dimensions":["fidelity to the study","what the page itself writes","what a reader sees change"],"move":"A palette with four dimensions and the tags from a CHI 2025 talk session, opening on that session's prompt and rebuilding a fixed sentence on any change.","backtalk":"A word counter backfired, because the fixed sentence is terser than the model's prose and adding an eighth tag dropped the count from 26 words to 16; it now counts tags across dimensions.","reframe":"The new copy joins the batch for my review.","commit":"9dc3a5f16","sources":["wood1976scaffolding"]},{"date":"2026-10-13","title":"A sun you can drag, and a switch that stills everything","system":"color-themes","frame":"Four theme buttons felt static. I asked for a control that feels like the day itself and syncs the 3D scenes, and for a good-looking way to pause motion.","dimensions":["where an hour lives","which surfaces follow every move","contrast of continuous color","how many places must learn about paused"],"move":"A sun arc that picks any minute and previews it across the page, the footer coast and the 3D home, and a Pause motion switch laid over the reduced-motion query.","backtalk":"Continuous color stays out of text. The first sky glow at 17 % opacity was invisible, while 30 % reads as light without touching contrast, and one of the tests expected 1:20 pm plus four hours to be evening.","reframe":"Keep the panel's four stops; they are both the quick choice and the test contract.","commit":"21fa75817","sources":["shneiderman1983direct"]},{"date":"2026-10-13","title":"A coffee ring, by the physics of drying","system":"objects-place","frame":"The coffee stain was a broken ring of dashes, closer to a stamp than a stain, and it nearly vanished in the evening theme.","dimensions":["rim sharpness","tide lines","a second ring","drips","strength in light and dark themes"],"move":"The ring drawn the way one dries, with a continuous rim sharp outside and fading inward, tide lines and drips, and a lighter dried palette in the evening.","backtalk":"The first interior turned into pale clouds and its fiber displacement sawtoothed the rim.","reframe":"The stain is a desk cue, not a feature.","commit":"0ebf99fb7","sources":["deegan1997coffee"]},{"date":"2026-10-14","title":"A stain that looked like glass","system":"objects-place","frame":"I found the redrawn stain lamer than before. In place it read as a pale glass disc, with a perfect thin outline and bubble-like drips.","dimensions":["medium","how coffee spreads as it dries","the paper beneath","main-thread cost","what the security policy allows"],"move":"A raster of per-pixel coffee density, painted fresh on every visit as washes with dark edges, backruns and granulation, in time-boxed idle slices.","backtalk":"The first rasters were thin and grey, and the puddles looked like stickers until they dried like watercolour. The first integration showed nothing, because the Content-Security-Policy admits data URLs but not blob URLs.","reframe":"Keep the stain quiet enough that any text it crosses stays easy to read.","commit":"89f45a4da","sources":["curtis1997watercolor"]},{"date":"2026-10-15","title":"Technical records that the next pass can start from","system":"memory","frame":"I asked for the technical details, key source code and citations to be recorded behind each long-running project, so later agents can build on what has been done.","dimensions":["record schema","what a person reads and what a program reads","how a next move is proposed","integrity checks"],"move":"Three records as data, each rendered as a page beside its project, served as JSON and turned into a next-move brief by a script, with a test for paths, sources, systems and commits.","backtalk":"Compiling them surfaced real gaps, such as the clifftop planting swaying on its own rule outside the shared wind field. A record route under a project made the AI link point at a missing anchor, so pages can now name their AI anchor.","reframe":"Append each new move to its record when it ships, and let the brief rank what to try next."}],"open_problems":[{"id":"visitor-comprehension","title":"Does the redesign help visitors understand the research?","system":"structure","why_hard":"Every comparison here is design history under fixed captures, not a study. Comprehension needs real visitors, a task and a baseline, and the old site exists only as an archive.","candidate_moves":["A small first-glance test with research peers and prospective students, asking what I study after 15 to 30 seconds, on the archived and current homepages."],"sources":[],"readiness":"needs-research"},{"id":"foundation-debt","title":"Foundation work still open from the September roadmap","system":"qa","why_hard":"The focus mixin migration, the accessibility spec additions and purge parity as a documented lane each touch many partials or the deploy path, so they keep losing to visible work.","candidate_moves":["Migrate focus styles to one mixin, partial by partial, with the overrides audit after each.","A documented lane that builds, purges and serves `_site` for the checkpoint suite."],"sources":[],"readiness":"ready"},{"id":"retell-waves","title":"Retell the remaining project stories","system":"structure","why_hard":"Each story deserves the medium its evidence supports, and a uniform template would flatten them. Deep rewrites go one route at a time with fixed before and after captures.","candidate_moves":["Playful provenance stories first, keeping each joke and moving custody details behind it.","Research artifacts next, leading with the question and the canonical publication."],"sources":[],"readiness":"needs-decision"},{"id":"continuous-world","title":"A continuous illustrated world below the desk","system":"objects-place","why_hard":"A scroll-scrubbed backdrop carrying the desk's horizon and coast down the homepage needs artwork, must stay quieter than text, and must not move the largest contentful paint away from the hero.","candidate_moves":["A first generated or authored asset reviewed at four viewports, with its prompt, model, date and license recorded first."],"sources":["pear-no"],"readiness":"needs-decision"},{"id":"sidenotes","title":"Margin notes for long posts","system":"type-reading","why_hard":"No post or project uses footnotes yet, so margin notes have nothing to show.","candidate_moves":["Build sidenotes with the first post that needs footnotes."],"sources":[],"readiness":"blocked"},{"id":"homepage-tokens","title":"Bring the homepage onto the shared tokens","system":"color-themes","why_hard":"The homepage's own partial predates the Material-Lite and type tokens and is a high-conflict file shared with the desk scene, so changes there need one writer at a time.","candidate_moves":["Adopt the spacing and focus tokens in `_sass/_home.scss` in a dedicated pass, checked with the iteration and checkpoint lanes."],"sources":[],"readiness":"ready"}],"sources":[{"id":"wayback-2026-02-09","label":"Wayback, February 9, 2026","citation":"Internet Archive. Wayback Machine capture of dylantao.github.io, February 9, 2026, 01:34:29 UTC.","url":"https://web.archive.org/web/20260209013429/https://dylantao.github.io/"},{"id":"schon1983reflective","label":"Schön 1983","citation":"Donald A. Schön. *The Reflective Practitioner: How Professionals Think in Action*. Basic Books, 1983.","url":"https://www.hachettebookgroup.com/titles/donald-a-schon/the-reflective-practitioner/9780465068784/"},{"id":"al-folio","label":"al-folio","citation":"Maruan Al-Shedivat and contributors. *al-folio*, a Jekyll theme for academics, version 1.","url":"https://github.com/alshedivat/al-folio"},{"id":"inter-font","label":"Inter","citation":"Rasmus Andersson. *Inter* typeface (SIL Open Font License).","url":"https://rsms.me/inter/"},{"id":"material3","label":"Material Design 3","citation":"Google. *Material Design 3*.","url":"https://m3.material.io/"},{"id":"paper-shaders","label":"Paper Shaders","citation":"Paper. *Paper Shaders*, `@paper-design/shaders` 0.0.80 (Apache-2.0).","url":"https://shaders.paper.design/"},{"id":"gsap","label":"GSAP","citation":"GreenSock. *GSAP* 3.15 and ScrollTrigger, under the GSAP Standard License.","url":"https://gsap.com/"},{"id":"heer2007transitions","label":"Heer and Robertson 2007","citation":"Jeffrey Heer and George Robertson. *Animated Transitions in Statistical Data Graphics*. IEEE Transactions on Visualization and Computer Graphics 13(6), 2007.","url":"https://idl.cs.washington.edu/files/2007-AnimatedTransitions-InfoVis.pdf"},{"id":"segel2010narrative","label":"Segel and Heer 2010","citation":"Edward Segel and Jeffrey Heer. *Narrative Visualization: Telling Stories with Data*. IEEE Transactions on Visualization and Computer Graphics 16(6), 2010.","url":"https://idl.cs.washington.edu/files/2010-Narrative-InfoVis.pdf"},{"id":"counterpoint2024","label":"Sivaraman and Perer 2024","citation":"Venkatesh Sivaraman and Adam Perer. *Counterpoint: Orchestrating Large-Scale Custom Animated Visualizations*. CMU Data Interaction Group, 2024.","url":"https://dig.cmu.edu/publications/2024-counterpoint.html"},{"id":"chartability","label":"Chartability","citation":"Frank Elavsky. *Chartability*, heuristics for accessible data visualization.","url":"https://www.frank.computer/chartability/"},{"id":"css-view-transitions-2","label":"CSS View Transitions 2","citation":"W3C. *CSS View Transitions Module Level 2*.","url":"https://www.w3.org/TR/css-view-transitions-2/"},{"id":"css-scroll-animations","label":"Scroll-driven Animations","citation":"W3C. *Scroll-driven Animations*.","url":"https://www.w3.org/TR/scroll-animations-1/"},{"id":"css-scroll-snap","label":"CSS Scroll Snap","citation":"W3C. *CSS Scroll Snap Module Level 1*.","url":"https://www.w3.org/TR/css-scroll-snap-1/"},{"id":"apple-springs","label":"Apple, Animate with springs","citation":"Apple. *Animate with springs*, WWDC23.","url":"https://developer.apple.com/videos/play/wwdc2023/10158/"},{"id":"css-values-4","label":"CSS Values 4","citation":"W3C. *CSS Values and Units Module Level 4*, font-relative lengths.","url":"https://www.w3.org/TR/css-values-4/"},{"id":"murdock1962serial","label":"Murdock 1962","citation":"Bennet B. Murdock. *The Serial Position Effect of Free Recall*. Journal of Experimental Psychology 64(5), 1962.","url":"https://doi.org/10.1037/h0045106"},{"id":"pirolli1999foraging","label":"Pirolli and Card 1999","citation":"Peter Pirolli and Stuart Card. *Information Foraging*. Psychological Review 106(4), 1999.","url":"https://doi.org/10.1037/0033-295X.106.4.643"},{"id":"wcag-pause-stop-hide","label":"WCAG 2.2, pause, stop, hide","citation":"W3C. *Understanding Success Criterion 2.2.2, Pause, Stop, Hide*, WCAG 2.2.","url":"https://www.w3.org/WAI/WCAG22/Understanding/pause-stop-hide.html"},{"id":"wcag-contrast","label":"WCAG 2.2, contrast","citation":"W3C. *Understanding Success Criterion 1.4.3, Contrast (Minimum)*, WCAG 2.2.","url":"https://www.w3.org/WAI/WCAG22/Understanding/contrast-minimum.html"},{"id":"html-event-loop","label":"HTML Standard, event loops","citation":"WHATWG. *HTML Living Standard*, event loops and the processing model.","url":"https://html.spec.whatwg.org/multipage/webappapis.html#event-loops"},{"id":"wcag-bypass-blocks","label":"WCAG 2.2, bypass blocks","citation":"W3C. *Understanding Success Criterion 2.4.1, Bypass Blocks*, WCAG 2.2.","url":"https://www.w3.org/WAI/WCAG22/Understanding/bypass-blocks.html"},{"id":"wcag-non-text","label":"WCAG 2.2, non-text content","citation":"W3C. *Understanding Success Criterion 1.1.1, Non-text Content*, WCAG 2.2.","url":"https://www.w3.org/WAI/WCAG22/Understanding/non-text-content.html"},{"id":"shneiderman1983direct","label":"Shneiderman 1983","citation":"Ben Shneiderman. *Direct Manipulation: A Step Beyond Programming Languages*. IEEE Computer 16(8), 1983.","url":"https://doi.org/10.1109/MC.1983.1654471"},{"id":"deegan1997coffee","label":"Deegan et al. 1997","citation":"Robert D. Deegan, Olgica Bakajin, Todd F. Dupont, Greg Huber, Sidney R. Nagel and Thomas A. Witten. *Capillary Flow as the Cause of Ring Stains from Dried Liquid Drops*. Nature 389, 1997.","url":"https://doi.org/10.1038/39827"},{"id":"curtis1997watercolor","label":"Curtis et al. 1997","citation":"Cassidy J. Curtis, Sean E. Anderson, Joshua E. Seims, Kurt W. Fleischer and David H. Salesin. *Computer-Generated Watercolor*. SIGGRAPH 1997.","url":"https://grail.cs.washington.edu/projects/watercolor/"},{"id":"filter-effects-2","label":"Filter Effects 2, backdrop root","citation":"W3C. *Filter Effects Module Level 2*, the backdrop root.","url":"https://drafts.fxtf.org/filter-effects-2/"},{"id":"filter-effects-1","label":"Filter Effects 1, feDisplacementMap","citation":"W3C. *Filter Effects Module Level 1*, the `feDisplacementMap` filter primitive.","url":"https://www.w3.org/TR/filter-effects-1/"},{"id":"wood1976scaffolding","label":"Wood, Bruner and Ross 1976","citation":"David Wood, Jerome S. Bruner and Gail Ross. *The Role of Tutoring in Problem Solving*. Journal of Child Psychology and Psychiatry 17(2), 1976.","url":"https://doi.org/10.1111/j.1469-7610.1976.tb00381.x"},{"id":"mq5-reduced-motion","label":"Media Queries 5, reduced motion","citation":"W3C. *Media Queries Level 5*, the `prefers-reduced-motion` feature.","url":"https://www.w3.org/TR/mediaqueries-5/#prefers-reduced-motion"},{"id":"llms-txt","label":"The /llms.txt proposal","citation":"Jeremy Howard and contributors. *The /llms.txt file*, a proposal for language-model-readable site indexes.","url":"https://llmstxt.org/"},{"id":"visualwebarena2024","label":"Koh et al. 2024","citation":"Jing Yu Koh, Robert Lo, Lawrence Jang, Vikram Duvvur, Ming Lim, Po-Yu Huang, Graham Neubig, Shuyan Zhou, Russ Salakhutdinov and Daniel Fried. *VisualWebArena: Evaluating Multimodal Agents on Realistic Visual Web Tasks*. ACL 2024.","url":"https://aclanthology.org/2024.acl-long.50/"},{"id":"paxel-yc","label":"Y Combinator design walkthrough","citation":"Eve Bouffard with Aaron Epstein. Y Combinator's 2026 design walkthrough, demonstrating Paxel's Human and Machine views.","url":"https://youtu.be/VbqaL_eHhKY?t=433"},{"id":"pear-no","label":"pear.no","citation":"Pear. *pear.no*, a reference for one pinned scene whose copy changes as the reader scrolls.","url":"https://pear.no/"},{"id":"react-bits","label":"React Bits, Chroma Grid","citation":"React Bits. *Chroma Grid* and *Folder* components, studied as interaction references; no code was copied.","url":"https://reactbits.dev/components/folder"},{"id":"playwright","label":"Playwright","citation":"Microsoft. *Playwright*, browser automation for the visual harness.","url":"https://playwright.dev/"}]}