Argand.org public marketing site — pre-launch landing page
  • Svelte 76.1%
  • TypeScript 17.1%
  • CSS 2.8%
  • Python 2.2%
  • HTML 1.1%
  • Other 0.7%
Find a file
nicweyand ecd585d354
All checks were successful
CI / quality (push) Successful in 44s
CI / deploy (push) Successful in 19s
Add Rive-ready scene seam to the step-decks (SVG today, Rive when authored)
Introduce a per-section "scene seam" so a deck can name a sceneId; when Nic later
registers a .riv for it, premium devices render the Rive scene while potato/everyone
keeps the SVG. The section-.riv registry map is EMPTY today, so every id resolves to
null and the SVG always renders -- zero visual change.

- RiveCanvas.svelte: bounded (non-fullscreen) Rive player extracted from RiveScene.
  Shares the self-hosted-wasm (?url + RuntimeLoader.setWasmUrl) and lifecycle pattern;
  dynamic-imports canvas-lite only on mount, sizes to its container (ResizeObserver),
  pauses on reduced-motion + hidden tab, and calls an onerror prop on load failure.
- SceneFrame.svelte: the seam. Renders the SVG children by default; only swaps in
  RiveCanvas when sceneId is set, the device is premium (not potato, motion allowed),
  a .riv is registered, and it has mounted client-side. Reverts to the SVG on error.
  SSR emits the SVG; Rive is attempted only after hydration.
- registry.ts: resolveSceneRiv(sceneId) backed by an empty map (returns null for all
  ids today) with a comment showing how to register one later.
- StepDeck/ArgandDeck: optional sceneId prop wraps the scene render in SceneFrame.
  Threaded stable ids from all 7 callers (search-flow, principle, anti-ai, engine,
  supply-chain, why-gaps, argand-bio).
- StepDeck optional autoplay (default OFF): auto-advance every ms, pause on hover +
  hidden tab, disabled under reduced motion. Not enabled on any caller yet.
- Pure helpers shouldRenderRive() + autoplayActive() (plus a shared STEP_TRANSITION
  config) are unit-tested; registry test asserts every live sceneId resolves to null.

Rive runtime stays a dynamic chunk via the canvas-lite dynamic import -- not in the
main entry bundle.
2026-06-16 01:47:31 -04:00
.forgejo/workflows ci: use pnpm-managed node directly (runner has no corepack) 2026-06-11 14:44:10 -04:00
content fix: set betaCtaLabel and betaDisclaimer to correct values in home.json 2026-06-12 16:16:34 -04:00
docs feat(theme): dark mode — werewolf-blue motif on slate (ported from argand/web) 2026-06-15 18:52:53 -04:00
listmonk add Listmonk double opt-in / welcome email template (Argand palette, email-safe) 2026-05-28 21:24:34 -04:00
scripts ci: auto-deploy argand.org to utilities on push to main 2026-06-11 14:30:42 -04:00
src Add Rive-ready scene seam to the step-decks (SVG today, Rive when authored) 2026-06-16 01:47:31 -04:00
static feat(scenes): Rive scene runtime + switcher — replace hand-rolled backdrop engine 2026-06-15 18:05:08 -04:00
.gitignore gitignore: exclude .claude local settings and worktree metadata 2026-06-12 15:59:34 -04:00
.npmrc feat: initial Argand marketing landing page 2026-05-28 09:10:25 -04:00
energy-check.png chore: add working QA screenshots (energy/homepage/presstrace/principal) 2026-06-15 18:09:13 -04:00
homepage-check.png chore: add working QA screenshots (energy/homepage/presstrace/principal) 2026-06-15 18:09:13 -04:00
lighthouserc.json chore(lighthouse): assert category budgets on real pages, exclude 404 fallback shell — green meaningful gate 2026-06-15 16:49:21 -04:00
package.json feat(scenes): legibility scrim (site-wide) + canvas-lite runtime (~300KB gz) 2026-06-15 18:22:19 -04:00
pnpm-lock.yaml feat(scenes): legibility scrim (site-wide) + canvas-lite runtime (~300KB gz) 2026-06-15 18:22:19 -04:00
presstrace-intro.png chore: add working QA screenshots (energy/homepage/presstrace/principal) 2026-06-15 18:09:13 -04:00
principal-check.png chore: add working QA screenshots (energy/homepage/presstrace/principal) 2026-06-15 18:09:13 -04:00
README.md Newsletter: complete the no-JS path end-to-end (urlencoded → Listmonk form) 2026-06-02 13:50:18 -04:00
svelte.config.js feat(scenes): Rive scene runtime + switcher — replace hand-rolled backdrop engine 2026-06-15 18:05:08 -04:00
tsconfig.json feat: initial Argand marketing landing page 2026-05-28 09:10:25 -04:00
vite.config.ts Implement argand.org audit: route split, progressive forms, SEO/trust finish 2026-06-01 10:20:58 -04:00
vitest.config.ts feat(theme): gentle cross-browser dark↔light crossfade on toggle 2026-06-16 01:19:06 -04:00

argand.org — pre-launch site

The public pre-launch site for Argand, an independent, privacy-first search engine built in pure Rust by one person. This repo is the marketing/landing site only; it is public on purpose, and is part of the project's trust story — so it is held to the same bar as the rest of Argand.

Live: https://argand.org

What's public here

  • The full source of the landing site (SvelteKit + adapter-static).
  • The deploy script (scripts/deploy.sh).
  • The self-hosted double-opt-in email template (listmonk/optin-welcome.html).
  • Trust artifacts: security.txt, PGP key, and the genesis warrant canary (static/.well-known/).

What stays private until launch

  • The search engine itself and its ~59 microservices / ~47 Rust crates.
  • The crawl, index, ranking models, and the Argand Plane vector store.
  • The /api/newsletter-subscribe endpoint implementation (a same-origin proxy to a self-hosted Listmonk instance on Argand's own infrastructure).

Stack

  • SvelteKit 2 (Svelte 5 runes) + TypeScript, @sveltejs/adapter-static (fully prerendered — no server at runtime).
  • A strict Content-Security-Policy, self-hosted fonts/images, no third-party scripts.
  • vitest for unit tests.

Develop

pnpm install
pnpm dev          # local dev server
pnpm check        # svelte-check / type check
pnpm test         # vitest
pnpm build        # prerender to ./build
pnpm preview      # preview the production build

Node 22+, pnpm 10+.

Project structure

src/
  routes/                 # one folder per page (all prerendered)
    +layout.svelte        # site nav, footer, global <head>, JSON-LD
    +page.svelte          # short, conversion-focused homepage
    benchmarks/ privacy/ presstrace/ roadmap/ about/ updates/
    security-policy/ accessibility/ press/
    sitemap.xml/+server.ts  # generated at build time
    feed.xml/+server.ts     # generated at build time
  lib/
    components/           # SeoHead, OnThisPage, PageHeader, sections/, decks
    data/                 # services + updates (build log) data
    nav/site.ts           # routes, nav links, absolute-url helper
    seo/                  # pure sitemap + feed builders (unit-tested)
static/
  fonts/ images/ icons/   # self-hosted assets
  robots.txt opensearch.xml site.webmanifest
  .well-known/            # security.txt, pgp key, warrant canary

The waitlist flow

Both signup forms (newsletter + early-accounts) are progressively enhanced: they are real <form method="POST" action="/api/newsletter-subscribe"> elements with native HTML validation, and JavaScript only layers on inline success/error feedback. With JS disabled, the browser performs a normal POST and lands on a real confirmation page.

How both paths complete (the /api/newsletter-subscribe endpoint is a same-origin Caddy proxy to a self-hosted Listmonk; config outside this repo):

  • JS pathfetch posts JSON ({ email, list_uuids }). Caddy routes Content-Type: application/json to Listmonk's /api/public/subscription, which returns JSON; the component renders inline success/error.
  • No-JS path — the native form posts application/x-www-form-urlencoded (email, plus a hidden l = the list UUID, Listmonk's public-form param). Caddy routes urlencoded posts to Listmonk's /subscription/form, which subscribes and renders a real HTML confirmation page. Caddy also serves Listmonk's /subscription/* (confirm / unsubscribe / manage-prefs) and /public/static/* on-origin so those pages — and the unsubscribe links in emails (Listmonk root_url = https://argand.org) — resolve under the brand domain. Admin (/admin, authed /api/*) is not proxied.

SEO & discovery

  • sitemap.xml, feed.xml: prerendered endpoints; <lastmod> / <lastBuildDate> are stamped from the build timestamp (vite.config.ts __BUILD_TIME__), so freshness never needs hand-editing.
  • robots.txt: static; disallows /api/ and points at the sitemap.
  • Structured data (JSON-LD): site-wide Organization + WebSite in the layout <head>, plus a per-subpage BreadcrumbList (Home → page) emitted by SeoHead from the route's nav label, all serialised through a <script>-safe helper (src/lib/seo/jsonld.ts).
  • Icons / PWA: regenerate with uv run --with pillow --with fonttools --with brotli scripts/gen-icons.py (renders from the site's own Atkinson Hyperlegible woff2).
  • opensearch.xml is kept in the repo but intentionally not advertised via <link rel="search"> until the engine is live.

Trust & policy

  • Vulnerability disclosure: /security-policy, machine-readable at /.well-known/security.txt.
  • Warrant canary: /.well-known/canary.json (+ detached signature). Rotation ops are documented in docs/CANARY.md.
  • Privacy architecture: /privacy.
  • Accessibility statement: /accessibility.

Deploy

scripts/deploy.sh builds, packages, SHA-256-verifies the upload, extracts to a timestamped release on the datalake, then atomically flips the current symlink (keeping previous for rollback). Caddy serves current.

./scripts/deploy.sh

Quality / CI

CI (.forgejo/workflows/ci.yml) runs on every push: pnpm check, pnpm test, pnpm build, plus an advisory Lighthouse pass. To run the Lighthouse audit locally against a production build:

pnpm build
pnpm dlx @lhci/cli autorun   # uses lighthouserc.json

Reporting issues

License

Pre-launch. The site content and brand assets are © 2026 Nic Weyand, all rights reserved, unless noted otherwise.