# Alex Jardine: published website content > Alex Jardine is a web developer and technical SEO specialist in Toronto, with a background in design and growth. Remote across Canada and the US, in person across the GTA. Generated from the same published content as the website. Each document names its original HTML source; screenshots, recordings and interactive examples remain on those pages. Project-specific evidence and limitations are retained. --- Source: https://alexjardinedigital.com/ Author: Alex Jardine Alex Jardine Development · Technical SEO · Design Toronto Web developer and technical SEO specialist Freelance · White-label · Contract # I build websites and web applications, with technical SEO built in. [See the work ↓]() [Work with me]() What I do My work connects development, design and technical SEO. I design and build websites and applications that work well and get found. I can take on a defined part of a project or work with your team through launch. **Independent web developer and technical SEO specialist,** with a background in design. Available for freelance projects, white-label agency partnerships and contract roles. [Resume]() ## Every project gets its own world. Design + full-stack development Light demos ## Lambert Lighting I designed and built the complete catalogue site, including a custom page generator, specification comparisons, contextual product-file requests and a WebGL lighting system. **13** Production pages **3 to 4 wk** Strategy through launch [Read the case study ↗]() [Live site ↗]() [![Lambert Lighting website with architectural imagery and product-family navigation.]()]() Creative direction Design + full-stack development Search setup ## The Black Salt Room I designed and built the complete Next.js site, from its visual direction and structured content to a validated inquiry API and technical SEO. **100** Lighthouse scores, launch build **1.5 s** Mobile LCP, launch build [Read the case study ↗]() [Live site ↗]() [![The Black Salt Room website with charcoal and amber art direction.]()]() Design + full-stack development Service copy ## B&B Contractors I designed, wrote and built the complete Next.js site, including its technical SEO and a validated, rate-limited estimate backend. The live site uses email enquiries at the client’s request. **17** Website-generated jobs **6** Service groups Owner-reported · October 2nd 2026 [Read the case study ↗]() [Live site ↗]() [![B&B Specialty Contractors website introducing the business and its services.]()]() Local SEO Destination content Mews booking ## Palisades Lodge Local search, destination content and a custom Mews booking path for a new Eastern Sierra lodge. **+248%** Organic clicks, Search Console **12,006** Business Profile views Google data · May to October 2026 [Read the case study ↗]() [Project context ↗]() [![Palisades Lodge of Big Pine: the lodge building with its mountain-scene sign above the street.]()]() In their words > Since the launch, I've been staying fully booked. We receive a constant stream of new inquiries and reservations directly through the site every single day. Alina, Owner, The Black Salt Room "The migration went wonderfully, and Alex was a joy to work with." Bodie Foundation · Shopify migration with zero merchant downtime "I just needed a simple, clean site, a basic digital business card to share during meetings. Alex gave me exactly what I asked for, and the whole process was completely effortless." Eren B, Owner, B&B Specialty Contractors · 17 website-generated jobs, owner-reported ## Growth work on agency and in-house teams. SEO, content and website performance for software, professional services, robotics, fitness and recreation brands, before and alongside the studio. [Career history]() ![Copper CRM]() ![BDO Canada]() ![Standard Bots]() ![Parasoft]() ![GrowerIQ]() ![Movati Athletic]() ![Fairgrounds Racket Club]() ## Software I built and still run. Client sites get the same engineering: typed code, automated tests, security reviews and a search check on every release. [![Silktrace domain overview with saved organic traffic history, ranking keywords and top pages.]() Own product · Live SaaS ### Silktrace SEO research in one private workspace. Encrypted provider credentials, and a signed approval before any paid request runs. **41** API routes, each access-classified **10+** Signed-up users Next.js · TypeScript · PostgreSQL ↗]() [![Jardine Studio homepage with its generated gold dither field.]() Own studio platform ### Jardine Studio My studio's site, free SEO tools and lead pipeline. Every pull request is rebuilt and checked page by page for lost search signals before it can merge. **845** Automated tests **1,000+** Commits since May 2026 Next.js · Redis · Playwright ↗]() Open source on GitHub: [Domain Looker]() and [GPU Flight Recorder](), both MIT licensed. [See my GitHub]() [Work ### All projects Client sites, products and growth programs. ↗]() [Services ### How I can help Development, technical SEO and design, with flexible ways to work together. ↗]() [About ### Who you'd work with My background, experience and how I work with clients and teams. ↗]() --- Source: https://alexjardinedigital.com/work Author: Alex Jardine Work # Websites, software and growth. Client websites first, then the software: tools built around real working problems, from SEO research and lead review to content operations and system diagnostics. Hover or focus a project Open a project to step into its world. [![Palisades Lodge of Big Pine: the lodge building with its mountain-scene sign above the street.]() ### Palisades Lodge Growth partner for a new Eastern Sierra lodge: organic clicks up 248%, 42 Google reviews from none, and a custom Mews booking step. Client · Local SEO *↗*]() [![Lambert Lighting website with architectural imagery and product-family navigation.]() ### Lambert Lighting I designed and built the complete catalogue site, including a custom page generator, specification comparisons, contextual product-file requests and a WebGL lighting system. Client · Design + full-stack development *↗*]() [![The Black Salt Room website with charcoal and amber art direction.]() ### The Black Salt Room I designed and built the complete Next.js site, from its visual direction and structured content to a validated inquiry API and technical SEO. Client · Design + full-stack development *↗*]() [![B&B Specialty Contractors website introducing the business and its services.]() ### B&B Contractors I designed, wrote and built the complete Next.js site, including its technical SEO and a validated, rate-limited estimate backend. The live site uses email enquiries at the client’s request. Client · Design + full-stack development *↗*]() [![Silktrace saved Palisades domain report with visibility, keyword coverage and links to further reports.]() ### Silktrace A live SEO research SaaS with private workspaces, encrypted credentials and request-bound spending, designed and built solo. Own product · Product design *↗*]() [![Current Jardine Studio homepage in dark mode with a gold ordered-dither field and the headline Websites, systems, and search.]() ### Jardine Studio A production web platform I built and run: site, publishing system, free tools and lead pipeline, with 845 automated tests behind it. Own studio · Design & development *↗*]() ## Smaller builds and tools Builds and tools for my own work. Two are open source on [GitHub](). 1. [Own site Migration · Technical SEO ### Next.js migration A static portfolio moved onto Next.js with 212 before-and-after screenshot pairs as proof.]() 2. [Own tool CMS · Product engineering ### AWL The private control room for my studio site: every page, price and article edited safely and committed to Git.]() 3. [Own tool Full-stack · Product design ### Lead Finder A prospecting operating system for SEO agencies: discovery, AI qualification and a pipeline that remembers every business.]() 4. [Open source Systems tooling · Product design ### GPU Flight Recorder An open-source Windows recorder that keeps evidence alive when the display freezes.]() 5. [Open source Utility · Full-stack ### Domain Looker An open-source bulk domain checker that never mistakes a failed lookup for an available name.]() ## Agency and contract work Work done inside agencies and teams, shown with my part only. 1. In-house Webflow · Migration SEO ### Copper CRM: Craft CMS to Webflow migration SEO on the in-house team moving a 200+ page marketing site: redirect and URL mapping, content migration and launch SEO QA. 2. Agency · VonClaro Webflow · Technical SEO ### Standard Bots: technical SEO and content strategy Technical SEO fixes plus keyword and content strategy on an industrial robotics company’s Webflow site. 3. Contract · SharpEnd Shopify · Migration ### Bodie Foundation: Ecwid to Shopify migration Ran the whole move: products and data, redirects and SEO. Launched with zero merchant downtime. “The migration went wonderfully, and Alex was a joy to work with.” 4. Agency · VonClaro WordPress · Elementor ### GrowerIQ: WordPress and Elementor improvements Ongoing work on an existing build: fixed issues and refined the design and mobile layouts with targeted HTML and CSS. --- Source: https://alexjardinedigital.com/services Author: Alex Jardine Services # Development. Technical SEO. Design. I build websites and web applications, handle migrations, and work on the technical SEO behind them. I can deliver a complete project, take on a specific part, or join an existing team. I can also design the site or build from your team’s designs. [See the work ↗]() [For agencies ↗]() ## Development Websites, web applications and the systems behind them: booking, forms, integrations and custom tools. I work on new builds, existing codebases and platform migrations, with a clear handoff for whoever maintains the work. - Custom websites - Booking flows - Intake flows - CRM integrations - Custom tools - Member areas - Platform migrations [See AWL]() ## Technical SEO Technical audits and implementation, migration planning, redirects, structured data and search measurement. I also work on local search and content structure, with changes guided by evidence and results measured over time. - Technical SEO - Local SEO - Content architecture - Structured data - AI search visibility - Website audits [See Palisades Lodge]() ## Design Website structure, interface design and visual direction, from a new site to improvements within an existing brand. I can develop the design or build from your team’s work. - Visual identity - Page design - Brand systems - Site redesigns - Motion and WebGL - Landing pages [See Lambert Lighting]() How a project runs ## From the brief to a clean handoff. ### Diagnose Understand the brief, review what already exists and agree on the work I’ll own. ### Design Work from your designs or develop the structure and interface where needed. ### Build Implement the agreed scope, with progress you can review along the way. ### Launch Check functionality, accessibility, performance and search essentials, then hand over the work with documentation. ### Support Resolve post-launch issues and agree on any ongoing development or SEO work. Working together ## Direct contact. Clear ownership. ### Direct access You work directly with me, with clear responsibilities, regular updates and a documented handoff. ### Agreed availability We agree the scope, working hours and delivery schedule before work starts. ### Flexible delivery I can own a defined scope or work alongside your team. If additional specialists are needed, we agree on their involvement before they join. [How I work with agencies]() --- Source: https://alexjardinedigital.com/agencies Author: Alex Jardine For agencies # Development and technical SEO for your team. I work with design and marketing agencies on web development and technical SEO: freelance support, white-label builds, migrations and ongoing work. I fit into your process, work with your designers and developers, and keep your client relationship in your hands. Available now · Toronto, working across ET and PT [Start an agency conversation]() [Capability sheet, PDF]() Experience ## Agency and in-house experience. ### VonClaro Growth manager across eight agency client accounts. I led the SEO that doubled organic sessions and grew organic leads 43% in six months for an equipment manufacturer. ### SharpEnd Contract web and SEO delivery, including Palisades Lodge and Bodie Foundation’s move from Ecwid to Shopify with zero merchant downtime. ### Copper CRM SEO on the in-house migration of a 200+ page marketing site from Craft CMS to Webflow: redirect mapping, content migration and launch QA. ## How I plug in When your team is full or a brief needs a specialist. I work inside your process and deliver under your name, to your standards. - Overflow builds - White-label delivery - Ongoing retainers - Fixed-scope projects ## What I can own Builds from your Figma files, CMS work, integrations and migrations, with technical SEO checks before, during and after launch. - Next.js and React builds - Builds inside your design system - WordPress and Elementor fixes - Shopify setup and migrations - Webflow migrations and SEO - Redirect maps and migration QA - Schema and GA4 - Accessibility and performance passes [See the work]() ## What you get at handoff Documented work your team can maintain and extend. - QA checklist results - README and handoff doc - 30 days of post-launch fixes How an engagement runs ## From your brief to a clean handoff. ### Brief Send the Figma file, brief or ticket. I ask the questions that save a revision later. ### Quote A fixed quote or an estimate within one business day, with what is in and out of scope. ### Build The work happens in your tools: Slack, Jira, Linear or Asana, with progress you can see. ### Handoff QA results, a README and 30 days of fixes after launch. Working terms ## Your client stays your client. ### Confidential by default I work under your NDA and deliver unbranded work when required. Client communication stays with you unless we agree otherwise. Nothing appears in my portfolio without your permission. ### Fits your schedule Toronto-based, with working hours that overlap ET and PT. Replies within one business day. ### Rates on request Hourly, day and project rates, in CAD or USD. Ask and I will send them with a quote. --- Source: https://alexjardinedigital.com/about Author: Alex Jardine About # Alex Jardine Web developer and technical SEO specialist in Toronto, with a background in design and growth. Founder of [Jardine Studio](). Toronto, Canada Remote across Canada and the US In person across the GTA I came up through SEO, content and growth: first on a Shopify store I ran while studying marketing at Sheridan College, then for agency clients and in-house teams. The longer I did that work, the more often the website itself was the constraint. [View Resume ↗]() [Capabilities (PDF)]() [LinkedIn]() [GitHub]() [Email directly]() ## A report can only point at a problem. I wanted to fix it. How I work So I started designing and building the thing I was trying to improve: the pages, the interface, the integrations behind them and the experience a customer actually has. I keep following a problem into the next discipline until I understand how the whole thing works. Growth led to design, design to development, development to the systems underneath. Jardine Studio is where they meet. ExperienceBuilding and improving websites since 2022, with earlier experience in e-commerce and SEO. Hired forWeb development, technical SEO, migrations, integrations and design. Builds inNext.js, React and TypeScript, alongside WordPress, Shopify and Webflow for projects built on those platforms. Takes onFreelance projects, agency partnerships and contract roles, embedded or independent. ![Silktrace domain overview for palisadesbigpine.com: estimated organic traffic over 18 months, known ranking keywords and top pages]() Silktrace, an SEO research product I designed and built solo. [Read the case study]() Experience ## From growth work to building the whole thing. The short version. The [full resume]() has the detail. 1. Nov 2023 Now ### Jardine Studio Founder · Web designer, developer and technical SEO, solo practice Custom websites and web applications for hospitality, B2B, local-service and consumer clients, owned from requirements through launch and support. Run alongside my VonClaro role until November 2025. 2. 2024 2026 ### SharpEnd Web and SEO consultant, contract Palisades Lodge search, content and booking work, and Bodie Foundation’s move to Shopify with zero merchant downtime. 3. Oct 2023 Nov 2025 ### VonClaro Growth Manager, Organic and Web Performance Organic search and website performance across eight agency client accounts. Doubled organic sessions and grew organic leads 43% in six months for an equipment manufacturer, and built Python audit workflows that removed about 25 hours of manual work a month. 4. Mar 2023 Oct 2023 ### Copper CRM SEO Specialist Helped migrate the 200+ page marketing site from Craft CMS to Webflow on the in-house marketing team. 5. May 2022 Mar 2023 ### Independent Digital marketing consultant SEO, landing-page, analytics and website work for small businesses, implementing the fixes rather than only reporting them. 6. 2017 2022 ### Earlier Shopify store owner · Customer support Built and ran a Shopify store while studying marketing at Sheridan College, then supported business customers on B2B software at Reynolds and Reynolds, building SQL and Excel reporting along the way. Selected accounts From agency and in-house roles. These are not Jardine Studio engagements. ![Copper CRM]() **Copper CRM** Y Combinator-backed CRM · Helped migrate Craft CMS to Webflow (in-house) ![BDO Canada]() **BDO Canada** Top-5 global accounting network · Lead SEO and content ![Standard Bots]() **Standard Bots** Industrial robotics · Technical SEO and content strategy, through a previous agency ![Parasoft]() **Parasoft** Enterprise software testing · Strategy, SEO and content ![GrowerIQ]() **GrowerIQ** Cultivation and agriculture SaaS · SEO and content ![Movati Athletic]() **Movati Athletic** Multi-location fitness · Multi-location local SEO ![Fairgrounds Racket Club]() **Fairgrounds Racket Club** Multi-city racket club · Webflow work through a previous agency ## Skills The tools behind the work, in plain text. Front endReact, Next.js, TypeScript, JavaScript, HTML and CSS, Tailwind, Three.js and WebGL, Framer Motion, GSAP CMS and platformsWordPress and Elementor, Shopify, Webflow, Craft CMS, Sanity, Squarespace, MDX SEO and analyticsTechnical SEO, site migrations, schema and JSON-LD, Core Web Vitals, Screaming Frog, Google Business Profile, GA4, Google Tag Manager, Search Console, Google Ads conversion tracking Back end and dataNode.js, Express, Python, FastAPI, PostgreSQL (Neon, Supabase), Drizzle, SQLAlchemy, Redis IntegrationsMews, Resend, DataForSEO, Claude and OpenAI APIs DeliveryFigma, Vitest, Playwright, React Testing Library, pytest, axe-core, GitHub Actions, Vercel, Cloudflare Who I work with best ## Where I can make a difference. 1. ### A website that needs building or improving. New websites, rebuilds and focused improvements to existing sites, from the interface to the integrations behind it. 2. ### A product that needs building. Web applications, SaaS tools and internal systems, including the interface, data and integrations. Silktrace, Lead Finder and AWL are examples. 3. ### Search problems that need hands-on work. Technical audits, migration planning and implementation, with local search, content structure and measurement where needed. 4. ### A team that needs another specialist. Freelance and white-label support for agencies, alongside contract opportunities with in-house teams. I can take ownership of a defined scope and work within your process. There is also a short note on [how this site was built]() --- Source: https://alexjardinedigital.com/writing Author: Alex Jardine Writing # Notes from the work. Design, development, web management and growth, and what connects them. Project lessons, technical experiments and the tools I build along the way. Design · Development · Web management · UX and CX · Growth and search · Experiments · Tools [![Two terminal results drawn in glowing particles. Under a grep of package.json, next 16.4.1 sits settled in violet and cyan. Under npm ls sharp, sharp 0.35.4 glows pink and orange, its last digits drifting apart.]() Development Oct 22, 2026 10 min read ### The patch you applied might not be the one you’re running Next.js has shipped nine security releases this year, and between October 14 and 21 Next.js, React, Node.js and PHP all shipped security fixes. Here are the ways a patch can look applied when it isn’t, and the checklist I now use on every site I maintain. Read the article]() 1. [Oct 15, 2026 Development ### How this site works An 80,000-particle field, text the particles flow around, page transitions printed in halftone, and the fallbacks that keep all of it honest. 8 min]() 2. [Oct 4, 2026 Portfolio ### Welcome to my portfolio Who I am, what I build, where to start, and how this site was made. 4 min]() --- Source: https://alexjardinedigital.com/privacy Author: Alex Jardine # Privacy How this portfolio handles information. Short version: no accounts, no cookies, no advertising trackers. Alex Jardine Updated Oct 16, 2026 ## What this site is alexjardinedigital.com presents Alex Jardine’s work, services and writing. It does not create user accounts, run advertising or set tracking cookies. The only thing it collects that you type is a message you choose to send through the contact form; searches on the writing page stay in your browser. ## Anonymous measurement The site counts visits and a few actions: reaching the contact section, sending the contact form (with its enquiry type only), opening an email link, copying the email address, opening the resume or capability sheet, viewing a project, using a button that jumps to the work or contact section, following a LinkedIn or GitHub link or visiting a client website. If you arrived through a tagged link, actions also carry that link’s campaign source, remembered only for your current browser tab. Your browser also checks how quickly the main content appears, layout stability and responsiveness while you read. Those speed checks stay in your browser and are not sent anywhere. Visits and those actions are counted with Vercel Web Analytics, which uses no cookies. Each record holds the page address with any campaign tags from the link you followed, the referring site, your country, browser, operating system and device type, and for an action a single label such as the project or website opened. Records contain no click text or email address, and Vercel counts unique visitors without cookies or any lasting identifier. ## Saved preferences If you pause the background animation, that choice is saved in your browser’s local storage so the next page respects it. A short-lived session flag also smooths the transition between pages. You can clear both through your browser’s site-data settings. ## Fonts and hosting The typefaces are served from this site, so no font service receives your request. The site is hosted on Vercel, which processes ordinary request information like network addresses and requested URLs to serve pages and keep the service secure. ## Contact form If you send the contact form, your name, email address, enquiry type, any budget or timeline you choose, your message, the page you sent it from and the campaign source of the link you arrived through are emailed to Alex through Resend, an email delivery service. They are used only to reply and discuss your request. Nothing from the form is added to analytics. ## Links and email Links to client websites, Jardine Studio, LinkedIn and GitHub open services with their own privacy practices. The contact links open your own email application; if you send a message, it is used only to reply and discuss your request. ## Questions Email [alex@jardinestudio.com]() with any question about this page or to ask about information you have sent. --- Source: https://alexjardinedigital.com/work/jardine Author: Alex Jardine My own studio · Flagship case # Jardine Studio A production web platform I designed, engineered and operate: the marketing site, a publishing system, free analysis tools, a lead pipeline and paid-search landing pages, secured and tested like a product. Role **Founder, designer, engineer** Status **Live · 982 commits since May 2026** Worked with **Solo** Stack and why **Next.js, React, TypeScript and Redis: one typed codebase; Redis rate-limits the free tools** Live site **[jardinestudio.com ↗]()** [Read case study]() ## The short version What it is Jardine Studio is my web design, development and SEO studio in Toronto. jardinestudio.com is its storefront, its proof of work and its lead engine, built and run entirely by me. What I built A Next.js platform with schema-driven content, an MDX journal, three free tools, emailed PDF reports, consent-aware conversion tracking, paid-search landing pages and an admin lead dashboard, behind a deliberate security model. Why it stands out It is the kind of system agencies build with a team: 982 commits, 845 automated tests and a written security model, designed, engineered and operated by one person. The challenge ## Referral-only work. Nothing to check. Jardine Studio grew through referrals. That works until a prospective client wants to evaluate the studio before a conversation and finds nothing to look at. The site had to be the studio’s storefront, its proof of work and its lead engine at once, and stay secure and maintainable with one person running it. Its content is edited through AWL, a local tool I built for this site. [See the editing tool]() ![Current Jardine Studio desktop homepage in dark mode: a gold ordered-dither field, the headline Websites, systems, and search. Built around your business, and a row of client logos.]() The live homepage in dark mode, captured 4 October 2026. The gold field is generated, not photographed. By the numbers **982** Commits May 8th to October 2nd 2026 **845** Automated tests 710 unit and 135 browser tests **11** API routes Every state-changing route origin-checked; 10 of 11 rate-limited **3** Free tools SEO audit, cost calculator, CMS fit finder Counted from the repository on 4 October 2026. Scope ## Not a brochure. A platform. Four jobs share one codebase, one content model and one design system. Each is ordinary on its own. The work is making them reliable together. Publish ### The public site - Service pages from a shared, validated schema - MDX journal with related reading - Work, proof and local landing pages - Central metadata, canonicals and sitemap rules Analyse ### Free tools - SEO audit: bounded crawl, scored findings, emailed PDF - Website cost calculator with honest scope states - CMS fit finder - Results that carry into a pre-filled brief Grow ### Acquisition - Five paid-search landing pages - Consent-aware GA4 and Google Ads conversions - First-party attribution for every lead - IndexNow submission script Operate ### Behind the scenes - AWL, a schema-driven content editor - Protected lead dashboard - Managed redirects; 410 Gone for retired articles - A written data-retention schedule Security ## Public tools are an attack surface. A free audit fetches any URL a stranger types, renders PDFs, sends email and stores leads. I treated each of those as a risk with a named control, then tested the control. Risk, control and how it is checked | Risk | Control in the code | Checked by | | --- | --- | --- | | A visitor’s URL points the crawler at internal systems | SSRF-safe fetcher: every hostname is resolved and must be a public address, re-checked on each of up to five redirects, under one deadline. | Dedicated SSRF test suite | | Bots run up crawl, PDF and email costs | Redis sliding-window limits per action, such as three SEO crawls an hour. In production, if the limiter is unavailable the route refuses with 503 instead of running unprotected. | Route tests for limits and failure | | Another site submits requests on a visitor’s behalf | Origin and Referer validation on state-changing API routes. | Route tests | | Oversized or malformed request bodies | Bounded readers with per-route limits between 1 and 8 KB, strict UTF-8 decoding and content-type checks. | Unit tests | | Someone else opens a private report | HMAC-signed unlock tokens bound to the audit and the email address, expiring after 24 hours, verified in constant time. | Unlock route tests | | Guessing the admin token | Constant-time comparison of SHA-256 digests, a server session and 10 attempts an hour. | Admin action tests | | Injected scripts or clickjacking | Content Security Policy per surface: app pages cannot be framed; client demos run under a scoped, hash-pinned policy with violation reports sent to a rate-limited endpoint. HSTS with preload and a restrictive Permissions-Policy. | Security tests and a production health suite | From the source and test files. Rules for changing a policy live in a written security operations guide: a named requirement, minimum hosts, a privacy review and updated tests. Decisions ## What I chose, and what it cost. ### Fail closed. Chose If the rate limiter is unavailable in production, expensive routes return 503. Instead of Running unprotected when Redis is down. Trade-off Abuse protection can never silently switch itself off. ### Separate demo policy. Chose Client demos get their own hash-pinned CSP, framed only by the site. Instead of Loosening the app policy so every demo works. Trade-off Each new demo script must be registered and hashed; the app policy never weakens. ### Retire content honestly. Chose Retired articles return 410 Gone with noindex. Instead of Redirecting everything to the homepage. Trade-off A small list to maintain, and search engines get an accurate signal. ### Carry choices, not people. Chose Only validated, finite scope options travel in shareable links. Instead of Putting names, emails and free text in the URL. Trade-off A link restores a scope, not a conversation, and personal data never ends up in forwarded links. Quality ## Tested like a product. Pricing rules, crawl budgets, tokens and security headers are exactly the things that break quietly. They have tests, and production gets its own health checks. **710** unit tests in 121 files Vitest: pricing, scope rules, audit checks, security policy, tokens, rate limits, routes **135** browser tests in 13 specs Playwright: calculator, contact handoffs, funnels, navigation, landing pages - **Accessibility.** axe checks on key routes, the open mobile menu and modals, plus 44px target sizes and keyboard focus through the CMS fit finder. - **Production health.** Asserts the security header baseline, the demo CSP exception, canonical redirects, valid JSON-LD and a cacheable response for Googlebot. - **Written operations.** Security, release QA and media pipeline guides, so changes follow the same checks every time. Search ## Rankings are checked before merge. Most search damage ships in ordinary pull requests: a heading reworded, a canonical dropped, a JSON-LD block lost in a refactor. Page guard builds main and the pull request side by side, loads every URL in the sitemap in Chromium, and compares what search engines and readers rely on. A lost title, description, canonical, robots value, heading, internal link, JSON-LD block, number or sentence fails the check. So does a page that ships over 10% or 20 KB more JavaScript, or gains more than 0.02 layout shift. Added content is reported, never failed, and an intentional copy change is labelled so the guard reports instead of blocking. Every pull request 1. **Build both** main and the pull request, served side by side 2. **Snapshot every page** Each sitemap URL in Chromium 3. **Compare** Metadata, headings, links, JSON-LD, copy, script size, layout shift 4. **Merge or fail** Labelled copy changes report instead of blocking Simplified from the workflow and its script. Search architecture ## Built to be read correctly. The guard protects what the architecture sets up. Each signal search engines use has one owner in the code. Signal, how the site handles it and how it is checked | Signal | How the site handles it | Checked by | | --- | --- | --- | | Rendering | Journal and work pages are generated at build time, and metadata is sent in the first HTML response to every crawler instead of being streamed in later. | Page guard snapshots | | Structured data | One JSON-LD graph from 11 schema components, from Organization and ProfessionalService to Article, FAQ and Breadcrumb, with stable ` @id `s shared across pages. | Page guard and the production health suite | | Metadata | Titles, descriptions and canonicals come from one central function, so no route sets its own by hand. | Page guard | | Sitemap | Dates come from each entry’s content revision, not the build time. Only published, indexable pages with a self-referencing canonical are listed. | Page guard reads the same sitemap | | Redirects and retirement | 53 redirects managed as content. Retired articles return 410 Gone with noindex. | Production health suite | From the source, the page guard workflow and its script. Engineering ## A free audit that survives the internet. The SEO audit crawls up to 25 same-origin pages within a 45-second budget and byte limits, runs PageSpeed on the homepage in parallel, and scores findings against the pages it actually reached. If discovery is cut short, the report says so. The full report unlocks by email with a signed token and renders to PDF on the server. Rendered PDFs are cached for a day under a 4 MB cap, and audit state expires on a schedule. From URL to report 1. **Validate and limit** URL normalised, body bounded, origin and rate checked 2. **Crawl safely** Public addresses only, redirect by redirect 3. **Check and score** Page checks, sitemap, robots, PageSpeed 4. **Report on screen** Findings with coverage stated 5. **Unlock by email** Signed token bound to audit and address, 24 hours 6. **Render the PDF** Server-side, cached and size-capped Simplified from the implementation. Product ## One tool, end to end. The cost calculator shows the product thinking in miniature: it prices what is defined, names what needs discovery and hands the visitor’s choices to the contact form. 1. 2. 3. Price the defined scope ![Calculator result: a planning range of 2,400 to 3,100 Canadian dollars before tax for a new site with 4 to 7 main pages, with Discuss this scope and Copy scope link buttons.]() 1. **A range, not a quote.** Rounded, before tax, labelled in CAD. 2. **One clear next step.** The primary action leads to a conversation, not a checkout. 3. **The variance is stated.** 30% headroom for normal variation, in plain words. Live calculator, illustrative configuration, captured 4 October 2026. Add something uncertain ![Calculator result labelled partly priced: 3,000 to 3,900 Canadian dollars for a 4 to 7 page rebuild, with content and data migration listed under Needs a conversation.]() 1. **The status changes too.** “Partly priced” warns before the number is read. 2. **Unknown work is named.** Migration moves into its own box instead of inflating the range. Same live calculator, rebuild with migration, captured 4 October 2026. Hand over the brief ![Contact form with Website build or rebuild selected and the message pre-filled with the project, pages, features, updates and timeline.]() 1. **Already classified.** The inquiry type is chosen for the visitor. 2. **An editable brief.** Scope and features are written in; the visitor adds context. Live contact page, captured 4 October 2026. Growth ## Measure leads without leaking people. Paid search only works if Google learns from real inquiries. The site reports accepted leads to GA4 and Google Ads from the same confirmed submit that writes its own first-party record, under consent and privacy rules that hold even when something fails. Consent first - Separate analytics and advertising choices, renewed after 180 days - Unavailable storage never counts as permission - Tags are environment-gated; an unconfigured build ships none One confirmed submit - First-party attribution records the journey - GA4 and Google Ads receive the same accepted lead - Smart Bidding gets accepted leads as its signal Privacy by design - Fixed channel labels only; free-text campaign values can hold personal data - Audit state kept 72 hours, raw results 7 days - Audit, lead and metrics records each have a retention period Content ## Edit content. Keep the design. Service pages are structured JSON validated against one shared schema. AWL, the editor I built for the site, generates its fields from that same schema, so the editor and the renderer cannot quietly disagree. Metadata and canonicals follow central rules, the sitemap respects publication status, and an architecture map supplies related services and reading. A new page type still needs schema and design work, by design. [Inside the editor: AWL]() One content contract 1. **Shared schema** Zod definitions for service pages 2. **AWL editor** Fields generated from the schema 3. **Site loader** Validates fields and slugs before rendering 4. **Published page** Shared layout, metadata, sitemap, related links Identity ## Stock skyline to its own signal. The first version leaned on a photographed waterfront. The current one generates its identity: an ordered-dither field in the same print-inspired language as the type. Drag to compare. ![Current homepage with the gold dither field.]() ![Earlier homepage with a photographed city waterfront and the headline You built a real business.]() Earlier Current The earlier design as previously shown in this portfolio, and the current design captured 4 October 2026. ### Owned, not borrowed. A skyline could belong to any Toronto business. The dither field comes from the design itself. ### Ordinary state first. A finished CSS treatment sits underneath. The shader starts after load and an idle moment, and motion or data-saving preferences keep it off. ### Built to recover. It pauses when hidden and restores itself if the browser drops the graphics context. Where it stands ## Live, maintained, still shipping. Shipped - Marketing site, journal and local pages - SEO audit with emailed PDF reports - Cost calculator and CMS fit finder - Five paid-search landing pages - Consent-aware conversion tracking - Admin lead dashboard and AWL editor Engineering 982 commits in five months, 845 automated tests, a written security model, CSP reporting and production health checks. Built with Next.js 15, React 19, TypeScript, Zod, MDX, Upstash Redis, Resend, React PDF, Three.js, Vitest and Playwright, on Vercel. ## Related work [The editor behind this site **AWL** Schema checks, path-scoped Git commits and media maintenance for a custom site.]() [A deeper product build **Silktrace** An SEO research app with private workspaces and request-bound paid operations.]() [All projects ↗]() --- Source: https://alexjardinedigital.com/work/silktrace Author: Alex Jardine My own product · Live SaaS # Silktrace Silktrace is a live SEO research platform. Sign in, connect your own DataForSEO account and investigate any domain’s traffic, keywords, technical health, links and AI search presence in one private workspace. I designed it, built it and run it, from the interface to the threat model. Ownership **My own product** Status **Live · 10+ signed-up users** My role **Founder: product, design, engineering** Worked with **Solo** Stack and why **Next.js, TypeScript and PostgreSQL on Vercel: one typed codebase for app, API and data** Live site **[silktrace.app ↗]()** [Read case study]() ## The short version What it is A multi-tenant web app for SEO research: domain overview, organic keywords, page reports, technical audits, backlinks, dead-link opportunities, competitors, SERPs and AI search evidence, all in one workspace. What I built The product design, a Next.js app with 41 API routes and 24 pages, a PostgreSQL data model, sign-in, encrypted per-workspace provider credentials, durable task and quota accounting, and a procedural WebGL public site. Why it stands out It is built for hostile traffic and real money: a written threat model, an authorization class for every route, restore drills and deletion ledgers. Solo-built software with the operating discipline of a team product. The challenge ## Research in fragments. Context lost. SEO research usually means moving between separate reports: domain visibility in one, keyword demand in another, crawl findings and links somewhere else. Each switch loses the context of the last report. I wanted one investigation workspace where that context carries from the overview into keywords, pages and SERPs. Because it holds other people’s accounts, private data and paid provider calls, it also had to be safe to run in public, not just a demo. ![Silktrace public homepage with a procedural silver silk surface behind the product headline.]() The public site, captured 4 October 2026. The silk surface is procedural WebGL; it responds to motion preferences and pauses when hidden. By the numbers **41** API routes Each assigned an authorization class **1,012** Test assertions 51 unit test files, plus tenancy integration and security suites **13** Database migrations PostgreSQL with Drizzle **61** Engineering documents Threat model, runbooks, release and security contracts Counted from the repository on 4 October 2026. Scope ## A research tool, run as a service. A demo can stop at the interface. Silktrace has accounts, private data, paid third-party calls and an operations layer, because it is built for real accounts, private data and paid provider calls. Research ### Investigate - Domain overview and history - Organic keywords, pages and competitors - Technical audits with affected URLs - Backlinks and dead-link opportunities - SERP and AI search evidence Accounts ### Private by default - Sign-in with one private workspace per account - Bring-your-own DataForSEO key - Credentials encrypted per workspace - Quotas and account deletion Reliability ### Correct under failure - Durable task records - Idempotent request keys - Saved snapshots kept apart from new runs - Unknown spend reconciled, never retried Operations ### Run like a service - Database restore runbook and drill - Credential key rotation - Account suspension and deletion ledger - Sanitised preview environment and release scan Security ## Hostile traffic assumed. The threat model starts from one line: low expected traffic does not mean trusted users. Every boundary that matters has a control in the code and a test or drill behind it. Risk, control and how it is checked | Risk | Control in the code | Checked by | | --- | --- | --- | | One customer reads another customer’s research | The server resolves the workspace from the session; PostgreSQL relationships enforce ownership; the app connects as a restricted database role. | Two-workspace integration test against the real database | | A leaked or reused provider key | One encrypted credential per workspace with a versioned envelope; caches keyed by workspace and credential version; a key re-wrap script for rotation. | Unit tests and an operations script | | Surprise spend on paid research | Signed approval bound to the exact request at $0.05 or more, atomic quotas and idempotent task keys. | Spending, quota and duplicate-request tests | | Sign-in abuse and open redirects | Strict OAuth callback and redirect allowlist; public routes have no private or provider access. | Playwright OAuth negative-case matrix | | A route quietly skips authorization | Every route is classified, from public-static to authenticated-write, with origin checks and idempotency where writes can retry. | Route authorization matrix and edge isolation suite | | Data loss or a bad deploy | Documented restore runbook, restore verification and drill scripts, a deletion ledger and a release security scan. | Runbook drills | From the source, tests and operations documents. In use ## Start with a domain. Leave with a better question. A report earns its place by pointing at the next thing to inspect. Three views from the same regional topic show how the hierarchy keeps evidence one step away from every claim. 1. 2. 3. Read the overview ![Saved Silktrace domain report for palisadesbigpine.com showing evidence state, coverage, an estimated traffic chart, 38 known ranking keywords and a top pages table.]() 1. **Evidence state comes first.** A cached snapshot with $0 live spend. You know what you are reading before you read the chart. 2. **Estimated, and labelled so.** Traffic is clearly labelled as a DataForSEO estimate. 3. **The next question is pre-written.** “What deserves attention” links straight into the keyword and pages reports. 4. **Every row continues.** Each top page opens into its own focused evidence. Saved Palisades report, Silktrace 0.1.48. Check what the data is ![Detail of the evidence header: Source DataForSEO, cache reused, $0 live spend; evidence state cached snapshot; coverage 6 ranked pages and 38 keyword signals; history 17 months.]() 1. **Cost is visible before anything runs.** This read reused a cached result. No new spend. 2. **A snapshot is called a snapshot.** Saved evidence never pretends to be live. 3. **Coverage is stated.** 6 ranked pages and 38 keyword signals: the limits of the sample are on screen. Detail from the same saved Palisades capture. Ask the next question ![Keyword review for where to stay in bishop ca: score 73, review state Map to best page, 90 monthly searches, keyword difficulty 2, CPC 2.96 dollars, commercial intent.]() 1. **A review rank, not a verdict.** The score blends volume, difficulty, intent and whether the site already ranks. 2. **“Map to best page.”** A person decides where it belongs. Demand alone does not justify another page. 3. **Intent in plain sight.** Commercial intent, CPC and difficulty sit beside the decision. A saved keyword investigation in the same topic. **The iteration that shaped it.** An early overview led with written assessment, and people had to read before they could scan. I moved the chart and page table back to the main view and put interpretation in Insights. Evidence within reach first; help reading it second. Decisions ## Browsing a report should not buy another. Every new provider request costs real money. The architecture treats spending as something a person approves, the server accounts for and an interruption can never silently repeat. ### Reading is free. Chose Opening a saved report reads stored evidence. New research is a separate, explicit action. Instead of Refreshing data whenever a report opens. Trade-off Saved views can be older, so each one carries its capture time and freshness state. ### Approve the exact request. Chose Requests estimated at $0.05 or more need a short-lived signed approval bound to the workspace, inputs, estimate and request key. Instead of A blanket “allow spending” setting. Trade-off One more confirmation, but approval can never carry over to a different purchase. ### Never blind-retry money. Chose Quota is reserved and a durable task recorded before execution. Matching requests reuse that task. Instead of Automatically retrying failed provider calls. Trade-off Interrupted work is recorded as unknown spend and waits for a decision instead of paying twice. ### Ownership lives on the server. Chose The session resolves the workspace; queries check ownership; caches are keyed by workspace and credential version. Instead of Trusting a project ID sent by the browser. Trade-off More plumbing in every query, and no path from one workspace’s research to another’s. Architecture ## One workspace. Two different actions. The latest run and the last saved audit are stored separately. If a refresh fails, the interface can say so while the earlier findings stay visible, instead of a failed request looking like vanished data. Coverage also names what the provider did not establish, including Google-selected canonicals and real-user Core Web Vitals. Missing evidence is never quietly turned into zero. Request new research 1. **Bind the approval** To the exact request, at estimates of $0.05 or more 2. **Reserve a task** Quota before execution; matching requests reuse it 3. **Execute and save** Provider evidence plus a receipt Other paths - **Just reading a saved report?** Authorize the workspace and read stored evidence. No provider call, no charge. - **Interrupted after a possible submission?** Record unknown spend, release the concurrency slot, keep the task. No automatic resubmission. Simplified from the implementation. Where it stands ## A research product with decisions behind it. Shipped - Domain overview, organic, pages and keyword reports - Technical audits with affected URLs and freshness - Backlink and dead-link evidence for human review - Google sign-in and private workspaces - Request-bound spending approval and quotas - Procedural WebGL public identity In use and tested More than ten people have signed up and run research in their own workspaces. Tests cover cross-workspace access, altered spending approvals, duplicate requests, quota limits and abandoned-task reconciliation. Built with Next.js and TypeScript, PostgreSQL with Drizzle migrations, DataForSEO, Playwright. Deployed on Vercel behind Cloudflare, with GitHub Actions. ## Related work [Research that remembers **Lead Finder** A prospecting workspace built on stable business identity and change history.]() [The domain in these screenshots **Palisades Lodge** The hospitality engagement whose search growth Silktrace is tracking here.]() [All projects ↗]() --- Source: https://alexjardinedigital.com/work/palisades Author: Alex Jardine Hospitality · Ongoing engagement through SharpEnd # Palisades Lodge Palisades Lodge is a new lodge in Big Pine, California, on the road into the Eastern Sierra. Through SharpEnd, I lead its search and growth work: I write every page and article on its site, set up the Google Business Profile and review routine that took it from no reviews to 42 at 4.7 stars, and engineered the booking step that hands guests to Mews. Client **Palisades Lodge of Big Pine** Engagement **Ongoing, through SharpEnd** My role **SEO, content, local, booking development** Worked with **Solo, through SharpEnd; the lodge supplies its content** Stack and why **Custom booking step before Mews: checks dates in Pacific time so Mews books the right night** Lodge site **[palisadesbigpine.com ↗]()** [Read case study]() ## The short version What it is An ongoing search, content and development engagement for an independent lodge with no brand recognition, competing for travellers who search for the region, not the property. What I built A destination and trip-planning content programme, an established Google Business Profile with a review routine, and a custom booking front end that validates stays in the lodge’s Pacific time before handing over to Mews. Why it stands out Growth and engineering in one engagement, with numbers to show for it: organic clicks up 248% and impressions up 391% in Search Console’s three-month view. The challenge ## A new lodge. No one searching for it. Palisades Lodge is a new, independent property in Big Pine, California. It started with no brand recognition, no Google reviews and a young website. Travellers do not search for a lodge they have never heard of. They search for the region, the road trip and where to stay nearby. The work had to meet them there, then carry them to a direct booking on the lodge’s own site. Where it stands **+248%** Organic clicks 4.51K clicks in the Search Console “Last 3 months” view, from about 1.3K the three months before **+391%** Search impressions 102K impressions in the same view, from about 21K the three months before **42** Google reviews at 4.7 stars From none, as of October 2nd 2026 **12,006** Business Profile views May to October 2026 selector Google Search Console and Google Business Profile. Percentage changes as reported by Google; earlier totals are derived from them. The lodge is new, so part of the rise is a young site gaining its first rankings. Strategy ## Answer the trip. Then explain the stay. Apartments with kitchens suit guests using the Eastern Sierra as a base for hiking, road trips and longer stays. Giving them a reason to choose Big Pine is part of giving them a reason to choose the lodge. Each guide earns the property a relevant place inside the answer, without turning every article into a sales pitch. 1. Discover ### Why Big Pine? Destination and itinerary guides answer the trip before anyone searches for a hotel. - [Big Pine travel guide ↗]() - [Highway 395 road-trip guide ↗]() 2. Judge ### Why this lodge? Stay-planning content and an established Business Profile help a guest assess the property. - [Extended stay near Bishop ↗]() - Google Business Profile, review requests and replies 3. Book ### Which nights? A custom booking step on the lodge’s own site validates the stay, then hands over to Mews. - Dates, party and promo code - Pacific-time validation and late-night handling [Explore the full blog ↗]() Search ## More people find the lodge. I write every website page and blog article. Related guides and contextual links build connected coverage around Big Pine and the Eastern Sierra, so different planning questions converge on useful accommodation information. Month on month, the growth holds: September brought 23% more organic clicks and 44% more impressions than August. Search Console · Web search · All countries and devices | Measure | Aug 1 to 31 | Sep 1 to 30 | Change | | --- | --- | --- | --- | | Organic clicks | 528 | 649 | +23% | | Search impressions | 35,545 | 51,110 | +44% | Typeset from the dated September 2026 export. ![Google Search Console performance summary for palisadesbigpine.com showing total clicks and impressions with percentage increases over the last three months.]() Google Search Console, palisadesbigpine.com. Figures shown in the supplied “Last 3 months” capture; percentage changes are reported by Google. Captured result ## Recommended by name. In one captured search for “best hotel in big pine ca”, Palisades Lodge appears first under Top Hotel Recommendations in Google’s AI Overview. It is a single snapshot: AI Overviews vary by person, place and day. It is the kind of visibility the content programme and local work were built to earn. ![Google search for best hotel in big pine ca with an AI Overview listing Palisades Lodge first under Top Hotel Recommendations in Big Pine.]() In this captured Google search for “best hotel in big pine ca,” Palisades Lodge appears first under “Top Hotel Recommendations in Big Pine” within the AI Overview. Local ## Easier to find. Easier to judge. I established the Google Business Profile with the lodge’s details, categories, imagery and description, then put a review request and reply routine around the guest stay. The listing went from no reviews to 42 at 4.7 stars by October 2nd 2026. Destination content helps people discover the area; the profile helps them assess the property. ![Google Business Profile performance panel for May to October 2026: 1,433 interactions, 12,006 profile views and 4,350 search appearances.]() 1. **1,433 interactions.** Business Profile interactions, as Google reports them. 2. **12,006 profile views.** People who opened the listing. 3. **4,350 search appearances.** Searches that showed the profile. Business Profile performance, May to October 2026, captured October 2nd 2026. Engineering ## A date picker has to understand the property. Guests make their first booking decision on the lodge’s own site, then continue into Mews with their choices attached. The interface validates and prepares; Mews owns the reservation. The useful engineering is in that handoff. 1. 2. Choose the stay ![Palisades Lodge homepage with the booking panel open: October 12 to 14, 2026 selected, 2 adults, 0 children, an optional promotional code field and a Next button.]() 1. **Dates first.** October 12 to 14 selected in the property’s calendar. 2. **Party and promotion code travel too.** Adults, children and an optional code join the request. 3. **Then Mews.** “Next” hands a prepared request to the booking engine. Live lodge homepage, captured 4 October 2026. Use the property’s day ![Two-month calendar for October and November 2026 opened from the dates field, with the field showing a visible focus ring and October 4 marked as today.]() 1. **Visible focus.** Keyboard users can open the picker, move through dates and return to the form. 2. **Today means today at the lodge.** Valid dates follow the Pacific calendar, whatever the visitor’s clock says. 3. **Seven days, seven columns.** A QA pass caught host CSS squeezing a week into six columns. Checks now compare date positions with weekdays across 12 months and nine viewport widths. Live desktop calendar, captured 4 October 2026. Edge cases ## The awkward hour after midnight. Just after midnight, a guest may still be trying to book the night that has technically ended. The integration notes record that Mews can shift a past-day arrival forward, which would book the wrong night. Before 6 AM, the interface offers a call to the lodge for that case, with a way back to choose different dates. Cleanup and duplicate-initialisation handling also keep the widget from stacking listeners when the host page replaces its markup. Implemented booking rules 1. **Guest selects a stay** Check-in, check-out, adults, children, optional code 2. **Validate in Pacific time** The property’s day, not the visitor’s timezone 3. **Prepare the request for Mews** Dates, party and code continue into booking Edge cases - **Previous property day, before 6 AM** Offer a call to the lodge instead of shifting the intended night. - **Incomplete, reversed or expired dates** Explain the problem and return focus to the form’s feedback. Source-derived schematic. Live implementation checked 4 October 2026. Mews owns the reservation step. On a phone ## The same journey, on a phone. Guides, the booking step and the handoff to Mews all work at phone width, where much trip planning happens. ![The Palisades Lodge booking step on a phone: dates, adults, children, an optional promotional code and a Next button.]() Live site at 390 px, captured 16 October 2026. Where it stands ## Content, local and booking work as one journey. Delivered - All website and blog writing - Destination, itinerary and stay-planning guides - Google Business Profile and review routine - Custom booking front end with Mews handoff - Full keyboard journey since September 2026 - Weekday-alignment regression checks Measured September versus August: 649 organic clicks (+23%) and 51,110 impressions (+44%). 42 Google reviews at 4.7 stars, from none. ## Related work [The research tool **Silktrace** The SEO product I built, shown here investigating this same domain.]() [Another small hospitality business **The Black Salt Room** An atmospheric studio site built to answer practical questions before an inquiry.]() [All projects ↗]() --- Source: https://alexjardinedigital.com/work/lambert Author: Alex Jardine Client website · Architectural lighting # Lambert Lighting Lambert makes architectural lighting in Rhode Island. I designed and built the complete catalogue site, including a custom page generator, specification comparisons, contextual product-file requests and a WebGL lighting system. Client **Lambert Architectural Lighting** Status **Client project** My role **Design, front-end and back-end development, catalogue architecture, logo refinement and launch** Worked with **Solo; Lambert supplied its product content** Stack and why **Custom static generator, no framework: fast, cheap to host, little to attack** Live site **[lambertlighting.com ↗]()** [Read case study]() ## The short version What it is The website and online catalogue for an architectural lighting manufacturer, used by architects, lighting designers and specifiers to choose fixtures and request IES, LDT, Revit and cut-sheet files. What I built The visual design and a refined logo, the catalogue architecture, a custom static site generator, a WebGL lighting field, comparable specification tables and the pre-filled request workflow. Why it stands out It treats a B2B catalogue as a working tool: application-first navigation, specs side by side, and a lean static build that is fast, checkable and hard to attack. The challenge ## A 46 MB PDF. Doing a website’s job. Much of Lambert’s product information lived inside a 46 MB catalogue PDF. Architects, lighting designers and specifiers had to dig through it to compare fixtures, check configurations and find the right IES, LDT, Revit and cut-sheet files. The brief was to replace that obsolete catalogue with an application-led system: pages a buyer can read, compare, share and return to while planning a project, taken from strategy through launch in three to four weeks. Screen recording of the Lambert homepage: warm light beams drift slowly through a dark field behind the headline Architectural lighting. Ready to specify. The opening, recorded from the live site: the light field drifts behind restrained navigation and a direct route into the product range. **5** Application families **7** Product configurations **14** Legacy terms still mapped **13** Production pages Delivered scope of the rebuilt site. The journey ## From a space to a specification. The main navigation starts with grazer, cove, downlight, suspended linear and wall wash. Each family explains where the light goes before asking anyone to compare product codes. 1. 2. 3. Start with the application ![Cove family page: indirect light from a concealed channel, a short description and an illuminated lighting stage.]() 1. **Purpose first.** Indirect light from a concealed channel. 2. **The light explains the family.** Each family gets its own lighting stage. Desktop detail of the Cove lighting stage. Compare in one table ![Grazer specification table comparing LR-40, LR-24 and LR-60W by beam, colour temperature, CRI, output, load and IP rating, with IES, LDT, Revit and cut sheet buttons on each row.]() 1. **One family, one table.** Beam, CCT, CRI, output, load and IP for every configuration. 2. **Files beside the row.** IES, LDT, Revit and cut sheet belong to the configuration they describe. 3. **Conditions stated.** Delivered output at 25°C ambient. Product values shown belong to the supplied catalogue data. Request without retyping ![Request form message pre-filled with Please send the IES file for LR-40.]() 1. **Pre-filled.** Choosing IES for LR-40 opens the request with both already in the message. The delivered request workflow. Vocabulary ## New structure. Old words still work. Returning buyers know the old catalogue names. Rather than break that habit, a crosswalk sends every familiar term to the right application family. Familiar term Application family - LED Tape **Cove** - Cove Lighting **Cove** - Linear Extrusions **Suspended linear** - Grazers **Grazer** Examples from the 14-term category crosswalk. The application families are the main route; the old words remain a way in. Build ## One model behind the catalogue. Families, configurations, project content and legacy mappings live in one versioned content file. A custom generator turns them into static pages, specification tables, contextual request links and product metadata, so a new configuration uses the existing structure instead of another hand-built page. Build checks catch missing families and product codes, wrong heading counts, and images without dimensions or alternative text. Gallery code loads only on pages that use a gallery. One catalogue model 1. **Versioned content** Families, configurations, crosswalk, projects 2. **Static generator** Builds every page from the model 3. **Family pages and tables** Comparable specifications 4. **Request links and schema** Contextual file requests, canonical URLs, product metadata Fails the build - **Missing data** Absent families or product codes, wrong heading counts, images without dimensions or alt text. Engineering ## Fast, static and locked down. A catalogue does not need a heavy framework. Every page is pre-built HTML, the only scripts are first-party bundles, and the browser is told exactly what it may load. Speed ### Lean delivery - Static HTML generated at build time - Every image in AVIF and WebP at four sizes - Blurred placeholders while images load - Gallery code only where a gallery exists Craft ### Light as interface - WebGL light field on the lightweight OGL library - Distinct lighting stage per family - Settled states for reduced motion and data Security ### Locked-down headers - Content Security Policy: scripts from the site only - No framing, no plugins, no base-URL tricks - HSTS with preload - Canonical host enforced by permanent redirect Decisions ## What the design chose. ### Applications before products. Chose Five families, each explaining where the light goes. Instead of A product-code catalogue mirroring the old PDF. Trade-off Returning buyers lose their familiar entry point, which is why the crosswalk exists. ### A generator, not a CMS. Chose A small static generator over one content file. Instead of A client-facing CMS. Trade-off Every update goes through the build, so every page stays fast, checked and consistent. ### Explain, do not simulate. Chose Bespoke lighting stages per family, settled for reduced motion and data. Instead of Photometric-looking renders. Trade-off The stages are explanatory; specifications stay readable without the effect. On a phone ## Specs that fit a phone. Specification tables and file requests stay usable at phone width, for specifiers checking a fixture away from their desk. ![The LR Grazer specification section on a phone: an introduction to the file requests and a sideways-scrolling table of beam, colour temperature and CRI.]() Live site at 390 px, captured 16 October 2026. Where it stands ## A usable catalogue, with a clear handoff. Delivered - Application-first navigation across five families - Comparable specification tables - Pre-filled IES, LDT, Revit and cut-sheet requests - Legacy category crosswalk - Static generator with build checks - Logo refined for digital use In use The request flow is live: specifiers send IES, LDT, Revit and cut-sheet requests through it, with the product and file already filled in. Why it holds up The next product addition is a content entry, not another design exercise. ## Related work [Another client build **The Black Salt Room** Atmosphere and practical detail for a small Toronto studio.]() [Content systems **AWL** A schema-driven editor for a custom website.]() [All projects ↗]() --- Source: https://alexjardinedigital.com/work/awl Author: Alex Jardine My own tool · Website control room # AWL AWL is the private control room for jardinestudio.com. It manages every page, service, FAQ, redirect, price tier and journal article on the site, and commits each change to Git, so a custom-coded website is as easy to run as a CMS without becoming one. Ownership **My own tool, for the studio website** Status **Working tool · local only** My role **Product design and engineering** Built with **Next.js, TypeScript, Zod, Git** [Read case study]() ## The short version What it is A Next.js app that runs only on my machine and edits the Jardine Studio repository directly: 11 structured content files, MDX journal articles and their images. What I built Schema-synced editors for pages, services, FAQs, SEO, redirects, calculator pricing, the ticker and site architecture, plus atomic writes with read-back, path-scoped commits, a media inventory and maintenance tools. Why it stands out It gives a hand-built site the editing comfort of a CMS with no second content model, no hosted attack surface and no risk of overwriting work in progress. By the numbers **11** Content files managed Plus MDX journal articles and their media **56** Automated tests 14 test files: Git safety, writes, media, schemas **28K** Lines of TypeScript Next.js app and editing libraries **0** Public endpoints Binds to 127.0.0.1 only Counted from the repository on 4 October 2026. Scope ## One editor for the whole site. Not just blog posts. AWL edits the content, search settings and even the pricing behind the public calculator, all against the same schemas the website validates. Content ### Pages and copy - Page sections and calls to action - Service pages - FAQs and ticker - Work entries - MDX journal articles Search ### Discoverability - SEO titles with length targets - Redirects - Site architecture map and gaps - Tag metadata Business ### Live tools - Calculator configuration - Pricing tiers - Widget settings Upkeep ### Maintenance - Image usage and alt text - Reviewed year updates - Schema sync checks - One commit per change In use ## Make the edit small. Make the result inspectable. Change one word in a page title, save, and get back a commit you can review. Then watch the editor refuse a save that would overwrite somebody’s draft. 1. 2. 3. Edit a field ![AWL page manager for the sample Home page: a success notice reading Saved. Commit cef576f created in the website repo, label and path fields, and the hero title field with a character counter.]() 1. **Saved, with a hash.** Commit cef576f created in the website repo. 2. **Fields that match the page.** The hero title, with a 45 to 75 character target. 3. **Shaped around this site.** Pages, service pages, journal, media and maintenance. Sample workspace, captured 9 September 2026. The edit and commit are real. Review the change commit cef576f content/pages.json **+1** **−1** ``` Home page · hero title - Thoughtful websites for places, people and ideas. + Considered websites for places, people and ideas. ``` Typeset from the original Git diff of the sample save, 9 September 2026. One title edit, one content file, nothing else in the commit. Refuse a risky save ![AWL refusing a save with the message: Awl will not overwrite uncommitted work in the website repo. Commit, stash, or discard changes for content/pages.json.]() 1. **Work in progress is protected.** content/pages.json already held an uncommitted draft, so the write was refused. 2. **Nothing changed on disk.** The draft’s bytes and the repository commit stayed exactly as they were. A real refused save in the sample repository. Under the hood ## An editor that follows the website. AWL uses the website’s own page-content schema, with a synchronisation check before every save. If the site’s schema changes after AWL copied it, the editor refuses to write until they agree again. Text saves go to a temporary file beside the destination, are renamed into place and read back. Transient Windows and OneDrive locks get bounded retries; a mismatch is reported as an error. The commit includes only the intended content path, so unrelated staged and unstaged work stays out of it. Implemented save path 1. **Edit structured content** Fields follow the website’s schema 2. **Check before writing** Schema sync, target file state, input validation 3. **Write and verify text** Temporary file, rename, read back 4. **Commit the intended path** Return a hash; leave other work alone Deliberately outside - **Publishing** Pushing and deployment stay a separate, manual step. A save is not a publish button. Simplified from the implementation. Maintenance ## Know where every image is used. A media folder does not tell you what a file supports. AWL scans images and the references to them in content and components, then connects each asset to its usage and alternative text. Alt text is edited at the reference where it belongs. Before changing it, AWL re-reads the source and checks the old value still matches; if another edit intervened, it asks for a rescan. Dynamic values stay in code. ![AWL media reference panel: one reference in content/journal/planning-a-website.mdx line 16, a Markdown image with existing alt text, and Save and Reset buttons.]() 1. **Exactly where it is used.** planning-a-website.mdx, line 16. 2. **Edit at the reference.** Alt text changes in the source that uses the image. 3. **Status at a glance.** References with and without alt text are marked. A real alt-text edit in the sample workspace. The saved commit changed that single journal file. Site view ## See the whole site at once. An architecture view maps page relationships and flags content gaps, so editing decisions can be made with the structure in view. It is tailored to this site’s own files, schemas and content mapping, which is exactly what lets the interface reflect how the site actually works. ![AWL architecture view mapping the sample site’s pages and flagging content gaps.]() Sample capture, 9 September 2026. Where it stands ## Editorial control, source ownership intact. Shipped - Page, service, journal, SEO and widget editors - Schema synchronisation checks - Atomic, read-back text writes - Path-scoped local commits - Media usage inventory and alt-text editing - Reviewed year maintenance for journal content By design Runs privately on the owner’s machine and works directly in the website repository, so publishing stays a deliberate, reviewed step. Proof shown Real edits, real commits and a real refused save, demonstrated on a sample workspace. ## Related work [The site AWL edits **Jardine Studio** The studio platform whose schemas AWL shares.]() [Another content model **Lambert Lighting** A catalogue generated from one versioned content file.]() [All projects ↗]() --- Source: https://alexjardinedigital.com/work/lead Author: Alex Jardine Internal product · Version 2.0 beta # Lead Finder Lead Finder is a prospecting operating system for small SEO and web agencies. It finds local businesses that need help, proves why with real website and search evidence, and runs the pipeline in a workspace that remembers every business across research runs. Ownership **Internal product, Jardine Studio** Status **Working beta · 2.0 development** My role **Product design and engineering** Built with **Python, FastAPI, React, TypeScript** [Read case study]() ## The short version What it is An agency picks a niche and a territory. Lead Finder discovers businesses with no real website or weak search visibility, audits their sites, qualifies them with AI against the evidence and hands the good ones to a pipeline. What I built A FastAPI and React product with 171 API endpoints, 19 database migrations, two discovery engines, a bounded first-party crawler, AI qualification with Anthropic’s Claude, workspace tenancy, a usage ledger and Stripe billing scaffolding. Why it stands out It solves the hard part of prospecting: staying true over time. Stable business identity, change detection that ignores noise, and evidence behind every recommendation. By the numbers **171** API endpoints Async FastAPI service **2,190+** Automated tests 223 test files across backend and frontend **19** Database migrations SQLAlchemy models with Alembic **172K** Lines of code Python and TypeScript, including tests Counted from tracked repository files on 4 October 2026. Test count is test functions in source. Scope ## Find. Work. Replenish. The product contract is three jobs. It deliberately stops short of email sending, dialers and client reporting, so it can do research and decisions properly. Find ### Discovery - No-website and social-only finder - Search visibility engine on DataForSEO - Saved Markets by niche and territory - AI-suggested niches kept local to a campaign Prove ### Evidence - Bounded first-party site crawl - Critical, warning and improvement findings by page - PageSpeed as supporting context - AI qualification grounded in the evidence Work ### Pipeline - Save, flag or skip at the point of review - Activities, next actions and outcomes - Today queue of what needs attention - Exports and handoff Replenish ### Memory - Stable identity across runs - New versus changed businesses - Health and shortfall signals per market - Usage ledger and caps In use ## Decide next to the evidence. The market list stays in view while a business opens over it. Inspect the website or search evidence, open any finding down to the page it came from, then save, flag or skip without losing your place. 1. 2. 3. Review in context ![Lead Finder market view with a business detail panel open on the Website tab: a technical audit of all 4 discovered pages with 0 critical, 6 warning and 4 improvement findings, and Save to CRM, Flag and Skip buttons.]() 1. **The market stays put.** The opportunity list remains visible behind the panel. 2. **“Changed”, not “new”.** A returning business says what happened since last time. 3. **Counts before claims.** Findings by severity across all 4 discovered pages. 4. **Decide in place.** Save to CRM, Flag or Skip at the point of review. Saved beta capture, dev.190. Open the finding ![Finding detail: heading hierarchy skips levels, page structure, 3 affected pages, with exact heading sequences such as H5, H3, H5 retained for each affected URL.]() 1. **A finding names its scope.** Page structure, three affected pages. 2. **Observation and advice are separate.** What was found, then what to check. 3. **The exact facts.** The heading sequence retained from each crawled page. Saved beta capture, dev.192. Return to the market ![Lead history: Electrico HG seen again and marked Changed, listing changed website domain, contactability and qualification; Merlo Electric marked as a first observation.]() 1. **Seen again, and changed.** The changed fields are named, not just flagged. 2. **First observation.** New businesses never blur into returning ones. Saved beta capture, dev.190. Refresh logic ## A refresh should tell you something. Refreshing a market should answer one practical question: what deserves another look? The comparison is deliberately selective. Flags a change - A new website domain - A changed contact route - Qualification moved - Qualification reasons changed Named in the history so the reviewer sees why it resurfaced. Ignored as noise - Search position 12 to 18, inside the same band - The same data in a different order - Different model wording, same meaning Ordinary jitter does not earn another look. Marked unknown - Missing from a Search Visibility refresh Absence is not evidence that the opportunity is gone, so incomplete research cannot rewrite what the workspace knows. Data model ## One business. Many observations. A business, an observation and an opportunity are separate records. New research adds evidence to an existing prospect while the earlier research, review decisions and pipeline activity stay attached. Matching is deliberately conservative. A provider identifier or corroborated details can establish a match. A shared domain alone cannot. Conflicting or weak signals create a provisional identity for a person to review instead of silently merging two businesses. Identity rules 1. **Observation A** First research run 2. **Observation B** Later research run 3. **Identity check** Provider ID, or corroborated name, domain, phone and location 4. **One prospect** Retained observations, review decisions, pipeline activity Edge cases - **Conflicting or weak match** Stays provisional for human review. - **Shared domain only** Never enough to merge two businesses. Simplified from the implemented identity rules. Trade-offs ## Careful by default. ### Bounded crawls, labelled samples. Chose Up to 25 pages. Small sites show every eligible page; larger sites show a labelled sample. Instead of Presenting a partial crawl as a full assessment. Trade-off Fast, predictable crawls, and reports that say exactly which pages were checked. ### Positions compared in bands. Chose Search rank changes count only when they cross a band. Instead of Alerting on every movement. Trade-off The history highlights meaningful movement instead of daily jitter. ### A pipeline, not a mailer. Chose Notes, activities, next actions and recorded outcomes. Instead of Building email delivery and sequencing in. Trade-off Outreach happens elsewhere, and the tool stays focused on research and decisions. Where it stands ## Continuity is the result. A reviewer can return to a market and tell a new observation from a changed business, with the history still attached. Shipped in the beta - Saved Markets with two discovery paths - Website and search evidence with page-level findings - Save, flag and skip review decisions - Change history across refreshes - Integrated pipeline for follow-up Built with React and TypeScript on an asynchronous FastAPI service, SQLAlchemy and Alembic, durable run records, DataForSEO, Anthropic’s Claude, Stripe billing scaffolding and Supabase auth in preview. ## Related work [The other research product **Silktrace** Domain and keyword investigation with request-bound paid research.]() [Another research tool **Domain Looker** Honest RDAP checks for messy domain shortlists.]() [All projects ↗]() --- Source: https://alexjardinedigital.com/work/salt Author: Alex Jardine Client website · Wellness studio # The Black Salt Room The Black Salt Room is a breathwork and yoga studio in a Toronto home, with six spots per session. I designed and built the complete Next.js site, from its visual direction and structured content to a validated inquiry API and technical SEO. Client **The Black Salt Room** Status **Client project** My role **Creative direction, design, front-end and back-end development, content, technical SEO and launch** Worked with **Solo; the owner supplied content and photos** Stack and why **Next.js and TypeScript: fast pages plus a validated inquiry API** Live site **[blacksaltroom.com ↗]()** [Read case study]() ## The short version What it is The first website for a small studio offering breathwork, yin and restorative yoga, meditation and seasonal circles, all arranged personally by inquiry rather than an online checkout. What I built Creative direction and a Next.js site with five offering pages, reviewed articles with named authors and sources, an inquiry flow that remembers the chosen practice, structured data and a performance-first video hero. Why it stands out A small business with a serious build: a launch build that scored 100 on Lighthouse, a validated inquiry API and content written to earn trust, not just traffic. The challenge ## A personal practice. No website yet. The Black Salt Room had no website. Sessions run in a Toronto home with six spots each, and every booking is arranged personally by inquiry rather than an online checkout. The site had to carry the atmosphere of the room, explain unfamiliar practices like breathwork and yin yoga in plain terms, and turn interest into an inquiry for the right session. > Since the launch, I’ve been staying fully booked. We receive a constant stream of new inquiries and reservations directly through the site every single day. > > Alina, Owner, The Black Salt Room **100** Lighthouse scores Launch build **1.5 s** Mobile LCP Launch build **0** Layout shift Launch build **5** Offering pages Each with session details and a direct inquiry Lighthouse lab results from the launch build. The journey ## Feel the room. Understand the session. Charcoal, amber light, serif headings and restrained movement set the mood. Practical answers sit right beside it, so the atmosphere never replaces information. 1. 2. 3. Arrive ![The Black Salt Room homepage: A Quiet Place to Land, with group size six people maximum, small group and private formats, and arranged personally by inquiry.]() 1. **Six people maximum.** Group size answered before anyone asks. 2. **Small group or private.** Both formats named up front. 3. **By inquiry only.** No calendar or checkout, by design. Picture a session ![Breathwork offering page with an Inquire about Breathwork button and a details grid: length, structure, format, group size, props and arrival, followed by related guides.]() 1. **Inquire about this practice.** The button carries Breathwork into the form. 2. **What actually happens.** Length, structure, format, group size, props and arrival time. 3. **Related guides.** Preparation and comparison articles one click away. Ask personally ![Inquiry form with Breathwork evening pre-selected as the area of interest, a message field, and a location panel explaining the address is shared once a session is confirmed.]() 1. **Already selected.** Arriving from Breathwork pre-selects the area of interest. 2. **Privacy and access, upfront.** The address is shared once confirmed; access needs are invited. 3. **A person replies.** The owner replies personally, within a few days. Desktop views captured 4 October 2026, including content added after launch. Content ## Service pages and articles do different jobs. Offering pages explain the services. Articles answer preparation and comparison questions, such as how to choose a breathwork class or what happens at a first session, and link back to the relevant practice. I built the article model around a short answer, named writer and studio-reviewer roles, sources and real update dates. The writing favours session details, participant choice and clear boundaries over sweeping wellness claims. ![Article How to Choose a Breathwork Class in Toronto with publish and update dates, written-by and reviewed-by credits, a short answer box and related practice links.]() 1. **Real dates.** Published and updated dates come from the content record. 2. **Named roles.** Writer and studio reviewer, both credited. 3. **The short answer first.** The useful part before the long read. 4. **Back to the practice.** Related practice links close the loop. Live site, captured 16 October 2026. Decisions ## Quiet on the surface. Careful underneath. ### Inquiry, not checkout. Chose Pricing, availability and suitability are discussed before a session is confirmed. Instead of A public calendar and payment flow. Trade-off More owner effort per booking, which matches how a six-person home studio actually runs. ### Still image first. Chose The hero starts as a still; a small video loop fades in only on eligible desktop screens when it can play. Instead of Autoplaying video for everyone. Trade-off Phones and data-saving visitors see a still, and nobody downloads video they will not watch. ### Keep old links alive. Chose A permanent redirect from the former Dundas article URL to its Toronto replacement, with stored sitemap dates. Instead of Letting the old path quietly 404. Trade-off A small redirect map to maintain, and truthful freshness signals. On a phone ## Calm on a small screen. On phones the hero starts as a still image and the inquiry path stays one tap away. ![The Black Salt Room homepage on a phone: A Quiet Place to Land, small group breathwork and yoga by inquiry only, with Explore offerings and About the studio buttons.]() Live site at 390 px, captured 16 October 2026. Where it stands ## A useful front door for a small practice. Delivered - Atmospheric identity and homepage - A page per practice with session details - Inquiry form that carries the chosen practice - Reviewed articles with sources and dates - Typed offering and article records - Inquiry API with shared browser and server validation - HSTS, framing and permissions headers Launch build Lighthouse scores of 100, a 1.5 s mobile LCP and zero layout shift at launch. Result The owner reports the studio has stayed fully booked since launch, with new inquiries arriving through the site every day. ## Related work [Hospitality growth **Palisades Lodge** Content, local presence and a booking handoff for a new lodge.]() [Another small business **B&B Contractors** Three pages and a clear route to an estimate.]() [All projects ↗]() --- Source: https://alexjardinedigital.com/work/bb Author: Alex Jardine Client website · Trades # B&B Contractors B&B Specialty Contractors handles six trades across the Greater Toronto Area. I designed, wrote and built the complete Next.js site, including its technical SEO and a validated, rate-limited estimate backend. The live site uses email enquiries at the client’s request. Client **B&B Specialty Contractors** Status **Client project · Live** My role **Design, front-end and back-end development, service copy, technical SEO and launch** Worked with **Solo** Stack and why **Next.js and TypeScript: six services from one typed list keep every page consistent** Live site **[bbspecialtycontractors.com ↗]()** [Read case study]() ## The short version What it is The website for a GTA contractor covering drywall, paint, flooring, trim, windows, doors and exteriors, with one clear route to a free estimate. What I built Page design, service copy and a Next.js build: one typed service list feeding every page, page-level metadata and structured data, an email-first estimate route, and a ready-to-enable estimate form with validation and Redis rate limiting. Why it stands out Proportion: the right amount of website for a small business, delivered cleanly, with results the owner can count in jobs. The challenge ## A business card, not a brochure. B&B Specialty Contractors takes on drywall, paint, flooring, trim, windows, doors and exteriors across the Greater Toronto Area. The owner wanted a simple, clean site to share during meetings: a digital business card, not a sprawling brochure. The site had to say what B&B does, show where it works and make a free estimate the obvious next step, without more pages than the business needed. **17** Jobs from the website Reported by the owner, October 2nd 2026 **6** Service groups **3** Pages Results reported by the owner. Scope from the delivered build. The journey ## Explain the work. Then ask for the inquiry. Service area and the estimate action appear first, so visitors can establish fit before reading further. Each service then answers the question a homeowner actually has: do you do my kind of job? 1. 2. 3. Establish fit ![B&B Specialty Contractors homepage with the service area and a free estimate action at the top.]() Recognise the job ![Windows and Doors service section: installation only, a list of included work and typical projects such as window replacements and new entry doors.]() 1. **Installation only.** The scope limit sits where it matters. 2. **What is included.** Four concrete inclusions, not adjectives. 3. **Typical projects.** Visitors recognise their own job. Ask for an estimate ![Free estimate page with email, a 24-hour response and the Greater Toronto service area, then three steps: send the basics, schedule a look, get a written quote.]() 1. **Email, as the owner prefers.** The primary action matches how the business works. 2. **Service area.** Greater Toronto Area, stated before anyone writes. 3. **What happens next.** Description, site visit or video walkthrough, then a written quote. Build ## One service list. Two levels of detail. The homepage cards and the detailed service rows draw from the same typed list. Each card links straight to its matching section, so updating a service never means reconciling two copies. Every page has its own title, description and canonical URL, plus business and breadcrumb structured data. Unconfirmed details such as opening hours and review ratings are left out rather than guessed. Content model 1. **Six service records** Title, image, descriptions, scope limit, inclusions, typical projects 2. **Homepage overview** Selected services: title, image, short copy 3. **Services page** All six with full detail Simplified from the implementation. Decisions ## Built for the way the owner works. ### Email first. Chose The public estimate route is email, as the client preferred. Instead of Imposing a form the owner did not want to manage. Trade-off Less structured intake, but a contact route the business is comfortable using. ### Ready to scale. Chose A complete estimate form with validation, rate limiting and notifications, ready to switch on. Instead of Forcing a form on the business from day one. Trade-off The owner keeps the email workflow they prefer, with the upgrade already built. On a phone ## One tap to an estimate. On a phone, the service area, the six trades and the free-estimate button all sit on the first screen. ![The B&B Specialty Contractors homepage on a phone: Greater Toronto Area, Fast Response and Quality Service, the six trades, and Get a free estimate and Our services buttons.]() Live site at 390 px, captured 17 October 2026. Where it stands ## A useful business site, kept proportionate. Delivered - Home, Services and Free Estimate pages - Six services from one typed list - Email-first estimate route with clear next steps - Page metadata and structured data - Deployed to the live domain Result 17 jobs generated by the website in total, latest figure confirmed October 2nd 2026. > I just needed a simple, clean site, a basic digital business card to share during meetings. Alex gave me exactly what I asked for, and the whole process was completely effortless. > > Eren B, Owner, B&B Specialty Contractors ## Related work [Another small business **The Black Salt Room** An atmospheric site that answers practical questions before an inquiry.]() [A larger catalogue **Lambert Lighting** Application-first navigation for architectural lighting.]() [All projects ↗]() --- Source: https://alexjardinedigital.com/work/gpu Author: Alex Jardine Open source · Windows diagnostics # GPU Flight Recorder GPU Flight Recorder is an open-source Windows tool that records what your PC was doing when the display froze. I built it when my own screen kept locking up for 10 to 25 seconds during Fusion and Blender sessions, and nothing available could tell me why. Ownership **My own tool · MIT licence** Status **Public beta** My role **Product design and engineering** Source **[github.com/Krmzyn/gpu-flight-recorder ↗]()** [Read case study]() ## The short version What it is A local desktop app that continuously records GPU telemetry, Windows events, power transitions, crash-dump filenames and display state, then turns a freeze into an inspectable report you can hand to support. What I built Independent collector threads, Win32 calls through ctypes, PowerShell and nvidia-smi probes, durable JSONL logging with crash recovery, a Tkinter control panel, freeze classification and support bundles, with CI on GitHub. Why it stands out Systems engineering around failure: the recorder keeps working while the thing it watches is broken, and it never claims more than the evidence shows. By the numbers **0** Third-party dependencies Python standard library and Windows APIs **4.5K** Lines of Python Recorder, collectors, reports and GUI **26** Automated tests unittest, run in GitHub Actions **MIT** Open source Public repository on GitHub Counted from the repository on 4 October 2026. ![GPU Flight Recorder window during a normal recording: heartbeat age 0 s, GPU probe age 0 s, nvidia-smi OK, 0 GPU failures, temperature, power, VRAM, display and observation marker buttons.]() 1. **Heartbeat age.** How old the proof-of-life signal is. 2. **Probe age.** A stale GPU reading looks stale, never current. 3. **Failures counted separately.** Probe failures are not mixed into load or temperature. 4. **One-click markers.** “Display froze”, “audio continued”, “display recovered”: context the collectors cannot know. Actual Windows recording, 8 September 2026, during normal operation. Architecture ## One probe waits. The heartbeat continues. The heartbeat and the NVIDIA probe run on separate worker threads, and Windows events, display information, keyboard state and selected processes have their own collectors. A slow GPU query produces a timeout; it does not silence everything else. GPU probe **Blocked call · 1,094 ms** Heartbeat 0 ms 1,094 ms **6** heartbeat records written while the probe waited. Measured with the original worker code, 4 October 2026. Durability ## Write while the evidence still exists. Each observation is appended to a timestamped JSONL stream, and the default writer flushes and syncs every record to disk. That costs extra disk work, and leaves far less sitting in buffers when a session ends badly. On the next launch, sessions still marked as running are recovered and reported. A line-by-line reader keeps valid records around a malformed one and preserves the damaged fragment as a parse-error record. A fixture with two complete records and a truncated third confirmed exactly that. Recording and recovery 1. **Append** One JSON line per observation 2. **Flush and sync** Each record reaches the disk 3. **Recover** Sessions still marked running are reported next launch 4. **Tolerate damage** Valid lines kept; the broken fragment preserved as evidence Report ## A finding, with its evidence attached. The report combines markers, heartbeat activity and probe failures, separates startup blips from significant failures, and labels its conclusion as likely. Every conclusion links back to the evidence that produced it. final\_report.md ``` ## Freeze Classification - Analysis window uses display-freeze marker: 2026-10-04T12:01:27-04:00 to 2026-10-04T12:06:42-04:00. - Heartbeat evidence present: logger/Windows proof-of-life records continued in the analysis window. - Display-path stall likely: heartbeat continued, input/audio evidence exists, and GPU probing failed or stalled. ``` Typeset from the report generator’s output for a test session. Where it stands ## Something another person can inspect. Shipped - Independent heartbeat, GPU and system collectors - Visible signal ages and collector health - Durable JSONL streams with recovery - Observation markers - Qualified freeze classification - Bounded support-bundle export In practice It gave me the evidence I was missing: heartbeat proof-of-life, GPU probe hangs, Windows event timing and crash-dump filenames. ## Related work [Another precise utility **Domain Looker** Domain checks that never confuse a failed request with availability.]() [Careful file handling **AWL** Atomic, read-back writes and path-scoped commits.]() [All projects ↗]() --- Source: https://alexjardinedigital.com/work/domain Author: Alex Jardine Open source · Domain research # Domain Looker Domain Looker checks up to 100 domains at once against official registry data and tells you honestly which are taken and which might be free. Paste anything: URLs, Markdown links, international names. No API keys and no registrar upsell. Ownership **My own tool · MIT licence** Status **Independent tool** My role **Design and development** Source **[github.com/Krmzyn/domain-looker ↗]()** [Read case study]() ## The short version What it is An open-source web app for bulk domain availability pre-checks using RDAP, the structured, registry-published successor to WHOIS. What I built A React 19 and Vite front end with an Express API in strict TypeScript: input normalisation with public-suffix rules, IANA bootstrap routing, per-registry pacing, caching, six explicit result states and CSV export, with tests and CI on GitHub. Why it stands out Precision in a small tool. It never confuses a failed request with an available domain, and every exported row keeps where it came from. **100** Domains per run URLs, Markdown links or bare names **6** Explicit result states Including rate-limited and unknown **15 min** Result cache Labelled, with the original check time **MIT** Open source Public repository with CI From the implementation and repository, 4 October 2026. In practice ## Six messy lines. Five honest rows. The parser accepts ordinary URLs and Markdown links, strips paths and subdomains, uses public-suffix rules to find the registrable name and converts international names to their ASCII lookup form. Duplicates collapse to one lookup; an invalid line gets its own row instead of stopping the batch. Sample input · 6 lines | Pasted | Normalized | Result | | --- | --- | --- | | ` https://www.example.org/pricing ` | ` example.org ` | Registered | | ` [Our blog](https://blog.example.net/launch) ` | ` example.net ` | Registered | | ` bücher.de ` | ` xn--bcher-kva.de ` | Registered | | ` example.org ` | ` example.org ` | Duplicate, skipped | | ` not a domain ` | ` (none) ` | Invalid | | ` my-brand-idea-2026.com ` | ` my-brand-idea-2026.com ` | Likely available | Six pasted lines become five result rows. Sample data, as in the demo mode below. ![Domain Looker workspace: pasted inputs on the left, results on the right with counts for registered, likely available, invalid, unsupported, rate-limited and unknown, and a table of original inputs, normalized domains and statuses.]() 1. **Paste anything.** URLs, Markdown links, names, mistakes. 2. **Counted honestly.** 5 unique rows checked, 1 duplicate skipped. 3. **Demo mode is labelled.** Demo results are clearly labelled on screen. 4. **“Likely”, not “available”.** The wording matches what RDAP can prove. Current desktop interface running locally in demo mode, 4 October 2026. The central decision ## A missing record is not a failed request. RDAP is the registration-data service behind every check. Each response maps to one explicit state, and only one of them suggests a name might be free. HTTP 200 + record **Registered** A registration record was returned. HTTP 404 **Likely available** No record found. A registrar still confirms purchase eligibility. HTTP 429 + Retry-After **Rate-limited** Try later. This says nothing about availability. Timeout · no service · malformed **Unsupported or unknown** Keep the reason instead of guessing. Implemented response handling, covered by tests. Details ## Spend requests where they are useful. The Node service uses IANA’s registry directory to route each lookup. Within a batch it allows up to four requests at once and one active request per registry host. Recent results are reused for 15 minutes, labelled as cached, with the original check time kept. The export follows the active filter and carries the original input, normalized domain, status, notes, check time and cache flag, so the result stays interpretable after it leaves the tool. CSV export excerpt | Original input | Normalized | Status | Checked | Cache | | --- | --- | --- | --- | --- | | ` https://www.example.org/pricing ` | ` example.org ` | registered | ` 2026-10-04T12:00Z ` | no | | ` bücher.de ` | ` xn--bcher-kva.de ` | registered | ` 2026-10-04T12:00Z ` | no | | ` not a domain ` | | invalid\_domain | ` 2026-10-04T12:00Z ` | no | Selected fields from the real CSV formatter, sample data. Where it stands ## A modest task, done precisely. Shipped - Mixed-input parsing with public-suffix rules - International domain conversion - Batch de-duplication and per-row errors - Six explicit result states - Per-registry pacing and 15-minute cache - Filtered CSV export with provenance Open source MIT licensed, strict TypeScript, tested and running CI on GitHub. ## Related work [Another precise utility **GPU Flight Recorder** A recorder that keeps evidence when a display freezes.]() [Research at scale **Lead Finder** A prospecting workspace that remembers what changed.]() [All projects ↗]() --- Source: https://alexjardinedigital.com/work/nextjs-migration Author: Alex Jardine Own site · Platform migration # Next.js migration I moved this portfolio from static HTML to Next.js and checked that nothing visible changed. Of 212 before-and-after screenshot pairs, 208 were pixel-identical, and the four small differences are measured in the report. Search engines now get clean URLs, real 404s and structured data. Ownership **My own site** Completed **October 2026** Stack **Next.js 16 · React 19 · TypeScript** Scope **16 routes · 10 case studies** [Read case study]() ## The short version What it is A design-led portfolio, finished and approved as frozen static HTML, that needed a real platform: server-rendered pages, clean URLs and the metadata search engines read, with no redesign along the way. What I built A Next.js 16 App Router site that reuses the original CSS, fonts, images and particle code byte for byte, adds clean routes, per-page metadata, structured data, a sitemap, robots.txt, RSS and real 404s, and shipped with a written parity record. Why it stands out Evidence that the move was safe. Every page was captured before and after at four widths and compared pixel by pixel, and the few differences are listed in the report. **212** Before-and-after screenshot pairs Five page templates at four widths **208** Pixel-identical The other four: no colour channel off by more than 5 of 255 **176** Route and width combinations Zero horizontal overflow **54** Reference files fingerprinted The build fails if one changes From the migration report and comparison data, 3 October 2026. The moving particle field, cursor and live clocks were masked in both builds; nothing else was. The brief ## A new platform nobody should notice. The design was finished and approved as a set of static HTML pages: hand-tuned type, a WebGL particle field, scroll choreography. It looked right, but it was a prototype. All ten case studies shared one page, **project.html?p=lambert** and its siblings, which arrived as an empty shell for JavaScript to fill in. An unknown project was just a message drawn by a script. The job was to put it on a production platform without redesigning it. Any visible change had to be deliberate and written down. Everything else had to land exactly where it was, at every width, in every case study. Same page ## Same page. What Google receives. A visitor sees the Lambert case study exactly as before. A crawler’s first request for it is where everything changed. First response project.html?p=lambert → /work/lambert **+11** **−2** ``` − Case study · Alex Jardine + Lambert Lighting · Alex Jardine + + + + + −
+
+

Lambert Lighting

+

Lambert makes architectural lighting in Rhode Island. I rebuilt …

+ … the rest of the case study, over 1,200 words +
``` Real responses: the frozen prototype file and the live page, trimmed to the lines a crawler reads first. The prototype’s `
` held no words until JavaScript ran. ![Lambert Lighting case study hero at 1440 pixels wide, identical in the static HTML reference and the Next.js build.]() The Lambert case study at 1440 px, captured at the migration on 3 October 2026 (its copy has been rewritten since). Below the header, the captures from the static prototype and the Next.js build are the same file, so there is nothing to compare by eye. The original stylesheet, fonts and images were reused unchanged. How it was done ## How the move was checked. Before any code moved, the approved reference was fingerprinted. Fifty-four source files are hashed, and the production build still refuses to run if one of them changes. If the approved design is edited by accident, the build stops. The port reused files rather than rewriting them. Search infrastructure was added underneath, and then every page was captured in pairs, from the same browser at the same viewport, and compared. The written record shipped with the code. The migration sequence 1. **Freeze the reference** Hash every approved source file 2. **Port without restyling** Reuse CSS, fonts, images and scripts as they are 3. **Add what search needs** Clean URLs, metadata, structured data, sitemap, real 404s 4. **Capture in pairs** Same browser and viewport, four widths, moving layers masked 5. **Measure every difference** Per-pair pixel deltas saved beside the screenshots 6. **Ship with the record** A report listing each difference and each untested area Search ## What crawlers receive now. Visitors see the same site. The HTML a crawler receives on the first request is completely different. What a search engine receives, now and before | Signal | Next.js build | Static prototype | | --- | --- | --- | | Case study address | A clean path per case study, such as /work/lambert | Was project.html?p=lambert | | First response | The whole page, rendered on the server and readable without JavaScript | Was an empty shell | | Unknown project | A real 404 status with a readable page | Was a script message | | Titles and descriptions | Per route in the first HTML, with canonical and social cards | Was set by script, no description | | Structured data | WebSite, Person, CreativeWork, and Article when published | Was none | | Sitemap and robots.txt | Generated from the same project data as the pages | Was none | | Feed | RSS, published articles only | Was none | | Unpublished sample | noindex, and kept out of the sitemap and feed | Was an ordinary page | The prototype was a local design reference, not the public site, so launch needed no redirects. On a live site this table becomes the redirect map: every old address, its new home and a permanent redirect between them. Calls I made ## Four decisions during the move. ### Reuse the original files Chose The original image files and stylesheet, served as they were. Instead of Framework image optimisation and a rewritten stylesheet at launch. Trade-off Some optimisation waited for a later, separately checked change, in exchange for zero crop or rendering drift on day one. ### Report every difference Chose Report the four imperfect pairs with their exact deltas. Instead of Raising the tolerance until every pair passed. Trade-off The headline is 208 of 212 instead of 212, and anyone can check the record. ### Mask only the moving layers Chose Mask the particle field, cursor and clocks, and nothing else. Instead of Masking any region that happened to differ. Trade-off Animation cannot be pixel-compared, so it was checked separately by behaviour and frame timing. ### Leave an unreproducible issue open Chose Record a first-load text size variation in the original that twelve fresh test runs could not reproduce. Instead of Shipping a speculative runtime fix. Trade-off One open review item in the report instead of new code with unknown side effects. In-house · Craft CMS to Webflow ## Copper CRM, 200+ pages. I led SEO for Copper CRM’s move of its 200+ page marketing site from Craft CMS to Webflow, working with the in-house team. My part was the redirect and URL map, the content migration and the launch SEO QA. That was a live site with existing search history, where a migration carries real risk: every old address needs a new home, and every redirect has to work on launch day. Copper’s working files stay with Copper, so the evidence on this page comes from a site I own. Result ## Where it stands now. The Next.js build went live in October 2026 and is the site you are reading. Everything added since has been built on it. Shipped - Server-rendered pages at clean /work/ addresses - Per-page titles, canonicals and social cards - Structured data, sitemap, robots.txt and RSS - Real 404s for unknown projects and articles - Build checks that fail on reference drift Not claimed Safari, physical phones and screen readers were outside this test pass. There are no traffic or ranking figures, because the prototype had no search history to compare against. Moving particles were checked by behaviour, not pixels. For a client migration The same method carries over: fingerprint the current site, map every URL, port without redesigning, capture pairs, and launch with a written record of what changed and what was not tested. ## Related work [Same discipline, larger system **Jardine Studio** Jardine Studio checks for search regressions on every pull request.]() [In the comparison above **Lambert Lighting** The client site used as the before-and-after example.]() [All projects ↗]() --- Source: https://alexjardinedigital.com/writing/keeping-nextjs-patched-2026 Author: Alex Jardine Published: 2026-10-22 # The patch you applied might not be the one you’re running Next.js has shipped nine security releases this year, and between October 14 and 21 Next.js, React, Node.js and PHP all shipped security fixes. Here are the ways a patch can look applied when it isn’t, and the checklist I now use on every site I maintain. Alex Jardine Oct 22, 2026 10 min read *Updated October 23, 2026: Amazon has since softened its PHP2Shell wording, and Netlify, Node.js, Vercel and PHP have added detail, so those sections are updated. I also corrected two dates and the count of upstream criticals, and filled in the full 2026 release list.* On October 14, Next.js and React shipped fixes for two critical remote code execution flaws. On October 21, Node.js and PHP followed with security releases of their own, and attackers were already probing for the PHP bugs. I maintain several production sites on Next.js, including this one, so I spent part of that week patching. The version bump was quick. Making sure the fix had actually landed took longer, because there are several ordinary ways for a patch to look applied when it isn’t. Here’s what I ran into and the checklist I use now. Next.js security advisories in 2026 5 critical 15 high 21 moderate 3 low ## Nine security releases in ten months By my count from the [Next.js advisories page](), the project has published 44 security advisories this year: 5 critical, 15 high, 21 moderate and 3 low. They arrived in nine security releases, and every one has version numbers you can check on npm: Next.js security releases, January to October 2026 Includes a critical Read the releases as a list - **January 26:** three advisories, fixed in 16.1.5 and 15.5.10. - **March 16:** five moderate advisories, fixed in 16.1.7, and on the 15 line in 15.5.13 and 15.5.14 (March 19). - **April 8:** one high advisory, fixed in 16.2.3 and 15.5.15. - **May 6 and 7:** 13 advisories across two releases a day apart, ending at 16.2.6 and 15.5.18. Vercel’s [changelog]() listed every 13.x and 14.x version as affected, with no patch for either. - **July 21:** nine advisories, fixed in 16.2.11 and 15.5.21. - **August 25:** two critical remote code execution flaws, one in AVIF image handling and one on Windows-hosted servers, fixed in 16.3.3 and 15.5.24. - **September 22:** a critical flaw in ` ImageResponse `, the ` next/og ` image generator, fixed in 16.3.6. - **September 30:** seven advisories, fixed in 16.3.8 and 15.5.27. - **October 14:** two criticals and a high, fixed in 16.4.1 and 15.5.28. All five of this year’s criticals allow remote code execution. As of October 22, none of the October Next.js or React advisories had been reported as exploited. I wouldn’t count on that lasting. When React2Shell landed last December, Wiz found that [39% of cloud environments]() contained vulnerable Next.js or React versions, and exploitation followed quickly. Rapid7 tracked 43 newly exploited high and critical vulnerabilities in the third quarter of 2026 and found exploitation began [three days after disclosure on average](), with 71% of it inside the first week. Datadog’s State of DevSecOps report puts the median dependency [278 days out of date](). That’s why I don’t leave a security release for the next sprint. ## A security patch is now a minor upgrade Since August, each critical fix has shipped on the newest minor of each line and nowhere else. If you were on 16.2 in August, there was no 16.2 patch. To stay patched on the 16 line, you went from 16.2.11 to 16.3.3, then 16.3.6, then 16.3.8, then 16.4.1. This site made that last jump, from 16.3.8, purely for the security fix. A minor upgrade is usually painless, but it can still break things. It can pull new transitive versions into the lockfile, and those can fail lint or tests for reasons unrelated to the fix. Since you’ll be doing this roughly once a month, it’s worth having tests that catch real breakage and a deploy you can roll back quickly. The line you’re on matters too. Next.js’s [support policy]() keeps each major in Maintenance LTS for two years after its release. Next.js 15 was released on October 21, 2024, so by that rule its maintenance window closed on October 21, 2026. The support page hadn’t been updated when I wrote this and still listed 15 as Maintenance LTS, so treat that as my reading of the policy, not an announcement. Either way, I’d plan as if 15.5.28 could be the last 15.x patch. The policy only promises fixes for unsupported versions “in rare circumstances,” and 13 and 14 got nothing this year. Four traps ## Trap one: your lockfile isn’t your package.json The October image flaw, [CVE-2026-96889](), isn’t in Next.js’s own code. It’s in librsvg, which Next.js reaches through ` sharp `, the image library behind ` next/image `. The advisory’s workaround is to make sure the ` sharp ` Next.js uses is 0.35.5 or later, and it notes that fresh installs get a fixed version automatically. A project with an existing lockfile may not. It wasn’t new in October, either. The librsvg CVE was published on September 23, and sharp shipped the fix in 0.35.5 on September 27, with [its own advisory]() three days later. That was two to three weeks before Next.js published on October 14. The ratings differ too: Next.js calls it critical, sharp rates it high. Next.js lists ` sharp ` as an optional dependency with a version range, and the ranges differ by line. On the 16 line, 16.4.1 asks for ` ^0.35.5 `, so the upgrade pulls it forward. (16.3.8 asked for ` ^0.35.4 `, which allowed the vulnerable release.) On the 15 line, 15.5.28 still accepts ` ^0.34.3 || ^0.35.5 `, so a lockfile that already resolved an older ` sharp ` can keep it after you bump Next.js. An override in your own ` package.json ` takes precedence over either range. I hit both of these. My studio’s site had an override pinning ` sharp ` to 0.35.4. On a client site, the upgraded Next.js still accepted an older ` sharp `. Both looked patched in ` package.json `, but both were still installing a vulnerable ` sharp `. ``` # What is actually installed, and why npm ls sharp next react react-dom npm explain sharp pnpm why sharp yarn why sharp # Overrides that can hold an old version in place grep -nE '"(overrides|resolutions)"' package.json grep -n -A5 '"pnpm"' package.json ``` Check what the lockfile resolves, not what package.json asks for. Then check what’s deployed. On a page served by Next.js, ` window.next.version ` in the browser console reports the version the client bundle was built with. It isn’t proof of what every server function is running, but it catches a common failure: a merged upgrade that never deployed. The same problem shows up one level higher. I keep this site’s source in one repository and deploy from a lean mirror. The security upgrade went into the mirror first, and a day later the source still said 16.3.8. If I’d synced from it, I would have quietly rolled the live site back, so the source needed the same upgrade. ## Trap two: the bug isn’t in Next.js Four of this year’s five critical Next.js advisories came from code underneath it. Three came through image handling: August’s AVIF flaw through libheif, September’s through Satori, and October’s SVG flaw through librsvg. The fourth, October’s ` use cache ` flaw, came from React Server Components. The AVIF and SVG flaws both arrive via ` sharp `. If your app uses ` next/image ` with the built-in optimizer, or ` next/og `, watch the image pipeline closely: it produced three of this year’s five criticals. The advisories are specific about preconditions. The October image flaw applies “when Next.js runs on glibc-based Linux.” Alpine-based Docker images use musl, so they may fall outside that particular advisory. That’s my reading, not Vercel’s statement, and it does nothing for the other October issues. Where you host matters as well. Netlify says its Image CDN serves ` next/image ` on its platform, so [that path isn’t exercised there](). The same flaw is still reachable through ` next/og ` ` ImageResponse `, which runs in your own functions, and there Netlify puts the impact at a crashed function invocation rather than code execution. The [Next.js announcement]() makes no such statement about Vercel. It just tells you to patch your Next.js dependencies, so that’s what I’d do on any host. The other two October issues depend on what your app uses: - The ` use cache ` flaw affects 16.0.0 through 16.4.0 apps with a ` use cache ` function that returns multiple typed arrays. There’s no workaround. It comes from an upstream [React Server Components flaw](), fixed in ` react-server-dom-* ` 19.0.9, 19.1.10, 19.2.9 and 19.3.1. - The Server Actions denial of service affects App Router apps with at least one Server Action, back to 13.0.0. Pages Router apps, or apps without Server Actions, aren’t affected. There’s no workaround and no patch for 13 or 14. Use the preconditions to decide which sites to patch first, then patch the rest. The next advisory will likely have different preconditions, and staying on the current minor keeps the fix one small upgrade away. ## Trap three: your runtime is someone else’s patch Node.js shipped [22.23.4, 24.22.1 and 26.12.1]() on October 21, fixing ten vulnerabilities. Node rated four of them high and none critical: two TLS hostname bypasses, a DNS name-truncation bug, and an out-of-bounds write in the bundled Brotli decoder that “permits remote code execution.” That last one is the one I’d worry about. It’s an upstream Google Brotli bug, and Google, which assigned the CVE, scores it 9.2. Node 20 reached end of life in April and Node 25 in June, so neither gets these fixes. Who applies that patch depends on how you run Node. On a managed platform, it’s usually the platform’s job, and its documentation should tell you when the new release reached your functions. [Vercel’s bulletin](), for example, says it added firewall rules the same day and manages patch versions for Node 22 and 24, but projects still on Node 20 have to switch to 22 or 24 and redeploy. In a Docker image, it’s whatever base image you built from. A tag like ` node:24-alpine ` only picks up 24.22.1 when you rebuild and redeploy, and it only has it once the image maintainers have published it. PHP showed how much that lag can matter. In the [Hacker News thread]() on the PHP release, developers described distribution packages and Docker images arriving hours after the fix, while one commenter’s managed host had patched within about an hour. The [PHP Foundation]() says one image vendor broke the embargo more than six hours early. Amazon’s threat intelligence team [saw an exploit attempt]() four days before disclosure and broad exploitation attempts once the fixes shipped, though it said it was unclear whether any would have succeeded. CISA said the same day that it had [not observed exploitation](). So the PHP bugs had been probed but not confirmed exploited, which is still reason enough to patch. ### If you also run PHP sites Many agencies run Next.js for some clients and WordPress for others, so the PHP side is worth a few lines. The fixed releases are [8.5.12, 8.4.27, 8.3.36 and 8.2.35](). Branches 7.4 to 8.1 have only minimal source backports from the PHP project, and anything older has no fix. PHP 8.2 itself reaches end of life on December 31, 2026, so if you’re on 8.2, plan the move to a newer branch now. The mistakes I saw developers describe were operational: - Changing the CLI ` php.ini ` while PHP-FPM reads its own. Check with ` phpinfo() ` served by FPM, not ` php -i `. - Applying the stopgap with ` ini_set() ` or `.user.ini `. PHP says to set ` session.upload_progress.enabled=0 ` in php.ini, an FPM pool or server configuration, and to restart every worker. On October 23, PHP [added]() that the setting should stay at 0 until the next regular bugfix release, even after patching, because of a separate issue in the same feature (CVE-2026-115560). Apps that never change the session serialize handler at runtime can leave it on. - Upgrading the package without restarting PHP-FPM, which keeps serving the old code. - Treating a firewall rule as the fix. Cloudflare deployed rules early, but it also reported attackers [changing their payloads]() and told customers to patch anyway. For site owners, I wrote a plainer guide on Jardine Studio: [Is your website exposed to this month’s security flaws?]() ## Trap four: scanners lag the advisories Dependabot and other scanners are worth running, but they read advisory databases, and those databases lag. In early September, a developer [reported on Hacker News]() that the critical AVIF advisory from August 25 was live on the Next.js repository but returned a 404 from the GitHub advisory API and OSV, and that their scanners had missed it. OSV [lists that advisory]() as published on September 8, two weeks after the fix, and lists both October 14 criticals as published on October 22. That’s a small sample, not a measured rate, but it’s enough that I don’t rely on scanners alone for criticals. So I watch the sources directly: the security advisories pages for ` vercel/next.js ` and ` facebook/react ` on GitHub, the Next.js blog, the Node.js security blog, and the PHP announcements. Next.js posted advance notice on October 8 for the October 14 release, and those six days were enough to plan the work before the fixes landed. ## The checklist I use This is what I ran across 12 JavaScript projects in October. Four were live sites, and all four were patched and live the same day, each as its own change so it could be reviewed and rolled back on its own. 1. **Inventory.** For every project, record the framework and version, where it runs, what provides the runtime, and who’s responsible for patching it. 2. **Resolve versions.** Read the lockfile, not ` package.json `: ` next `, ` react `, ` react-dom `, ` react-server-dom-* ` if present, and ` sharp `. Look for overrides and resolutions. 3. **Match the advisory’s preconditions.** App Router or Pages Router? Server Actions? ` use cache `? The image optimizer on glibc Linux? ` next/og `? A Windows host? Use the answers to decide which projects go first. 4. **Upgrade on a branch.** Move to the patched minor, force fixed transitive versions where needed, and regenerate the lockfile. 5. **Verify.** Type check, lint, test and run a production build. Serve it locally and load pages and images. Then confirm the resolved versions again. 6. **Ship and confirm.** Deploy, check the live version, and update every other copy of the code: mirrors, templates and starter repositories. 7. **Record it.** The date, the versions before and after, and anything that held an old version in place, so the next patch starts from a known state. ``` # Quick precondition checks for the October advisories grep -rln '"use cache"' app src 2>/dev/null grep -rln '"use server"' app src 2>/dev/null grep -rn 'ImageResponse' app src 2>/dev/null grep -n 'unoptimized' next.config.* ``` Step 3, run from the project root. ## If you look after sites for clients On client sites, the hard part of patching is usually knowing whose job it is. In the PHP thread, the same story came up repeatedly: an agency or freelancer builds the site and moves on, the client assumes the host handles updates, and nobody watches the releases, so when a critical fix lands, nobody applies it. If you have more than a handful of client sites, the inventory from step one is worth building first. One line per site: what it runs, which versions, where, and whose job the next patch is. When a release lands, you can see which sites are exposed without opening repositories one by one. ## Not sure your site is patched? If you’re not sure whether your site is behind, or you’ve inherited one that nobody has been maintaining, I’ll check it for free. Send me the URL, or for a private project, the ` package.json ` and lockfile. I don’t need access to your repository. I’ll reply within one business day with what it runs, which current advisories apply, and what it would take to patch it, whether that’s a quick version bump or a bigger move off Next.js 13, 14 or 15. If you want me to do the patching, or to keep an eye on releases for you every month, we can talk about that too. Email me at [alex@jardinestudio.com](), or use the form below. If you’re an agency with client sites in the same position, have a look at [how I work with agencies](). --- Source: https://alexjardinedigital.com/writing/how-this-site-works Author: Alex Jardine Published: 2026-10-15 # How this site works An 80,000-particle field, text the particles flow around, page transitions printed in halftone, and the fallbacks that keep all of it honest. Alex Jardine Oct 15, 2026 8 min read This site is usually the first piece of my work people see, so I treated it like a client project: pick one idea worth committing to, then do the unglamorous engineering that makes it hold up on real devices, and for people who would rather the page sat still. Here’s how it works, the calls I made along the way, and where my evidence runs out. ## One point cloud, many scenes Everything moving behind the page is a single WebGL2 point cloud: 80,000 particles on desktop and 36,000 on screens narrower than 860 pixels, drawn in one call per frame. Each particle knows where it came from, where it’s going and a few random values of its own, and the vertex shader works out where it should be right now. Every section of the site names a scene, and each scene is a small generator that writes positions and colours into buffers. My name on the homepage is sampled from the site’s display typeface, Syne, so the particles land on real letterforms. The knot is a parametric curve. The gears on Services are proper involute gears, 24 and 18 teeth with a shared module, set half a tooth out of phase so they stay in mesh as they turn. On Lambert Lighting, three pendants throw beams onto the floor. On the Writing page, a particle pencil writes a looping cursive line, and the same curve drives both the ink and the pencil tip. None of the scenes sit at fixed coordinates. Each one is sized to the open space in its section, measured from the real layout, and follows that section on a light spring as you scroll. ## Moving between shapes When one scene hands over to the next, every particle flies its own curve from where it was to where it belongs. Two details stop that reading as 80,000 separate movements. Every hand-over shares one bend, with only a small spread per particle, so the cloud travels as a stream. And the side of the old shape facing the new one leaves first, so the shape seems to pour toward where it’s going. People often scroll on before a shape has landed. When that happens, the shader that drew the last frame writes each particle’s on-screen position, colour and size into a buffer with transform feedback, and the next flight starts from exactly there. Nothing comes back to the CPU, so an interrupted hand-off costs no main-thread time and never jumps. ## Particles that get out of the way of words Particles behind text are a readability problem before they’re a visual one, so the big shapes flow around lines of text instead of sitting on them. While the page is still, I measure every line of text once, as a rectangle in page coordinates. Each frame just shifts that list by the scroll offset and sends the 32 rectangles nearest the middle of the screen to the shader. There, each particle checks its distance to each rounded rectangle, with a slightly different edge per particle so the boundary looks organic rather than boxed in. Particles near a word part around it, and the few that can’t clear it fade out. My first version simply shoved particles out to a fixed margin, which piled them into bright ridges along the edge of every paragraph. Measuring the page on every frame was a mistake too. It forced a layout pass for as long as the page was moving, which is exactly when a phone needs that time for scrolling. ## Deciding which scene owns the screen As you scroll, the site works out how much of each section’s space is on screen. A new scene takes over once its share passes 30% and is at least 12 points ahead of the current one, so the field never flickers at a boundary. That rule had a blind spot. The scene before Say Hello is usually a full-screen ambient field that always counts as fully visible, so Say Hello only won once that section had nearly left the screen, and the contact shape formed late. I added a weight, ` data-pull `, set to 1.8 on Say Hello. A weighted scene takes over once its weighted share is full, at about 55% of its space on screen, and the same weight applies on the way out, so it can’t flip-flop. On a 1440 by 900 desktop, the homepage used to hand over when the top of the contact section was 48% of the way down the screen. Now it happens at 83%, as the section comes into view. On a phone, case studies went from 35% to 80%. Fast scrolling gets its own rule. Above 4,500 pixels per second, the field stops trying to build a shape for every section it passes. The shape it’s holding disperses into a side field in its own colours, and nothing new forms until the page has slowed below 1,800 pixels per second for 140 milliseconds. At ordinary speeds, flights finish sooner the faster you scroll, up to three times quicker, so a shape is formed while its section is still in view. Clicking Contact or Back to top is a different case. A smooth in-page scroll starts slowly, and the slow-scroll rule used to read those first 150 milliseconds as the reader stopping, so the field started forming every section the page then raced past. A jump now holds the field until the scroll has actually ended: one dispersal on the way down, then only the destination forms. Any wheel, touch or key input hands control straight back to you. None of this geometry is built at the moment it’s needed. Every scene, and the side field each one disperses into, is generated in idle time ahead of use. ## Page transitions printed in halftone Every internal link on this site is a full page load. The transition makes it feel like one continuous piece anyway. When you click a link, an ink layer erases the page in an ordered-dither wave that starts where you clicked, while the particles fly into a full-screen halftone in the next page’s colours. The halftone is an 8 by 8 Bayer threshold over a soft radial tone, the way a print screen renders an image. Then the browser navigates. The next page has to show that same halftone on its very first frame, before any of its scripts have loaded. A small inline script in the document head reads a flag the previous page left behind and paints the same cells in the same colours. Once the title has been parsed, the halftone dissolves outward from it, and the particle field joins in behind as soon as it boots. Opening a work card speaks the same language: the card’s picture is rebuilt from the field’s own particles right where it sits, then streams into the halftone. The weak point is that the new page boots on the same thread that draws the dissolve, and one long task would freeze it halfway. Where the browser can render WebGL off the main thread, a worker takes the animation over on a second, identical canvas and carries on from the same point. Without a worker, the main thread keeps drawing. Without WebGL, the cover is a plain 2D fade. Safety timers clear the cover within a few seconds whatever happens, and a navigation that never completes uncovers the page again. I also took something out. The browser’s own cross-document fade held the last frame frozen for about a third of a second before the next page could paint. The halftone already covers that moment, so dither navigations skip it. ## Motion is optional The header has a Motion switch, with the same control in the menu on phones. Turning it off parks the field: the homepage keeps my name, and other pages let the particles rest at the edges of the screen. The site remembers your choice for next time. WCAG 2.2.2 asks for a way to pause moving content, and some people just prefer a still page. If your system asks for reduced motion, the site listens without being asked twice. There’s no homepage greeting, shapes are placed instead of flown in, the shimmer and cursor lens are off, the pencil holds still beside a finished line, and page transitions become ordinary navigations. The content is identical either way, and nothing ever waits on an animation. The interface follows the same rule: the art direction lives in the background, and the controls stay put. I removed magnetic buttons, card lifts and a label-scrambling hover, because each one moved a control under the pointer. The main call to action used to shift 17 pixels on hover. Now it doesn’t move at all. ## When the GPU isn’t up to it The most useful lesson was about devices without a usable GPU. Lab tools like PageSpeed Insights often run WebGL in software, and the live site scored badly there: Total Blocking Time between 1.4 and 26 seconds, with single tasks of half a second or more. Real devices with GPUs do far better, but anyone whose browser falls back to software rendering would have seen the same freezes. WebGL has an option for exactly this. Creating a context with ` failIfMajorPerformanceCaveat ` asks the browser to say no if it would run much slower than native, which usually means rendering in software. The particle field, the transition ink, the arrival cover and its worker, and the About portrait all ask for it now. A refused context falls back to the path each one already had: a designed static page with a dotted texture where the field would be, plain navigation, a 2D cover fade, and the still portrait. I measured the same build locally with the option on and off, watching long tasks for four seconds after load plus ten wheel scrolls: - Homepage, software WebGL: Total Blocking Time fell from 514 ms to 31 ms, and the worst single task from 390 ms to 81 ms. - Services: 522 ms to 17 ms. About: 565 ms to 14 ms. Work: 211 ms to 0 ms. - With a hardware GPU the field still runs, and the numbers didn’t change beyond noise. To be clear about the limits: these are local lab measurements in Chrome. I haven’t re-run PageSpeed Insights on the deployed build yet, Safari and Firefox make their own calls about software rendering and I didn’t measure them, and there’s no field data from real visitors yet. ## How it’s checked The production build won’t finish unless the writing, image, particle, navigation and case-study tests and a typecheck all pass. The particle and navigation suites alone run more than 60 tests. Release QA drives the site in Chromium, WebKit and Firefox at widths from 320 to 2,560 pixels, through fast scrolling, transitions, no WebGL, reduced motion, slow networks and a long session. The latest long session in Chromium held memory flat with no frames over 33 milliseconds. What it hasn’t had yet is proper time on a physical iPhone in Safari, which no automated browser fully stands in for. That’s next on my list. ## Why this matters for your project Very few sites need 80,000 particles. Most need what sits underneath them: a reason for every effect, a cost I’ve actually measured, a fallback that was designed rather than left to chance, and honest notes on what’s been tested and what hasn’t. That’s the standard I hold every project to, whether it has particles or not. If you’re hiring for work like this, or want to talk about a site of your own, email me at [alex@jardinestudio.com](), or have a look through the [case studies](). --- Source: https://alexjardinedigital.com/writing/welcome-to-my-portfolio Author: Alex Jardine Published: 2026-10-04 # Welcome to my portfolio Who I am, what I build, where to start, and how this site was made. Alex Jardine Oct 4, 2026 4 min read I’m Alex Jardine, a web developer and technical SEO specialist in Toronto, with a background in design and growth. I build websites, applications and tools, working independently and alongside agency and in-house teams. This portfolio brings that work together. If you are a business owner, an agency or a hiring team, this note is the quickest way to find what matters to you. ## Start with the work The [Work]() section holds the case studies. Each one opens with what the project is, what I built and why it stands out, then shows the evidence: real screenshots, annotated walkthroughs and the decisions behind them. - [Jardine Studio]() is my own studio platform: a production website with free tools, a lead pipeline and 845 automated tests behind it. - [Silktrace]() is a live SEO research product with private workspaces and request-bound spending. - [Palisades Lodge]() is an ongoing growth partnership: organic clicks up 248% and a custom booking step into Mews. - [Lambert Lighting](), [The Black Salt Room]() and [B&B Specialty Contractors]() are client websites, each built around how their customers decide. - [Lead Finder](), [AWL](), [GPU Flight Recorder]() and [Domain Looker]() are the products and tools I build for my own work. ## What I can do for you [Services]() explains the development, technical SEO and design work I take on. [For agencies]() covers freelance and white-label delivery, while [About]() introduces my background and experience. Whether you have a project, a partnership or a role in mind, those pages explain how I could contribute. ## How this site was built The portfolio is a Next.js site with a particle field I wrote in WebGL. Up to 80,000 particles reshape themselves for each section: my name on the homepage, a gear train on Services, a pencil writing at the top of this page. The motion respects reduced-motion settings, and the pause button in the header stops it entirely. The case studies use the same discipline I bring to client work: every number is counted from the source, every screenshot is real, and every page is checked at desktop and phone widths before it ships. ## What you will find in Writing Notes from the work: project lessons, technical experiments, web management and growth, and the tools I build along the way. ## Get in touch Have a project in mind, or a role you are hiring for? I’d like to hear about it. - Email: [alex@jardinestudio.com]() - Studio: [jardinestudio.com]() - LinkedIn: [linkedin.com/in/alex-jardine]() - GitHub: [github.com/Krmzyn]() - Resume: [download the PDF]()