Reference
Tunables & specs
This page is the reference hub for the things that aren't covered by a feature page: the flat registry of code-only knobs (tunables.md), the standalone build briefs under prompts/ and Sanity Page Composition.md, and the CLAUDE.md files that are the authoritative guide for any human or agent touching the repo.
Everything here points to a real file. Where a feature has its own page (animations, the audit tool, design tokens), the deep detail lives there — this page is the index that tells you which file owns each kind of reference material.
The tunables registry
website/tunables.md is a single flat document listing every code-adjustable knob that has no Studio or UI control: animation durations and easing curves, scroll thresholds (rootMargin, start/end fractions), breakpoint-gated effects, hardcoded layout ratios and offsets, and cache TTLs. It exists so a designer or future contributor can find and nudge these values without reverse-engineering each component.
The mental model: anything an editor should control is a Sanity field; anything only a developer should change — but that someone might reasonably want to tune — is a tunable. The file's own opening states it plainly:
A registry of code-adjustable knobs that have no Studio/UI control — animation timings, easing curves, scroll thresholds, breakpoint-gated effects, layout ratios, and similar hardcoded values a designer might want to nudge. Edit the source file, save, refresh.
What's in it today
The registry is organized by feature, not by file. The current categories:
- Grid backdrop, vignette & section-panel shadow — the dark-theme
:rootknobs insrc/styles/global.css:--grid-size,--grid-opacity,--grid-fade-spread/--grid-fade-blur(the vignette), and the.section-panelcard shadow/radius (--card-radius,--card-shadow-blur/-spread/-y). Plus the cursor-tracking glow knobs (--grid-glow-radius,--grid-glow-line, and theFOLLOW/IDLE_MSfeel consts inGridGlow.astro). - Scroll-in reveal cascade — the behavior constants in
src/lib/animation-config.ts(TRIGGER_FROM_BOTTOM,REVEAL_TRIGGERS,REVEAL_STAGGER_MS,REVEAL_MAX_DELAY_MS,HERO_REVEAL_BASE_DELAY_MS), the visual--reveal-*CSS vars, and the per-markup override attributes (data-reveal-stagger,data-reveal-delay,data-reveal-only,data-reveal-no-fade/-no-move,data-reveal-with-parent,data-reveal-trigger). - Hero headline animation — the inline beat timings/eases inside
initHeroHeadlines()and theIGNITE_TONESarray insrc/lib/heroHeadlineAnim.ts. - Marquee scroll speed —
--marquee-durationinglobal.css. - Edge caching —
EDGE_TTL_SECONDS(default300) andTRACKING_PARAMSinsrc/middleware.ts. - Global theme fetch memo —
TTL_MS(default60_000) insrc/sanity/queries/globalStyles.ts. - Link prefetch strategy —
prefetch.defaultStrategyinastro.config.mjs. - Audit tool — the disposable-email chunk preload trigger in
src/components/audit/disposable.ts. - CTA band parallax —
--parallax-rangeand theupdate()mapping inCtaSection.astro. - Sticky scroll content + image section — the pinned-image swap timing (
centerObserverrootMargin), crossfade speed, per-group fade-up, the mobile card-bleed scrub (START/END/easing,--bleed-up), and the desktop column-split geometry, all inStickyScrollSection.astro. - PageSpeed dials section — the
SIZE/STROKEdial geometry andband()/BAND_COLORthresholds inPsiScoresSection.astro, itsTTL_MSread memo inpsiSnapshot.ts, and the GitHub Action's refreshschedule.cronin.github/workflows/refresh-psi.yml.
Each of these has a companion feature page with the why: the cascade knobs are explained on Scroll-in reveals, the hero beats on Hero headline animation, the parallax/sticky scrub on Components, and the PSI dials/edge cache on Performance budgets and Deployment. tunables.md is the flat index across all of them.
The keep-it-current rule
The registry is part of the feature, not optional. The rule, from website/CLAUDE.md under "Code tunables":
Whenever you add or enhance a feature that introduces (or changes) such a knob, add or update its entry in
tunables.mdin the same change. Remove the entry when the knob is removed.
Each entry states three things briefly: what the knob does (including the direction of effect where it isn't obvious — e.g. "larger = fires later"), the path to the file it lives in, and semantic context to locate it (which <script>/<style> block, which named function / observer / variable / selector).
Never cite a line number in tunables.md
Locations are given as semantic context — a searchable identifier, selector, or block — never a line number. Line numbers drift the moment code is added or removed above them, and a registry that points at the wrong line is worse than no registry. When you locate a knob, give the next person something to grep, not a number to mis-trust.
A subtle but important distinction the file itself draws: the panel vs full-bleed lever (whether a section renders as a floating panel or an edge-to-edge band) looks like it belongs here, but it's not a code tunable — it's the editor-controlled sectionLayout field in Studio. tunables.md documents the boundary explicitly so nobody tries to "tune" something an editor owns. See Studio & singletons and Brand tokens & theme for that lever.
The build specs (prompts/)
The prompts/ directory and a couple of root-level .md files hold standalone briefs — design specs and the prompts that drove a build. They are historical/instructional artifacts, not live-wired configuration. Read them to understand why a feature is shaped the way it is; read the feature's own docs page and source for what it does now.
Sanity Page Composition.md
Sanity Page Composition.md is the design rationale for the page-builder pattern — how far Sanity can go in editing a page beyond filling in fixed fields. Its core thesis ("the hinge: instances, not designs"):
Sanity is a content database, not a design tool. [...] It cannot invent a new visual section. Every new "section type" is a coordinated pair of: a schema [...] and an Astro component [...]. Once a section type is installed, Sanity can place arbitrarily many instances of it. The first instance always costs code.
It lays out a five-level spectrum (fields-only → toggle+order → variants → full page builder → composable bodies) and the tradeoffs of each. The doc recommends starting at level 2; the site ultimately shipped the full level-4 page builder (a typed sections[] array dispatched by a polymorphic _type switch). Treat this file as the conceptual background for Page builder overview and Adding a section — its renderer sketch (sectionComponents[block._type]) is the seed of the real SectionRenderer.astro, not its current form.
website-audit-tool-spec.md
prompts/website-audit-tool-spec.md is the original build brief for the Free Website Audit lead magnet: URL normalization (normalizeUrl), the PSI v5 runPagespeed call, result parsing, the on-screen-vs-email split, and the HubSpot Forms v3 submission. It is the source of the deliberate "teaser on screen, fixes in the email" design that still defines the tool.
This spec describes the v1 architecture, not the current one
The spec describes a fully client-side tool: PSI and HubSpot called directly from the browser, the PSI key and HubSpot IDs in PUBLIC_* env vars. The shipped tool is different — it runs server-side through /api/audit (a Cloudflare Worker), so the PSI key (PSI_API_KEY) is a runtime secret, not a PUBLIC_* var, and the full PSI detail never reaches the client. The signal-check rebuild (next) superseded the single-PSI-grade design too. For the live architecture, read Audit tool: architecture & flow and Audit tool: checks, scoring & teaser — not this spec.
claude-code-prompt-audit-signals.md
prompts/claude-code-prompt-audit-signals.md is the brief for the rebuild that replaced the thin single-PSI summary with a multi-signal check system. It defines the architecture that is live today:
- Three decoupled layers — checks (pure functions over a once-fetched
SiteData), a weighted 0–10 score, and a fixed top-3 teaser selection. - The
Checkinterface (id,group,impact,subIssues,run(site),copy), the score formula (impact-weighted over only the checks that ran,unknownexcluded from the denominator), and the teaser ranking (fail before warn, then highest impact, always exactly 3 cards). - The guardrails that still govern the tool's copy: consequence-framing on the page and never the fix;
outdated-techstays conservative ("running an older version of X," never "you are vulnerable"); never surface anunknown; checks are deterministic and pure.
This is the authoritative intent doc for the audit tool. The concrete checks, scoring helpers (bandScore, psiBand), and teaser rendering live under src/components/audit/ and are documented on Audit tool: checks, scoring & teaser. Lead capture via HubSpot is on HubSpot & lead capture.
AI / agent context — the CLAUDE.md hierarchy
The three CLAUDE.md files are the authoritative guide for any human or AI agent working in this repo. They override default behavior; an agent reads the relevant one before touching code. They form a hierarchy:
| File | Scope | Authoritative for |
|---|---|---|
CLAUDE.md (root) | The monorepo as a whole | That the repo holds two independent packages; "read the relevant child CLAUDE.md first"; the cross-package rules (a new section type touches both; website/ only ever reads Sanity; the two repos don't share Git history). |
website/CLAUDE.md | The Astro marketing site | The full project guide — stack, routing, the Sanity read model, draft mode, the two-build split, brand tokens, scroll reveals, the hero animation, conventions, and the load-bearing footguns. The longest and most consulted of the three. |
studio/CLAUDE.md | The Sanity Studio | Schemas of record, page-builder section registry, singletons and their bootstrap scripts, Presentation Tool config, and the seed/migration scripts. |
How they relate: the root file is a dispatcher — it states the two-package structure and the cross-cutting rules, then points at the two child files for everything package-specific. The child files are self-contained guides for their package and cross-link each other where a change spans both (adding a section type, schema-to-render field changes). The website file additionally points at its own companion docs — tailwind.md, _design-best-practices/, Brand Guidelines.html, and this tunables.md — under its "Read first (authoritative)" header.
A few load-bearing rules these files encode are worth surfacing here because getting them wrong fails silently:
Stega breaks string equality in draft mode
In draft preview, sanityPreview encodes every string field with invisible Unicode tag characters for click-to-edit. They're invisible but not zero-length, so they break exact comparisons, switch, regex, JSON.parse, and object/array index keys. Any Sanity string that controls JS logic must be passed through stegaClean() from @sanity/client/stega first. Without it, code works in production (no stega) and silently fails only in draft. Rendering a plain string ({post.title}) does not need cleaning. See Visual editing.
urlFor() throws on asset-less images — guard on ?.asset
urlFor() throws when a Sanity image has no resolvable asset, and an unhandled throw in a section's frontmatter blanks the whole page (Astro discards the SSR stream after the doctype, and the CF dev adapter swallows the trace, so it fails silently). A ghost {_type:'image', alt:'…'} with no asset is still truthy, so never truthy-check the object — always guard if (!img?.asset) return ''. Treat every Sanity value as nullable at read time, even Studio-required() ones. See Components.
Never delete a Sanity singleton; never put secrets in .env
Deleting a singleton (sanity documents delete globalStyles) tombstones the ID and leaves createOrReplace as the only recovery path — always use the init script's createOrReplace, never delete-then-recreate. And secrets (SANITY_API_READ_TOKEN, PSI_API_KEY) live in .dev.vars / encrypted CF Secrets and are read via import { env } from 'cloudflare:workers' — never in .env, never committed. See Environment variables and Security.
astro build clobbers the running dev server's Vite cache
Running an astro build in website/ while astro dev is running 404s the dev server's pre-bundled gsap dep, breaking all client scripts. Restart astro dev after any build. astro check is safe. See Development setup.
For the broader agent-facing rules — conventions, the "no hardcoded copy" principle and its narrow exceptions, and the per-package checklists — start at Project overview and Monorepo architecture.
Where this lives
| What | Path (from repo root) |
|---|---|
| Code-tunables registry | website/tunables.md |
| Tunables keep-current rule | website/CLAUDE.md → "Code tunables" |
| Page-builder design rationale | Sanity Page Composition.md |
| Audit tool — original build spec (v1, client-side) | prompts/website-audit-tool-spec.md |
| Audit tool — signal-check rebuild brief (current architecture) | prompts/claude-code-prompt-audit-signals.md |
| Root agent guide (dispatcher) | CLAUDE.md |
| Website agent guide | website/CLAUDE.md |
| Studio agent guide | studio/CLAUDE.md |
Operator naming convention
Root-level runbooks and checklists are named _NAME.md (the leading underscore marks them as operator docs). Non-operator reference material lives under website/_reference/. The prompts/ directory specifically holds the build briefs above.