diff --git a/README.md b/README.md
index 5ebeb13..29da015 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
Source repository for [oss-singularity.io](https://oss-singularity.io/).
-Launch Pad v0 is a dependency-free static site shaped around the “Signal Observatory” visual direction: a precise cosmic shell, authentic project interfaces, and an intentionally human open-source voice. GitHub remains canonical; `dist/` is a reproducible, allowlisted production artifact.
+Launch Pad is a dependency-free static site shaped around the “Signal Observatory” visual direction: a precise cosmic shell, an adaptive pointer-reactive signal field, authentic project interfaces, and an intentionally human open-source voice. GitHub remains canonical; `dist/` is a reproducible, allowlisted production artifact.
## Development
@@ -12,7 +12,7 @@ Build and validate the complete site with:
./scripts/check-repository.sh
```
-The build uses authored HTML and CSS plus local optimized assets. It requires a POSIX shell and Python 3 for validation, installs no packages, executes no client-side JavaScript, and writes only to ignored `dist/`.
+The build uses authored HTML and CSS, one small dependency-free Canvas enhancement, and local optimized assets. It requires a POSIX shell and Python 3 for validation, installs no packages, makes no runtime network requests, and writes only to ignored `dist/`.
To preview the production tree locally after a successful build:
diff --git a/docs/product-requirements.md b/docs/product-requirements.md
index bf449de..3aa5aaa 100644
--- a/docs/product-requirements.md
+++ b/docs/product-requirements.md
@@ -73,7 +73,7 @@ Project documentation, releases, issue tracking, and installation instructions r
- Minimum 44×44 CSS-pixel primary interactive targets where practical
- Text contrast of at least 4.5:1 and non-text contrast of at least 3:1
- No information conveyed by color alone
-- Motion is decorative, modest, and removed for `prefers-reduced-motion: reduce`
+- Motion is decorative, modest, pointer-reactive, and removed for `prefers-reduced-motion: reduce`
- Layout remains usable at 320 CSS pixels, 200% zoom, and with long translated text even though v0 ships in English
## Performance budget
@@ -82,7 +82,7 @@ Measured against a clean production build before launch:
| Budget | Target |
| --- | --- |
-| Executable JavaScript | 0 bytes |
+| Executable JavaScript | under 8 KB, local and dependency-free |
| Initial HTML, uncompressed | under 35 KB |
| CSS, uncompressed | under 40 KB |
| Initial page transfer, excluding social preview | under 350 KB |
@@ -90,7 +90,7 @@ Measured against a clean production build before launch:
| Lighthouse mobile | Performance, Accessibility, Best Practices and SEO each at least 95 |
| Core stability goals | LCP under 2.0 s on a fast 4G profile; CLS under 0.05 |
-System fonts, responsive local images, explicit image dimensions, immutable asset caching, and zero client-side framework code support the budget.
+System fonts, responsive local images, explicit image dimensions, immutable asset caching, and zero client-side framework code support the budget. The optional reactive signal field makes no network requests, stores no state, stops in background tabs, caps display density, and is disabled when reduced motion is requested.
## SEO and social sharing
@@ -110,9 +110,9 @@ System fonts, responsive local images, explicit image dimensions, immutable asse
## Technology decision
-Use authored HTML and CSS with a small POSIX-shell build script that copies an explicit allowlist into `dist/` and verifies the required output. There are no package-manager dependencies.
+Use authored HTML, CSS and one small progressive-enhancement script with a POSIX-shell build that copies an explicit allowlist into `dist/` and verifies the required output. There are no package-manager dependencies.
-This is the simplest fit because the content is editorial, the interactions are links and anchors, and the verified default Stellar shell exposes no Node/npm runtime. PHP 8.2 is available but adds no value to v0. A static output is reproducible locally and in GitHub Actions, cheap to cache, easy to stage exactly, and easy to roll back.
+This remains the simplest fit because the content is editorial, the primary interactions are links and anchors, and the verified default Stellar shell exposes no Node/npm runtime. A small local Canvas enhancement adds visual reactivity without a framework, runtime service, tracking or third-party request. PHP 8.2 is available but adds no value. The static output remains reproducible locally and in GitHub Actions, cheap to cache, easy to stage exactly, and easy to roll back.
Reconsider a static-site generator only when repeated content, localization, or a larger editorial surface makes hand-authored pages demonstrably harder to maintain. Reconsider a server runtime only for a concrete server-side requirement.
diff --git a/scripts/build-site.sh b/scripts/build-site.sh
index 53af12f..932dca8 100755
--- a/scripts/build-site.sh
+++ b/scripts/build-site.sh
@@ -21,6 +21,7 @@ rm -rf -- "$output"
mkdir -p \
"$output/assets/brand" \
"$output/assets/projects" \
+ "$output/assets/scripts" \
"$output/assets/social" \
"$output/assets/styles"
@@ -44,6 +45,7 @@ copy_file assets/brand/oss-singularity-mark.svg assets/brand/oss-singularity-mar
copy_file assets/projects/pdrive-control-center-v080.webp assets/projects/pdrive-control-center-v080.webp
copy_file assets/projects/chatgpt-usage-v030.webp assets/projects/chatgpt-usage-v030.webp
copy_file assets/projects/nemo-action-bar.webp assets/projects/nemo-action-bar.webp
+copy_file assets/scripts/reactive-field.js assets/scripts/reactive-field.js
copy_file assets/social/oss-singularity-social-preview.png assets/social/oss-singularity-social-preview.png
copy_file assets/styles/site-v0.css assets/styles/site-v0.css
diff --git a/scripts/check-site.py b/scripts/check-site.py
index 2ca165a..2e31d1d 100755
--- a/scripts/check-site.py
+++ b/scripts/check-site.py
@@ -77,6 +77,7 @@ def main() -> int:
"assets/projects/chatgpt-usage-v030.webp",
"assets/projects/nemo-action-bar.webp",
"assets/projects/pdrive-control-center-v080.webp",
+ "assets/scripts/reactive-field.js",
"assets/social/oss-singularity-social-preview.png",
"assets/styles/site-v0.css",
}
@@ -84,9 +85,6 @@ def main() -> int:
if actual != required:
fail(f"build allowlist mismatch: missing={sorted(required - actual)} extra={sorted(actual - required)}")
- if any(path.suffix == ".js" for path in root.rglob("*")):
- fail("executable JavaScript is outside the v0 budget")
-
html_bytes = 0
for document in (root / "index.html", root / "404.html"):
parser = Document()
@@ -99,8 +97,9 @@ def main() -> int:
fail(f"{document.name} has no title")
if parser.h1_count != 1:
fail(f"{document.name} must contain exactly one h1")
- if parser.scripts:
- fail(f"{document.name} contains script elements")
+ expected_scripts = 1 if document.name == "index.html" else 0
+ if parser.scripts != expected_scripts:
+ fail(f"{document.name} must contain {expected_scripts} script elements")
if len(parser.ids) != len(set(parser.ids)):
fail(f"{document.name} contains duplicate ids")
@@ -147,6 +146,9 @@ def main() -> int:
css_bytes = (root / "assets/styles/site-v0.css").stat().st_size
if css_bytes > 40_000:
fail(f"CSS budget exceeded: {css_bytes} bytes")
+ script_bytes = (root / "assets/scripts/reactive-field.js").stat().st_size
+ if script_bytes > 8_000:
+ fail(f"JavaScript budget exceeded: {script_bytes} bytes")
for image in (root / "assets/projects").iterdir():
if image.stat().st_size > 180_000:
fail(f"project image budget exceeded: {image.name} is {image.stat().st_size} bytes")
@@ -163,7 +165,10 @@ def main() -> int:
ElementTree.parse(root / "sitemap.xml")
ElementTree.parse(root / "assets/brand/oss-singularity-mark.svg")
- print(f"site checks passed: html={html_bytes} css={css_bytes} transfer={transfer}")
+ print(
+ f"site checks passed: html={html_bytes} css={css_bytes} "
+ f"js={script_bytes} transfer={transfer}"
+ )
return 0
diff --git a/site/.htaccess b/site/.htaccess
index 6eaed4c..781057a 100644
--- a/site/.htaccess
+++ b/site/.htaccess
@@ -10,7 +10,7 @@ ErrorDocument 404 /404.html