Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bf715c9cf0 | ||
|
|
bf46d113f3 | ||
|
|
fde7514128 | ||
|
|
1e3ffc1bce | ||
|
|
001f36853a | ||
|
|
add14700f6 | ||
|
|
8e446ce5ca | ||
|
|
12fdb007f6 | ||
|
|
802c92f929 | ||
|
|
e2dac179e6 | ||
|
|
a9f6ec96b5 | ||
|
|
03ac12dfe4 | ||
|
|
9b8fe5c3a1 | ||
|
|
ff284f77fb | ||
|
|
d45a04381d | ||
|
|
8e846a9a36 | ||
|
|
6dfd19ea28 | ||
|
|
852f6fc2e9 | ||
|
|
13d62b98c0 | ||
|
|
07af33a070 | ||
|
|
50395d69d6 | ||
|
|
285aea8f2d | ||
|
|
39e2e6ab40 | ||
|
|
9d7db3290a | ||
|
|
ddbe5645e9 | ||
|
|
a1ca023ee8 | ||
|
|
2b3138d9d0 | ||
|
|
1c678a71fa | ||
|
|
6699a2fc44 | ||
|
|
964377765a | ||
|
|
5cfbb8d98d | ||
|
|
7633794d41 | ||
|
|
01c16ccab3 | ||
|
|
da77161fcc | ||
|
|
95804695fe | ||
|
|
2ab3300fd9 | ||
|
|
f06c644b36 | ||
|
|
cd251fafa8 |
No files matched your search
@@ -1,3 +1,4 @@
|
||||
# The contributor harness hashes provenance inputs byte-for-byte. Keep text
|
||||
# files in this boundary on LF even when Windows enables core.autocrlf.
|
||||
harness/ruview/** text=auto eol=lf
|
||||
harness/homecore/** text=auto eol=lf
|
||||
@@ -32,6 +32,9 @@ jobs:
|
||||
- name: Validate the manifest is well-formed JSON
|
||||
run: python -c "import json; json.load(open('scripts/fix-markers.json')); print('manifest OK')"
|
||||
|
||||
- name: Run unit tests for fix-marker checker
|
||||
run: python -m unittest scripts/tests/test_check_fix_markers.py
|
||||
|
||||
- name: Check fix markers
|
||||
run: python scripts/check_fix_markers.py
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
name: nvsim-server → ghcr.io
|
||||
|
||||
# Builds and publishes the nvsim-server Docker image to ghcr.io on:
|
||||
# Builds and publishes the nvsim-server Docker image under the repository
|
||||
# owner (ghcr.io/<owner>/nvsim-server) on:
|
||||
# - push to main affecting nvsim-server or nvsim
|
||||
# - tag push matching nvsim-server-v*
|
||||
# - manual workflow_dispatch
|
||||
@@ -31,6 +32,10 @@ jobs:
|
||||
|
||||
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f
|
||||
|
||||
# ghcr.io is always the target; there is no Docker Hub leg here.
|
||||
# The image path is derived from the repository owner because a fork
|
||||
# cannot push to the upstream's `ruvnet` namespace — that push
|
||||
# fails with `denied: The requested installation does not exist`.
|
||||
- uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9
|
||||
with:
|
||||
registry: ghcr.io
|
||||
@@ -41,7 +46,7 @@ jobs:
|
||||
id: meta
|
||||
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
|
||||
with:
|
||||
images: ghcr.io/ruvnet/nvsim-server
|
||||
images: ghcr.io/${{ github.repository_owner }}/nvsim-server
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=tag
|
||||
@@ -61,11 +66,16 @@ jobs:
|
||||
platforms: linux/amd64
|
||||
|
||||
- name: Smoke-test the image
|
||||
env:
|
||||
IMAGE: ghcr.io/${{ github.repository_owner }}/nvsim-server
|
||||
run: |
|
||||
docker pull ghcr.io/ruvnet/nvsim-server:sha-${GITHUB_SHA::7} || \
|
||||
docker pull ghcr.io/ruvnet/nvsim-server:latest
|
||||
# `type=sha,format=short` is emitted on every event, so this is
|
||||
# the one tag that always exists after a successful push. The
|
||||
# old `|| latest` fallback hid a failed push and only worked on
|
||||
# the default branch, so tag builds never reached this step.
|
||||
docker pull "${IMAGE}:sha-${GITHUB_SHA::7}"
|
||||
docker run --rm -d --name nvsim-test -p 7878:7878 \
|
||||
ghcr.io/ruvnet/nvsim-server:latest
|
||||
"${IMAGE}:sha-${GITHUB_SHA::7}"
|
||||
sleep 4
|
||||
curl -fsS http://localhost:7878/api/health
|
||||
docker stop nvsim-test
|
||||
@@ -0,0 +1,44 @@
|
||||
name: W1700K RF diagnostics
|
||||
on:
|
||||
pull_request:
|
||||
paths: ['tools/w1700k/**', 'docs/rf-diagnostics/**', '.github/workflows/rf-diagnostics.yml']
|
||||
push:
|
||||
branches: [main]
|
||||
paths: ['tools/w1700k/**', 'docs/rf-diagnostics/**', '.github/workflows/rf-diagnostics.yml']
|
||||
workflow_dispatch:
|
||||
permissions:
|
||||
contents: read
|
||||
concurrency:
|
||||
group: rf-diagnostics-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
jobs:
|
||||
verify:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
|
||||
- name: Bridge ABI and failure tests
|
||||
run: make -C tools/w1700k check
|
||||
- name: Viewer validation tests
|
||||
run: |
|
||||
node --test docs/rf-diagnostics/model.test.cjs
|
||||
node --check docs/rf-diagnostics/app.js
|
||||
deploy:
|
||||
if: github.ref == 'refs/heads/main' && github.event_name != 'pull_request'
|
||||
needs: verify
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262
|
||||
- name: Stage static viewer only
|
||||
run: |
|
||||
mkdir -p _site
|
||||
cp docs/rf-diagnostics/index.html docs/rf-diagnostics/model.js docs/rf-diagnostics/app.js docs/rf-diagnostics/style.css _site/
|
||||
- name: Preserve existing Pages content
|
||||
uses: peaceiris/actions-gh-pages@84c30a85c19949d7eee79c4ff27748b70285e453
|
||||
with:
|
||||
github_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
publish_dir: ./_site
|
||||
destination_dir: rf-diagnostics
|
||||
keep_files: true
|
||||
commit_message: 'deploy(rf-diagnostics): ${{ github.sha }}'
|
||||
@@ -1,12 +1,16 @@
|
||||
name: wifi-densepose sensing-server → Docker Hub + ghcr.io
|
||||
|
||||
# Build + publish the `wifi-densepose` sensing-server image to both Docker Hub
|
||||
# (`ruvnet/wifi-densepose`) and ghcr.io (`ghcr.io/ruvnet/wifi-densepose`) on:
|
||||
# Build + publish the `wifi-densepose` sensing-server image under the repository
|
||||
# owner (`docker.io/<owner>/wifi-densepose` and `ghcr.io/<owner>/wifi-densepose`)
|
||||
# on:
|
||||
# - push to main affecting the Dockerfile, the server crate, the UI assets,
|
||||
# or this workflow itself,
|
||||
# - tag push matching v* (release builds),
|
||||
# - manual workflow_dispatch.
|
||||
#
|
||||
# Docker Hub is only published when DOCKERHUB_USERNAME/DOCKERHUB_TOKEN exist;
|
||||
# ghcr.io always publishes.
|
||||
#
|
||||
# Closes #520 and #514: the stale `:latest` is rebuilt and pushed automatically
|
||||
# whenever the surface that produces it changes, and the Dockerfile fails the
|
||||
# build if the observatory/pose-fusion UI assets ever go missing again.
|
||||
@@ -61,17 +65,42 @@ jobs:
|
||||
|
||||
- uses: docker/setup-buildx-action@8d2750c68a42422c14e847fe6c8ac0403b4cbd6f
|
||||
|
||||
- name: Log in to Docker Hub
|
||||
# Bypassing docker/login-action@v3: the action kept emitting
|
||||
# "malformed HTTP Authorization header" against a known-good
|
||||
# dckr_pat_* token (verified by direct curl against the Hub API).
|
||||
# `docker login --password-stdin` is the documented credential
|
||||
# path and avoids whatever encoding step the action injects.
|
||||
# Publish under the repository owner instead of a hard-coded `ruvnet`
|
||||
# namespace: a fork cannot push to the upstream's Docker Hub or ghcr.io
|
||||
# namespaces, and `github.repository_owner` resolves correctly for both
|
||||
# the upstream and every fork.
|
||||
#
|
||||
# Docker Hub is optional. The login was previously hard-required, so a
|
||||
# repository without DOCKERHUB_USERNAME/DOCKERHUB_TOKEN failed at
|
||||
# `docker login` ("Must provide --username with --password-stdin") before
|
||||
# it ever built anything. ghcr.io needs no secret, so it stays the
|
||||
# always-on target.
|
||||
#
|
||||
# Bypassing docker/login-action@v3: the action kept emitting
|
||||
# "malformed HTTP Authorization header" against a known-good
|
||||
# dckr_pat_* token (verified by direct curl against the Hub API).
|
||||
# `docker login --password-stdin` is the documented credential
|
||||
# path and avoids whatever encoding step the action injects.
|
||||
- name: Resolve publish targets
|
||||
id: targets
|
||||
env:
|
||||
DH_USER: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
DH_TOKEN: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
OWNER: ${{ github.repository_owner }}
|
||||
run: |
|
||||
printf '%s' "$DH_TOKEN" | docker login docker.io -u "$DH_USER" --password-stdin
|
||||
set -uo pipefail
|
||||
image="wifi-densepose"
|
||||
images="ghcr.io/${OWNER}/${image}"
|
||||
if [ -n "$DH_USER" ] && [ -n "$DH_TOKEN" ]; then
|
||||
images="docker.io/${OWNER}/${image}"$'\n'"${images}"
|
||||
printf '%s' "$DH_TOKEN" | docker login docker.io -u "$DH_USER" --password-stdin
|
||||
else
|
||||
echo "::warning::DOCKERHUB_USERNAME/DOCKERHUB_TOKEN are not configured; this run publishes to ghcr.io only."
|
||||
fi
|
||||
echo "images<<EOF" >> "$GITHUB_OUTPUT"
|
||||
echo "$images" >> "$GITHUB_OUTPUT"
|
||||
echo "EOF" >> "$GITHUB_OUTPUT"
|
||||
echo "ghcr_image=ghcr.io/${OWNER}/${image}" >> "$GITHUB_OUTPUT"
|
||||
|
||||
- name: Log in to ghcr.io
|
||||
uses: docker/login-action@c94ce9fb468520275223c153574b00df6fe4bcc9
|
||||
@@ -84,9 +113,7 @@ jobs:
|
||||
id: meta
|
||||
uses: docker/metadata-action@dc802804100637a589fabce1cb79ff13a1411302
|
||||
with:
|
||||
images: |
|
||||
docker.io/ruvnet/wifi-densepose
|
||||
ghcr.io/ruvnet/wifi-densepose
|
||||
images: ${{ steps.targets.outputs.images }}
|
||||
tags: |
|
||||
type=ref,event=branch
|
||||
type=ref,event=tag
|
||||
@@ -123,7 +150,7 @@ jobs:
|
||||
- name: Smoke-test image assets + LAN-mode HTTP
|
||||
run: |
|
||||
set -euo pipefail
|
||||
IMAGE="ghcr.io/ruvnet/wifi-densepose:sha-${GITHUB_SHA::7}"
|
||||
IMAGE="${{ steps.targets.outputs.ghcr_image }}:sha-${GITHUB_SHA::7}"
|
||||
docker pull "$IMAGE"
|
||||
docker run --rm --entrypoint sh "$IMAGE" -c \
|
||||
'ls /app/ui/observatory.html /app/ui/pose-fusion.html /app/ui/index.html /app/ui/viz.html >/dev/null'
|
||||
@@ -147,7 +174,7 @@ jobs:
|
||||
- name: Smoke-test the bearer-token auth path
|
||||
run: |
|
||||
set -euo pipefail
|
||||
IMAGE="ghcr.io/ruvnet/wifi-densepose:sha-${GITHUB_SHA::7}"
|
||||
IMAGE="${{ steps.targets.outputs.ghcr_image }}:sha-${GITHUB_SHA::7}"
|
||||
docker run -d --name auth \
|
||||
-p 3000:3000 \
|
||||
-e CSI_SOURCE=simulated \
|
||||
|
||||
@@ -1,22 +1,7 @@
|
||||
# π RuView
|
||||
|
||||
[](docs/user-guide.md)
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center" width="33%"><a href="docs/user-guide.md"><img src="assets/readme/nav-start.svg" width="180" alt="Start here" /></a><br/><a href="docs/user-guide.md">Start here</a></td>
|
||||
<td align="center" width="33%"><a href="firmware/esp32-csi-node/README.md"><img src="assets/readme/nav-hardware.svg" width="180" alt="Hardware" /></a><br/><a href="firmware/esp32-csi-node/README.md">Hardware</a></td>
|
||||
<td align="center" width="33%"><a href="https://huggingface.co/ruvnet/wifi-densepose-pretrained"><img src="assets/readme/nav-models.svg" width="180" alt="Models" /></a><br/><a href="https://huggingface.co/ruvnet/wifi-densepose-pretrained">Models</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" width="33%"><a href="harness/ruview/README.md"><img src="assets/readme/nav-agents.svg" width="180" alt="AI toolkit" /></a><br/><a href="harness/ruview/README.md">AI toolkit</a></td>
|
||||
<td align="center" width="33%"><a href="docs/integrations/home-assistant.md"><img src="assets/readme/nav-home.svg" width="180" alt="Smart home" /></a><br/><a href="docs/integrations/home-assistant.md">Smart home</a></td>
|
||||
<td align="center" width="33%"><a href="https://cognitum.one/seed"><img src="assets/readme/nav-seed.svg" width="180" alt="Cognitum Seed" /></a><br/><a href="https://cognitum.one/seed">Cognitum Seed</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<details>
|
||||
<summary>Watch the original RuView sensing demo</summary>
|
||||
> **W1700K RF diagnostics:** [receiver bridge](tools/w1700k/README.md) · [browser viewer](https://ruvnet.github.io/RuView/rf-diagnostics/) · [integration design](docs/adr/ADR-419-w1700k-rf-diagnostics.md).
|
||||
> Reads IQ locally and exports bounded quality summaries to RuField. Synthetic contract tests are separate from hardware validation. RF diagnostics only; no CSI extraction or presence detection.
|
||||
|
||||
<p align="center">
|
||||
<a href="https://cognitum.one/seed">
|
||||
@@ -24,7 +9,20 @@
|
||||
</a>
|
||||
</p>
|
||||
|
||||
</details>
|
||||
[](docs/user-guide.md)
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td align="center" width="33%"><a href="docs/user-guide.md"><img src="assets/readme/nav-start-spatial-v4.svg" width="180" alt="Start here" /></a><br/><a href="docs/user-guide.md">Start here</a></td>
|
||||
<td align="center" width="33%"><a href="firmware/esp32-csi-node/README.md"><img src="assets/readme/nav-hardware-spatial-v4.svg" width="180" alt="Hardware" /></a><br/><a href="firmware/esp32-csi-node/README.md">Hardware</a></td>
|
||||
<td align="center" width="33%"><a href="https://huggingface.co/ruvnet/wifi-densepose-pretrained"><img src="assets/readme/nav-models-spatial-v4.svg" width="180" alt="Models" /></a><br/><a href="https://huggingface.co/ruvnet/wifi-densepose-pretrained">Models</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center" width="33%"><a href="harness/ruview/README.md"><img src="assets/readme/nav-agents-spatial-v4.svg" width="180" alt="AI toolkit" /></a><br/><a href="harness/ruview/README.md">AI toolkit</a></td>
|
||||
<td align="center" width="33%"><a href="docs/integrations/home-assistant.md"><img src="assets/readme/nav-home-spatial-v4.svg" width="180" alt="Smart home" /></a><br/><a href="docs/integrations/home-assistant.md">Smart home</a></td>
|
||||
<td align="center" width="33%"><a href="https://cognitum.one/seed"><img src="assets/readme/nav-seed-spatial-v4.svg" width="180" alt="Cognitum Seed" /></a><br/><a href="https://cognitum.one/seed">Cognitum Seed</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<p align="center">
|
||||
<a href="https://ruos.cognitum.one">
|
||||
@@ -47,7 +45,7 @@ Works natively with the four major smart-home ecosystems: **[Home Assistant](doc
|
||||
|
||||
Every WiFi router already fills your space with radio waves. When people move, breathe, or even sit still, they disturb those waves in measurable ways. RuView captures these disturbances using Channel State Information (CSI) from low-cost ESP32 sensors and turns them into actionable data: who's there, what they're doing, and whether they're okay.
|
||||
|
||||
[](docs/user-guide.md)
|
||||
[](docs/user-guide.md)
|
||||
|
||||
**What it senses:**
|
||||
- **Presence and occupancy** — detect people through walls, count them, track entries and exits
|
||||
@@ -68,16 +66,16 @@ Every WiFi router already fills your space with radio waves. When people move, b
|
||||
<!-- ruview-capability-cards -->
|
||||
<table>
|
||||
<tr>
|
||||
<td width="50%"><a href="docs/user-guide.md"><img src="assets/readme/feature-presence.svg" width="580" alt="Presence and motion: open documentation and current limitations" /></a><br/><a href="docs/user-guide.md">Presence and motion</a></td>
|
||||
<td width="50%"><a href="v2/crates/wifi-densepose-signal/README.md"><img src="assets/readme/feature-vitals.svg" width="580" alt="Breathing and vital signals: open documentation and current limitations" /></a><br/><a href="v2/crates/wifi-densepose-signal/README.md">Breathing and vital signals</a></td>
|
||||
<td width="50%"><a href="docs/user-guide.md"><img src="assets/readme/feature-presence-spatial-v4.svg" width="580" alt="Presence and motion: open documentation and current limitations" /></a><br/><a href="docs/user-guide.md">Presence and motion</a></td>
|
||||
<td width="50%"><a href="v2/crates/wifi-densepose-signal/README.md"><img src="assets/readme/feature-vitals-spatial-v4.svg" width="580" alt="Breathing and vital signals: open documentation and current limitations" /></a><br/><a href="v2/crates/wifi-densepose-signal/README.md">Breathing and vital signals</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%"><a href="docs/benchmarks/pose-estimation-cog.md"><img src="assets/readme/feature-pose.svg" width="580" alt="Pose research and limitations: open documentation and current limitations" /></a><br/><a href="docs/benchmarks/pose-estimation-cog.md">Pose research and limitations</a></td>
|
||||
<td width="50%"><a href="firmware/esp32-csi-node/README.md"><img src="assets/readme/feature-edge.svg" width="580" alt="Edge sensor mesh: open documentation and current limitations" /></a><br/><a href="firmware/esp32-csi-node/README.md">Edge sensor mesh</a></td>
|
||||
<td width="50%"><a href="docs/benchmarks/pose-estimation-cog.md"><img src="assets/readme/feature-pose-spatial-v6.svg" width="580" alt="Pose research and limitations: open documentation and current limitations" /></a><br/><a href="docs/benchmarks/pose-estimation-cog.md">Pose research and limitations</a></td>
|
||||
<td width="50%"><a href="firmware/esp32-csi-node/README.md"><img src="assets/readme/feature-edge-spatial-v4.svg" width="580" alt="Edge sensor mesh: open documentation and current limitations" /></a><br/><a href="firmware/esp32-csi-node/README.md">Edge sensor mesh</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="50%"><a href="docs/integrations/home-assistant.md"><img src="assets/readme/feature-automation.svg" width="580" alt="Local automation: open documentation and current limitations" /></a><br/><a href="docs/integrations/home-assistant.md">Local automation</a></td>
|
||||
<td width="50%"><a href="harness/ruview/README.md"><img src="assets/readme/feature-agents.svg" width="580" alt="Guided AI operator: open documentation and current limitations" /></a><br/><a href="harness/ruview/README.md">Guided AI operator</a></td>
|
||||
<td width="50%"><a href="docs/integrations/home-assistant.md"><img src="assets/readme/feature-automation-spatial-v4.svg" width="580" alt="Local automation: open documentation and current limitations" /></a><br/><a href="docs/integrations/home-assistant.md">Local automation</a></td>
|
||||
<td width="50%"><a href="harness/ruview/README.md"><img src="assets/readme/feature-agents-spatial-v4.svg" width="580" alt="Guided AI operator: open documentation and current limitations" /></a><br/><a href="harness/ruview/README.md">Guided AI operator</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
@@ -301,7 +299,7 @@ The separate **17-keypoint pose-estimation model** is now published at [`ruvnet/
|
||||
|
||||
### Results & proof
|
||||
|
||||
[](docs/benchmarks/pose-estimation-cog.md)
|
||||
[](docs/benchmarks/pose-estimation-cog.md)
|
||||
|
||||
See the measured benchmarks, witness records, and one-command reproducibility check.
|
||||
|
||||
@@ -357,7 +355,7 @@ number. The path to a first *reproducible* on-device baseline (PCK@20 ≥ 35%) i
|
||||
|
||||
## 🧩 Edge Module Catalog
|
||||
|
||||
[](firmware/esp32-csi-node/README.md)
|
||||
[](firmware/esp32-csi-node/README.md)
|
||||
|
||||
Add signed modules for health, security, buildings, industry, research, AI, and more.
|
||||
|
||||
@@ -533,7 +531,7 @@ Browse and install modules at [seed.cognitum.one/store](https://seed.cognitum.on
|
||||
|
||||
## 🔬 How It Works
|
||||
|
||||
[](docs/user-guide.md)
|
||||
[](docs/user-guide.md)
|
||||
|
||||
WiFi routers flood every room with radio waves. When a person moves — or even breathes — those waves scatter differently. WiFi DensePose reads that scattering pattern and reconstructs what happened:
|
||||
|
||||
@@ -659,7 +657,7 @@ These scenarios exploit WiFi's ability to penetrate solid materials — concrete
|
||||
|
||||
## 🧠 Self-Learning WiFi AI
|
||||
|
||||
[](docs/user-guide.md)
|
||||
[](docs/user-guide.md)
|
||||
|
||||
Learn compact room fingerprints from raw CSI and adapt the model to each environment.
|
||||
|
||||
@@ -790,7 +788,7 @@ For the portable RuView MetaHarness, use `npx @ruvnet/ruview@0.9.1`. The quick c
|
||||
|
||||
## 📖 Documentation
|
||||
|
||||
[](docs/user-guide.md)
|
||||
[](docs/user-guide.md)
|
||||
|
||||
Start with the user, build, and calibration guides; expand for the full reference map.
|
||||
|
||||
@@ -833,7 +831,7 @@ Start with the user, build, and calibration guides; expand for the full referenc
|
||||
>
|
||||
> Contributions and bug reports welcome at [Issues](https://github.com/ruvnet/RuView/issues).
|
||||
|
||||
[](https://cognitum.one)
|
||||
[](https://cognitum.one)
|
||||
|
||||
## 📄 License
|
||||
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="304" viewBox="0 0 1200 304" role="img" aria-labelledby="title desc"><title id="title">Explore Cognitum One</title><desc id="desc">Ambient Intelligence at the edge of the physical world. Open cognitum.one.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="303" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="44" y="42" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">COGNITUM ONE</text><text x="44" y="99" font-size="42" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Ambient Intelligence.</text><text x="44" y="147" font-size="25" fill="#78cfff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >At the edge of the physical world.</text><text x="44" y="190" font-size="16" fill="#b0c2db" text-anchor="start" font-weight="500" >Explore the platform behind the ecosystem.</text><rect x="44" y="224" width="256" height="46" rx="11" fill="url(#accent)"/><text x="65" y="254" font-size="18" fill="#071124" text-anchor="start" font-weight="500" >Explore Cognitum</text><path d="M263 247H284M277 240L284 247L277 254" fill="none" stroke="#071124" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="947" cy="151" r="173" fill="url(#halo)"/><g transform="translate(945 173) scale(1.55)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><g transform="translate(945 145) scale(1.65)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M0 -38L31 -20V18L0 38L-31 18V-20ZM-31 -20L0 -1L31 -20M0 -1V38" fill="none" stroke="#78cfff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M-15 1L-5 11L17 -12" fill="none" stroke="#438fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/></g></g><ellipse cx="945" cy="150" rx="140" ry="30" transform="rotate(-25 945 150)" fill="none" stroke="#437cb2" stroke-dasharray="5 18" class="flow"/><ellipse cx="945" cy="150" rx="160" ry="43" transform="rotate(-25 945 150)" fill="none" stroke="#437cb2" stroke-dasharray="5 18" class="flow"/><ellipse cx="945" cy="150" rx="180" ry="56" transform="rotate(-25 945 150)" fill="none" stroke="#437cb2" stroke-dasharray="5 18" class="flow"/></svg>
|
||||
|
After Width: | Height: | Size: 5.0 KiB |
@@ -0,0 +1,22 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="304" viewBox="0 0 1200 304" role="img" aria-labelledby="title desc"><title id="title">Explore Cognitum One</title><desc id="desc">Ambient Intelligence at the edge of the physical world. Open cognitum.one.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{stroke-dasharray:3 14;animation:flow 8s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
|
||||
.layer{animation:layer 6s ease-in-out infinite}
|
||||
@keyframes layer{50%{transform:translateY(-5px)}}
|
||||
|
||||
|
||||
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="303" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="44" y="42" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">COGNITUM ONE</text><text x="44" y="99" font-size="42" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Ambient Intelligence.</text><text x="44" y="147" font-size="25" fill="#78cfff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >At the edge of the physical world.</text><text x="44" y="190" font-size="16" fill="#b0c2db" text-anchor="start" font-weight="500" >Explore the platform behind the ecosystem.</text><rect x="44" y="224" width="256" height="46" rx="11" fill="url(#accent)"/><text x="65" y="254" font-size="18" fill="#071124" text-anchor="start" font-weight="500" >Explore Cognitum</text><path d="M263 247H284M277 240L284 247L277 254" fill="none" stroke="#071124" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="947" cy="151" r="173" fill="url(#halo)"/><g transform="translate(945 173) scale(1.55)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><g transform="translate(945 145) scale(1.65)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><g class="layer" style="animation-delay:-0.0s"><path d="M-38 -23L0 -42L38 -23L0 -4Z" fill="#102749" stroke="#78cfff"/><path d="M-38 -23V-16L0 3L38 -16V-23M0 -4V3" fill="#09172c" stroke="#315685"/></g><g class="layer" style="animation-delay:-0.7s"><path d="M-38 -10L0 -29L38 -10L0 9Z" fill="#102749" stroke="#438fff"/><path d="M-38 -10V-3L0 16L38 -3V-10M0 9V16" fill="#09172c" stroke="#315685"/></g><g class="layer" style="animation-delay:-1.4s"><path d="M-38 3L0 -16L38 3L0 22Z" fill="#102749" stroke="#438fff"/><path d="M-38 3V10L0 29L38 10V3M0 22V29" fill="#09172c" stroke="#315685"/></g><path d="M-12 -31L0 -37L12 -31L0 -25Z" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M-26 22V39" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M0 22V39" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M26 22V39" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/></g></g><ellipse cx="945" cy="150" rx="140" ry="30" transform="rotate(-25 945 150)" fill="none" stroke="#437cb2" stroke-dasharray="5 18" class="flow"/><ellipse cx="945" cy="150" rx="160" ry="43" transform="rotate(-25 945 150)" fill="none" stroke="#437cb2" stroke-dasharray="5 18" class="flow"/><ellipse cx="945" cy="150" rx="180" ry="56" transform="rotate(-25 945 150)" fill="none" stroke="#437cb2" stroke-dasharray="5 18" class="flow"/><path d="M24 1H1176" stroke="url(#accent)" opacity=".55"/><path d="M24 303H1176" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="304" viewBox="0 0 1200 304" role="img" aria-labelledby="title desc"><title id="title">Explore Cognitum One</title><desc id="desc">Ambient Intelligence at the edge of the physical world. Open cognitum.one.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="1199" height="303" rx="18" fill="url(#wash)" stroke="#245360"/><text x="44" y="42" font-size="12" fill="#72eee0" text-anchor="start" class="mono">COGNITUM ONE</text><text x="44" y="99" font-size="42" fill="#ecfafb" text-anchor="start" >Ambient Intelligence.</text><text x="44" y="147" font-size="25" fill="#72eee0" text-anchor="start" >At the edge of the physical world.</text><text x="44" y="190" font-size="16" fill="#9abac5" text-anchor="start" >Explore the platform behind the ecosystem.</text><rect x="44" y="224" width="256" height="46" rx="11" fill="url(#accent)"/><text x="65" y="254" font-size="18" fill="#041117" text-anchor="start" >Explore Cognitum</text><path d="M263 247H284M277 240L284 247L277 254" fill="none" stroke="#041117" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="947" cy="151" r="173" fill="url(#halo)"/><g transform="translate(945 150) scale(2.4)"><path d="M0 -38L31 -20V18L0 38L-31 18V-20ZM-31 -20L0 -1L31 -20M0 -1V38" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M-15 1L-5 11L17 -12" fill="none" stroke="#70b6ff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/></g><ellipse cx="945" cy="150" rx="140" ry="30" transform="rotate(-25 945 150)" fill="none" stroke="#3c8897" stroke-dasharray="5 18" class="flow"/><ellipse cx="945" cy="150" rx="160" ry="43" transform="rotate(-25 945 150)" fill="none" stroke="#3c8897" stroke-dasharray="5 18" class="flow"/><ellipse cx="945" cy="150" rx="180" ry="56" transform="rotate(-25 945 150)" fill="none" stroke="#3c8897" stroke-dasharray="5 18" class="flow"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="304" viewBox="0 0 1200 304" role="img" aria-labelledby="title desc"><title id="title">Explore Cognitum One</title><desc id="desc">Ambient Intelligence at the edge of the physical world. Open cognitum.one.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="303" rx="18" fill="url(#wash)" stroke="#245360"/><text x="44" y="42" font-size="12" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">COGNITUM ONE</text><text x="44" y="99" font-size="42" fill="#ecfafb" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Ambient Intelligence.</text><text x="44" y="147" font-size="25" fill="#72eee0" text-anchor="start" font-weight="650" letter-spacing="-0.6" >At the edge of the physical world.</text><text x="44" y="190" font-size="16" fill="#9abac5" text-anchor="start" font-weight="500" >Explore the platform behind the ecosystem.</text><rect x="44" y="224" width="256" height="46" rx="11" fill="url(#accent)"/><text x="65" y="254" font-size="18" fill="#041117" text-anchor="start" font-weight="500" >Explore Cognitum</text><path d="M263 247H284M277 240L284 247L277 254" fill="none" stroke="#041117" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="947" cy="151" r="173" fill="url(#halo)"/><g transform="translate(945 173) scale(1.55)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0a2733" stroke="#286171"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#366d80"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#5fe3d8" fill-opacity=".12" stroke="#72eee0" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#70b6ff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#91f6df"/></g></g><g transform="translate(945 145) scale(1.65)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M0 -38L31 -20V18L0 38L-31 18V-20ZM-31 -20L0 -1L31 -20M0 -1V38" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M-15 1L-5 11L17 -12" fill="none" stroke="#70b6ff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/></g></g><ellipse cx="945" cy="150" rx="140" ry="30" transform="rotate(-25 945 150)" fill="none" stroke="#3c8897" stroke-dasharray="5 18" class="flow"/><ellipse cx="945" cy="150" rx="160" ry="43" transform="rotate(-25 945 150)" fill="none" stroke="#3c8897" stroke-dasharray="5 18" class="flow"/><ellipse cx="945" cy="150" rx="180" ry="56" transform="rotate(-25 945 150)" fill="none" stroke="#3c8897" stroke-dasharray="5 18" class="flow"/></svg>
|
||||
|
Before Width: | Height: | Size: 3.0 KiB After Width: | Height: | Size: 4.7 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">A guided AI operator</title><desc id="desc">Source-cited setup and verification. CLI, MCP and agent workflows. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="30" y="39" font-size="11" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">RUVIEW / 06</text><text x="30" y="94" font-size="29" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >A guided AI operator</text><text x="30" y="136" font-size="16" fill="#b0c2db" text-anchor="start" font-weight="500" >Source-cited setup and verification.</text><text x="30" y="164" font-size="16" fill="#b0c2db" text-anchor="start" font-weight="500" >CLI, MCP and agent workflows.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 161) scale(0.53)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><g transform="translate(474 155) scale(0.68)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M0 0L0 -33" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="0" cy="-33" r="7" stroke="#78cfff" fill="none" class="breathe"/><path d="M0 0L-35 23" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="-35" cy="23" r="7" stroke="#78cfff" fill="none" class="breathe"/><path d="M0 0L35 23" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="35" cy="23" r="7" stroke="#78cfff" fill="none" class="breathe"/><circle cx="0" cy="0" r="15" stroke="#78cfff" fill="none" /><path d="M-6 0L-1 5L8 -6" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></g></g><path d="M30 204H550" fill="none" stroke="#2d4e77" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#78cfff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">TOOLKIT GUIDE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
After Width: | Height: | Size: 5.2 KiB |
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">A guided AI operator</title><desc id="desc">Source-cited setup and verification. CLI, MCP and agent workflows. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
|
||||
|
||||
|
||||
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="30" y="39" font-size="11" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">RUVIEW / 06</text><text x="30" y="89" font-size="25" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >A guided AI operator</text><text x="30" y="126" font-size="14" fill="#b0c2db" text-anchor="start" font-weight="500" >Source-cited setup and verification.</text><text x="30" y="151" font-size="14" fill="#b0c2db" text-anchor="start" font-weight="500" >CLI, MCP and agent workflows.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 144) scale(1.12)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><path d="M0 0L0 -36" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M0 -43l7 4v8l-7 4l-7 -4v-8Z" fill="#102749" stroke="#78cfff" class="breathe"/><path d="M0 0L-38 -10" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M-38 -17l7 4v8l-7 4l-7 -4v-8Z" fill="#102749" stroke="#78cfff" class="breathe"/><path d="M0 0L-23 31" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M-23 24l7 4v8l-7 4l-7 -4v-8Z" fill="#102749" stroke="#78cfff" class="breathe"/><path d="M0 0L30 28" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M30 21l7 4v8l-7 4l-7 -4v-8Z" fill="#102749" stroke="#78cfff" class="breathe"/><path d="M0 0L39 -17" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M39 -24l7 4v8l-7 4l-7 -4v-8Z" fill="#102749" stroke="#78cfff" class="breathe"/><circle cx="0" cy="0" r="15" stroke="#b8f36c" fill="none" /><path d="M-6 0L-1 5L8 -6" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></g></g><path d="M30 204H550" fill="none" stroke="#2d4e77" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#78cfff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">TOOLKIT GUIDE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M24 1H556" stroke="url(#accent)" opacity=".55"/><path d="M24 275H556" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">A guided AI operator</title><desc id="desc">Source-cited setup and verification. CLI, MCP and agent workflows. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#245360"/><text x="30" y="39" font-size="11" fill="#72eee0" text-anchor="start" class="mono">RUVIEW / 06</text><text x="30" y="94" font-size="29" fill="#ecfafb" text-anchor="start" >A guided AI operator</text><text x="30" y="136" font-size="16" fill="#9abac5" text-anchor="start" >Source-cited setup and verification.</text><text x="30" y="164" font-size="16" fill="#9abac5" text-anchor="start" >CLI, MCP and agent workflows.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(474 168) scale(0.83)"><path d="M0 0L0 -33" fill="none" stroke="#70b6ff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="0" cy="-33" r="7" stroke="#72eee0" fill="none" class="breathe"/><path d="M0 0L-35 23" fill="none" stroke="#70b6ff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="-35" cy="23" r="7" stroke="#72eee0" fill="none" class="breathe"/><path d="M0 0L35 23" fill="none" stroke="#70b6ff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="35" cy="23" r="7" stroke="#72eee0" fill="none" class="breathe"/><circle cx="0" cy="0" r="15" stroke="#72eee0" fill="none" /><path d="M-6 0L-1 5L8 -6" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></g><path d="M30 204H550" fill="none" stroke="#245360" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#72eee0" text-anchor="start" class="mono">TOOLKIT GUIDE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">A guided AI operator</title><desc id="desc">Source-cited setup and verification. CLI, MCP and agent workflows. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#245360"/><text x="30" y="39" font-size="11" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">RUVIEW / 06</text><text x="30" y="94" font-size="29" fill="#ecfafb" text-anchor="start" font-weight="650" letter-spacing="-0.6" >A guided AI operator</text><text x="30" y="136" font-size="16" fill="#9abac5" text-anchor="start" font-weight="500" >Source-cited setup and verification.</text><text x="30" y="164" font-size="16" fill="#9abac5" text-anchor="start" font-weight="500" >CLI, MCP and agent workflows.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 161) scale(0.53)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0a2733" stroke="#286171"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#366d80"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#5fe3d8" fill-opacity=".12" stroke="#72eee0" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#70b6ff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#91f6df"/></g></g><g transform="translate(474 155) scale(0.68)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M0 0L0 -33" fill="none" stroke="#70b6ff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="0" cy="-33" r="7" stroke="#72eee0" fill="none" class="breathe"/><path d="M0 0L-35 23" fill="none" stroke="#70b6ff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="-35" cy="23" r="7" stroke="#72eee0" fill="none" class="breathe"/><path d="M0 0L35 23" fill="none" stroke="#70b6ff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="35" cy="23" r="7" stroke="#72eee0" fill="none" class="breathe"/><circle cx="0" cy="0" r="15" stroke="#72eee0" fill="none" /><path d="M-6 0L-1 5L8 -6" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></g></g><path d="M30 204H550" fill="none" stroke="#245360" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">TOOLKIT GUIDE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 5.0 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Local automation</title><desc id="desc">Connect rooms to your smart home. Home Assistant, MQTT and Matter. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="30" y="39" font-size="11" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">RUVIEW / 05</text><text x="30" y="94" font-size="29" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Local automation</text><text x="30" y="136" font-size="16" fill="#b0c2db" text-anchor="start" font-weight="500" >Connect rooms to your smart home.</text><text x="30" y="164" font-size="16" fill="#b0c2db" text-anchor="start" font-weight="500" >Home Assistant, MQTT and Matter.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 161) scale(0.53)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><g transform="translate(474 155) scale(0.68)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M-37 -3L0 -34L37 -3M-28 -10V32H28V-10M-9 32V8H9V32" fill="none" stroke="#78cfff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="-9" r="5" stroke="#438fff" fill="none" class="breathe"/></g></g><path d="M30 204H550" fill="none" stroke="#2d4e77" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#78cfff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">INTEGRATION GUIDE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
After Width: | Height: | Size: 4.7 KiB |
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Local automation</title><desc id="desc">Connect rooms to your smart home. Home Assistant, MQTT and Matter. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
|
||||
|
||||
|
||||
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="30" y="39" font-size="11" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">RUVIEW / 05</text><text x="30" y="89" font-size="25" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Local automation</text><text x="30" y="126" font-size="14" fill="#b0c2db" text-anchor="start" font-weight="500" >Connect rooms to your smart home.</text><text x="30" y="151" font-size="14" fill="#b0c2db" text-anchor="start" font-weight="500" >Home Assistant, MQTT and Matter.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 144) scale(1.12)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><path d="M-43 12L0 -10L43 12L0 35Z" fill="#102749" stroke="#438fff"/><path d="M-32 17V-16L0 -35L32 -16V17M-32 -16L0 3L32 -16M0 3V34" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M-22 20L-10 13L12 25M-10 13V-5" fill="none" stroke="#b8f36c" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="-22" cy="20" r="4" stroke="#ffb45e" fill="#102749" class="breathe"/><circle cx="-10" cy="-5" r="4" stroke="#ffb45e" fill="#102749" class="breathe"/><circle cx="12" cy="25" r="4" stroke="#ffb45e" fill="#102749" class="breathe"/></g></g><path d="M30 204H550" fill="none" stroke="#2d4e77" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#78cfff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">INTEGRATION GUIDE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M24 1H556" stroke="url(#accent)" opacity=".55"/><path d="M24 275H556" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 3.4 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Local automation</title><desc id="desc">Connect rooms to your smart home. Home Assistant, MQTT and Matter. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#245360"/><text x="30" y="39" font-size="11" fill="#72eee0" text-anchor="start" class="mono">RUVIEW / 05</text><text x="30" y="94" font-size="29" fill="#ecfafb" text-anchor="start" >Local automation</text><text x="30" y="136" font-size="16" fill="#9abac5" text-anchor="start" >Connect rooms to your smart home.</text><text x="30" y="164" font-size="16" fill="#9abac5" text-anchor="start" >Home Assistant, MQTT and Matter.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(474 168) scale(0.83)"><path d="M-37 -3L0 -34L37 -3M-28 -10V32H28V-10M-9 32V8H9V32" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="-9" r="5" stroke="#70b6ff" fill="none" class="breathe"/></g><path d="M30 204H550" fill="none" stroke="#245360" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#72eee0" text-anchor="start" class="mono">INTEGRATION GUIDE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Local automation</title><desc id="desc">Connect rooms to your smart home. Home Assistant, MQTT and Matter. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#245360"/><text x="30" y="39" font-size="11" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">RUVIEW / 05</text><text x="30" y="94" font-size="29" fill="#ecfafb" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Local automation</text><text x="30" y="136" font-size="16" fill="#9abac5" text-anchor="start" font-weight="500" >Connect rooms to your smart home.</text><text x="30" y="164" font-size="16" fill="#9abac5" text-anchor="start" font-weight="500" >Home Assistant, MQTT and Matter.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 161) scale(0.53)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0a2733" stroke="#286171"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#366d80"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#5fe3d8" fill-opacity=".12" stroke="#72eee0" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#70b6ff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#91f6df"/></g></g><g transform="translate(474 155) scale(0.68)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M-37 -3L0 -34L37 -3M-28 -10V32H28V-10M-9 32V8H9V32" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="-9" r="5" stroke="#70b6ff" fill="none" class="breathe"/></g></g><path d="M30 204H550" fill="none" stroke="#245360" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">INTEGRATION GUIDE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Edge sensor mesh</title><desc id="desc">Provision, capture and calibrate. Explore ESP32 firmware and runbooks. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="30" y="39" font-size="11" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">RUVIEW / 04</text><text x="30" y="94" font-size="29" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Edge sensor mesh</text><text x="30" y="136" font-size="16" fill="#b0c2db" text-anchor="start" font-weight="500" >Provision, capture and calibrate.</text><text x="30" y="164" font-size="16" fill="#b0c2db" text-anchor="start" font-weight="500" >Explore ESP32 firmware and runbooks.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 161) scale(0.53)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><g transform="translate(474 155) scale(0.68)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><rect x="-25" y="-25" width="50" height="50" rx="7" fill="#0c2242" stroke="#78cfff"/><rect x="-13" y="-13" width="26" height="26" rx="3" fill="none" stroke="#438fff"/><path d="M-16 -36V-25M-16 25V36M-36 -16H-25M25 -16H36" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M0 -36V-25M0 25V36M-36 0H-25M25 0H36" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M16 -36V-25M16 25V36M-36 16H-25M25 16H36" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="0" r="4" stroke="#78cfff" fill="#78cfff" class="breathe"/></g></g><path d="M30 204H550" fill="none" stroke="#2d4e77" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#78cfff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">HARDWARE GUIDE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
After Width: | Height: | Size: 5.1 KiB |
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Edge sensor mesh</title><desc id="desc">Provision, capture and calibrate. Explore ESP32 firmware and runbooks. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
|
||||
.layer{animation:layer 6s ease-in-out infinite}
|
||||
@keyframes layer{50%{transform:translateY(-5px)}}
|
||||
|
||||
|
||||
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="30" y="39" font-size="11" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">RUVIEW / 04</text><text x="30" y="89" font-size="25" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Edge sensor mesh</text><text x="30" y="126" font-size="14" fill="#b0c2db" text-anchor="start" font-weight="500" >Provision, capture and calibrate.</text><text x="30" y="151" font-size="14" fill="#b0c2db" text-anchor="start" font-weight="500" >Explore ESP32 firmware and runbooks.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 144) scale(1.12)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><g class="layer" style="animation-delay:-0.0s"><path d="M-38 -23L0 -42L38 -23L0 -4Z" fill="#102749" stroke="#78cfff"/><path d="M-38 -23V-16L0 3L38 -16V-23M0 -4V3" fill="#09172c" stroke="#315685"/></g><g class="layer" style="animation-delay:-0.7s"><path d="M-38 -10L0 -29L38 -10L0 9Z" fill="#102749" stroke="#438fff"/><path d="M-38 -10V-3L0 16L38 -3V-10M0 9V16" fill="#09172c" stroke="#315685"/></g><g class="layer" style="animation-delay:-1.4s"><path d="M-38 3L0 -16L38 3L0 22Z" fill="#102749" stroke="#438fff"/><path d="M-38 3V10L0 29L38 10V3M0 22V29" fill="#09172c" stroke="#315685"/></g><path d="M-12 -31L0 -37L12 -31L0 -25Z" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M-26 22V39" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M0 22V39" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M26 22V39" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/></g></g><path d="M30 204H550" fill="none" stroke="#2d4e77" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#78cfff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">HARDWARE GUIDE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M24 1H556" stroke="url(#accent)" opacity=".55"/><path d="M24 275H556" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Edge sensor mesh</title><desc id="desc">Provision, capture and calibrate. Explore ESP32 firmware and runbooks. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#245360"/><text x="30" y="39" font-size="11" fill="#72eee0" text-anchor="start" class="mono">RUVIEW / 04</text><text x="30" y="94" font-size="29" fill="#ecfafb" text-anchor="start" >Edge sensor mesh</text><text x="30" y="136" font-size="16" fill="#9abac5" text-anchor="start" >Provision, capture and calibrate.</text><text x="30" y="164" font-size="16" fill="#9abac5" text-anchor="start" >Explore ESP32 firmware and runbooks.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(474 168) scale(0.83)"><rect x="-25" y="-25" width="50" height="50" rx="7" fill="#0a2630" stroke="#72eee0"/><rect x="-13" y="-13" width="26" height="26" rx="3" fill="none" stroke="#70b6ff"/><path d="M-16 -36V-25M-16 25V36M-36 -16H-25M25 -16H36" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M0 -36V-25M0 25V36M-36 0H-25M25 0H36" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M16 -36V-25M16 25V36M-36 16H-25M25 16H36" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="0" r="4" stroke="#72eee0" fill="#72eee0" class="breathe"/></g><path d="M30 204H550" fill="none" stroke="#245360" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#72eee0" text-anchor="start" class="mono">HARDWARE GUIDE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Edge sensor mesh</title><desc id="desc">Provision, capture and calibrate. Explore ESP32 firmware and runbooks. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#245360"/><text x="30" y="39" font-size="11" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">RUVIEW / 04</text><text x="30" y="94" font-size="29" fill="#ecfafb" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Edge sensor mesh</text><text x="30" y="136" font-size="16" fill="#9abac5" text-anchor="start" font-weight="500" >Provision, capture and calibrate.</text><text x="30" y="164" font-size="16" fill="#9abac5" text-anchor="start" font-weight="500" >Explore ESP32 firmware and runbooks.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 161) scale(0.53)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0a2733" stroke="#286171"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#366d80"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#5fe3d8" fill-opacity=".12" stroke="#72eee0" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#70b6ff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#91f6df"/></g></g><g transform="translate(474 155) scale(0.68)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><rect x="-25" y="-25" width="50" height="50" rx="7" fill="#0a2630" stroke="#72eee0"/><rect x="-13" y="-13" width="26" height="26" rx="3" fill="none" stroke="#70b6ff"/><path d="M-16 -36V-25M-16 25V36M-36 -16H-25M25 -16H36" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M0 -36V-25M0 25V36M-36 0H-25M25 0H36" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M16 -36V-25M16 25V36M-36 16H-25M25 16H36" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="0" r="4" stroke="#72eee0" fill="#72eee0" class="breathe"/></g></g><path d="M30 204H550" fill="none" stroke="#245360" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">HARDWARE GUIDE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 4.9 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Pose research</title><desc id="desc">Train and compare body keypoints. Live accuracy differs from benchmarks. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="30" y="39" font-size="11" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">RUVIEW / 03</text><text x="30" y="94" font-size="29" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Pose research</text><text x="30" y="136" font-size="16" fill="#b0c2db" text-anchor="start" font-weight="500" >Train and compare body keypoints.</text><text x="30" y="164" font-size="16" fill="#b0c2db" text-anchor="start" font-weight="500" >Live accuracy differs from benchmarks.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 161) scale(0.53)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><g transform="translate(474 155) scale(0.68)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><circle cx="0" cy="-28" r="8" stroke="#b8f36c" fill="none" /><path d="M0 -20V12M-24 -6L0 -14L24 -6M0 12L-18 38M0 12L18 38" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="-14" r="2.3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="0" cy="12" r="2.3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="-24" cy="-6" r="2.3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="24" cy="-6" r="2.3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="-18" cy="38" r="3" stroke="#ffb45e" fill="#071124" class="breathe"/><circle cx="18" cy="38" r="3" stroke="#ffb45e" fill="#071124" class="breathe"/></g></g><path d="M30 204H550" fill="none" stroke="#2d4e77" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#78cfff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">READ THE EVIDENCE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
After Width: | Height: | Size: 5.1 KiB |
@@ -0,0 +1,31 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Pose research</title><desc id="desc">Train and compare body keypoints. Live accuracy differs from benchmarks. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
|
||||
|
||||
|
||||
.limb{animation:limb 3s ease-in-out infinite;transform-origin:0 -18px}
|
||||
.stride{animation:limb 3s ease-in-out infinite reverse;transform-origin:5px 16px}
|
||||
|
||||
|
||||
|
||||
@keyframes limb{0%,100%{transform:rotate(-9deg)}50%{transform:rotate(9deg)}}
|
||||
|
||||
|
||||
|
||||
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
|
||||
|
||||
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="30" y="39" font-size="11" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">RUVIEW / 03</text><text x="30" y="89" font-size="25" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Pose research</text><text x="30" y="126" font-size="14" fill="#b0c2db" text-anchor="start" font-weight="500" >Train and compare body keypoints.</text><text x="30" y="151" font-size="14" fill="#b0c2db" text-anchor="start" font-weight="500" >Live accuracy differs from benchmarks.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 144) scale(1.12)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><circle cx="0" cy="-32" r="7" stroke="#b8f36c" fill="none" /><path d="M0 -25L-3 -2L5 16" fill="none" stroke="#b8f36c" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" /><g class="limb"><path d="M-1 -18L-19 -5L-31 -14M-1 -18L18 -11L28 -25" fill="none" stroke="#b8f36c" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" /></g><g class="stride"><path d="M5 16L-12 28L-22 47M5 16L24 28L33 44" fill="none" stroke="#b8f36c" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" /></g><circle cx="-1" cy="-18" r="2.8" stroke="#ffb45e" fill="#ffb45e"/><circle cx="-3" cy="-2" r="2.8" stroke="#ffb45e" fill="#ffb45e"/><circle cx="5" cy="16" r="2.8" stroke="#ffb45e" fill="#ffb45e"/><circle cx="-19" cy="-5" r="2.8" stroke="#ffb45e" fill="#ffb45e"/><circle cx="18" cy="-11" r="2.8" stroke="#ffb45e" fill="#ffb45e"/><path d="M-42 -42V-50H-30M30 -50H42V-42M42 45V53H30M-30 53H-42V45" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /></g></g><path d="M30 204H550" fill="none" stroke="#2d4e77" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#78cfff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">READ THE EVIDENCE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M24 1H556" stroke="url(#accent)" opacity=".55"/><path d="M24 275H556" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
@@ -0,0 +1,31 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Pose research</title><desc id="desc">Train and compare body keypoints. Live accuracy differs from benchmarks. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
|
||||
|
||||
|
||||
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
|
||||
|
||||
|
||||
.limb{animation:limb 3s ease-in-out infinite;transform-origin:0 -18px}
|
||||
.stride{animation:limb 3s ease-in-out infinite reverse;transform-origin:5px 16px}
|
||||
|
||||
|
||||
|
||||
@keyframes limb{0%,100%{transform:rotate(-3deg)}50%{transform:rotate(3deg)}}
|
||||
|
||||
|
||||
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
|
||||
|
||||
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="30" y="39" font-size="11" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">RUVIEW / 03</text><text x="30" y="89" font-size="25" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Pose research</text><text x="30" y="126" font-size="14" fill="#b0c2db" text-anchor="start" font-weight="500" >Train and compare body keypoints.</text><text x="30" y="151" font-size="14" fill="#b0c2db" text-anchor="start" font-weight="500" >Live accuracy differs from benchmarks.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 144) scale(1.12)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><defs><linearGradient id="humanSurface" x1="0" y1="0" x2="1" y2=".3"><stop stop-color="#153d48"/><stop offset=".42" stop-color="#338469"/><stop offset=".7" stop-color="#83c766"/><stop offset="1" stop-color="#163c4a"/></linearGradient><linearGradient id="humanRear"><stop stop-color="#0f2d40"/><stop offset="1" stop-color="#397a68"/></linearGradient></defs><g stroke="#88d783" stroke-width=".65" stroke-linejoin="round"><g class="limb"><path d="M-9 -25C-15 -25 -18 -19 -21 -13L-29 -5C-32 -3 -33 1 -30 3L-25 2L-14 -7C-9 -12 -6 -19 -6 -23Z" fill="url(#humanRear)"/></g><g class="stride"><path d="M-6 5C-13 10 -15 18 -17 26L-25 43L-29 47C-30 49 -28 51 -24 51L-17 50L-16 46L-8 30C-4 23 2 15 3 10Z" fill="url(#humanRear)"/><path d="M3 5C10 7 14 17 19 23C24 28 23 35 24 41L29 45C32 47 32 49 28 50L20 49C17 48 17 46 17 42L14 32C8 28 2 23 -3 16L-4 10Z" fill="url(#humanSurface)"/></g><g class="limb"><path d="M7 -26C14 -25 17 -20 20 -13L24 -17L29 -24C30 -27 33 -29 35 -26L35 -22L30 -16L24 -8C22 -5 18 -5 16 -8L8 -17Z" fill="url(#humanSurface)"/></g><path d="M-4 -32L4 -33L6 -28C11 -27 14 -24 13 -19L9 -5L10 7C10 13 2 15 -5 12L-10 7L-8 -5L-12 -20C-13 -25 -8 -28 -5 -28Z" fill="url(#humanSurface)"/><path d="M-6 -42C-6 -48 2 -51 7 -47C10 -45 10 -41 9 -39L11 -36L8 -35L7 -31L3 -29L-3 -32L-5 -37Z" fill="url(#humanSurface)"/></g><path d="M-9 -23Q-2 -18 10 -23M-7 -15Q0 -11 9 -16M-6 -8Q1 -5 8 -9M-7 1Q1 5 9 1M0 -26L2 9M-4 -40Q0 -42 7 -40M2 -32L6 -34" fill="none" stroke="#b8f36c" stroke-width="0.55" stroke-linecap="round" stroke-linejoin="round" opacity=".65"/><path d="M-5 -45Q1 -49 6 -46M-10 -22L-6 -8M9 17L17 28M-12 20L-20 42" fill="none" stroke="#78cfff" stroke-width="0.7" stroke-linecap="round" stroke-linejoin="round" opacity=".8"/><circle cx="-9" cy="-24" r="3.5" stroke="#ffb45e" fill="none" stroke-opacity=".25"/><circle cx="-9" cy="-24" r="1.5" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="10" cy="-23" r="3.5" stroke="#ffb45e" fill="none" stroke-opacity=".25"/><circle cx="10" cy="-23" r="1.5" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="1" cy="-12" r="3.5" stroke="#ffb45e" fill="none" stroke-opacity=".25"/><circle cx="1" cy="-12" r="1.5" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="2" cy="9" r="3.5" stroke="#ffb45e" fill="none" stroke-opacity=".25"/><circle cx="2" cy="9" r="1.5" stroke="#ffb45e" fill="#ffb45e" class="spark"/><path d="M-42 -42V-53H-30M30 -53H42V-42M42 45V55H30M-30 55H-42V45" fill="none" stroke="#438fff" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /></g></g><path d="M30 204H550" fill="none" stroke="#2d4e77" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#78cfff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">READ THE EVIDENCE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M24 1H556" stroke="url(#accent)" opacity=".55"/><path d="M24 275H556" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 5.5 KiB |
@@ -0,0 +1,27 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Pose research</title><desc id="desc">Train and compare body keypoints. Live accuracy differs from benchmarks. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
|
||||
|
||||
|
||||
.humanTurn{transform-box:fill-box;transform-origin:center 85%;animation:humanTurn 8s ease-in-out infinite}
|
||||
.humanBody{animation:humanBody 1.6s ease-in-out infinite}
|
||||
.armFar{transform-origin:-9px -24px;animation:armSwing 1.6s ease-in-out infinite}
|
||||
.armNear{transform-origin:10px -23px;animation:armSwing 1.6s ease-in-out infinite;animation-delay:-.8s}
|
||||
.legFar{transform-origin:-3px 9px;animation:legSwing 1.6s ease-in-out infinite;animation-delay:-.8s}
|
||||
.legNear{transform-origin:2px 9px;animation:legSwing 1.6s ease-in-out infinite}
|
||||
.humanLight{animation:humanLight 8s ease-in-out infinite}
|
||||
.gaitShadow{transform-box:fill-box;transform-origin:center;animation:gaitShadow 1.6s ease-in-out infinite}
|
||||
@keyframes humanTurn{0%,100%{transform:perspective(450px) rotateY(-24deg) rotateZ(-2deg)}50%{transform:perspective(450px) rotateY(24deg) rotateZ(2deg)}}
|
||||
@keyframes humanBody{0%,50%,100%{transform:translateY(0)}25%,75%{transform:translateY(-2px)}}
|
||||
@keyframes armSwing{0%,100%{transform:rotate(-17deg)}50%{transform:rotate(17deg)}}
|
||||
@keyframes legSwing{0%,100%{transform:rotate(-12deg)}50%{transform:rotate(12deg)}}
|
||||
@keyframes humanLight{0%,100%{opacity:.1}50%{opacity:.45}}
|
||||
@keyframes gaitShadow{0%,50%,100%{transform:scaleX(1);opacity:.7}25%,75%{transform:scaleX(.85);opacity:.45}}
|
||||
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="30" y="39" font-size="11" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">RUVIEW / 03</text><text x="30" y="89" font-size="25" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Pose research</text><text x="30" y="126" font-size="14" fill="#b0c2db" text-anchor="start" font-weight="500" >Train and compare body keypoints.</text><text x="30" y="151" font-size="14" fill="#b0c2db" text-anchor="start" font-weight="500" >Live accuracy differs from benchmarks.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 144) scale(1.12)"><ellipse cy="53" rx="37" ry="7" fill="#020816" class="gaitShadow"/><g class="humanBody"><defs><linearGradient id="humanSurface" x1="0" y1="0" x2="1" y2=".3"><stop stop-color="#153d48"/><stop offset=".42" stop-color="#338469"/><stop offset=".7" stop-color="#83c766"/><stop offset="1" stop-color="#163c4a"/></linearGradient><linearGradient id="humanRear"><stop stop-color="#0f2d40"/><stop offset="1" stop-color="#397a68"/></linearGradient></defs><g class="humanTurn"><g stroke="#88d783" stroke-width=".65" stroke-linejoin="round"><g class="armFar"><path d="M-9 -25C-15 -25 -18 -19 -21 -13L-29 -5C-32 -3 -33 1 -30 3L-25 2L-14 -7C-9 -12 -6 -19 -6 -23Z" fill="url(#humanRear)"/></g><g class="legFar"><path d="M-6 5C-13 10 -15 18 -17 26L-25 43L-29 47C-30 49 -28 51 -24 51L-17 50L-16 46L-8 30C-4 23 2 15 3 10Z" fill="url(#humanRear)"/><circle cx="-15" cy="26" r="1.8" fill="#ffb45e"/></g><g class="legNear"><path d="M3 5C10 7 14 17 19 23C24 28 23 35 24 41L29 45C32 47 32 49 28 50L20 49C17 48 17 46 17 42L14 32C8 28 2 23 -3 16L-4 10Z" fill="url(#humanSurface)"/><circle cx="19" cy="28" r="1.8" fill="#ffb45e"/></g><g class="armNear"><path d="M7 -26C14 -25 17 -20 20 -13L24 -17L29 -24C30 -27 33 -29 35 -26L35 -22L30 -16L24 -8C22 -5 18 -5 16 -8L8 -17Z" fill="url(#humanSurface)"/></g><path d="M-4 -32L4 -33L6 -28C11 -27 14 -24 13 -19L9 -5L10 7C10 13 2 15 -5 12L-10 7L-8 -5L-12 -20C-13 -25 -8 -28 -5 -28Z" fill="url(#humanSurface)"/><path d="M-6 -42C-6 -48 2 -51 7 -47C10 -45 10 -41 9 -39L11 -36L8 -35L7 -31L3 -29L-3 -32L-5 -37Z" fill="url(#humanSurface)"/></g><path d="M-9 -23Q-2 -18 10 -23M-7 -15Q0 -11 9 -16M-6 -8Q1 -5 8 -9M-7 1Q1 5 9 1M0 -26L2 9M-4 -40Q0 -42 7 -40M2 -32L6 -34" fill="none" stroke="#b8f36c" stroke-width="0.55" stroke-linecap="round" stroke-linejoin="round" opacity=".65"/><path d="M-5 -45Q1 -49 6 -46M-10 -22L-6 -8" fill="none" stroke="#78cfff" stroke-width="0.7" stroke-linecap="round" stroke-linejoin="round" opacity=".8"/><path d="M-8 -25Q-2 -21 1 -10L3 9Q-5 8 -6 3L-5 -8Z" fill="#a7ebff" class="humanLight" opacity=".2"/><circle cx="-9" cy="-24" r="3.5" stroke="#ffb45e" fill="none" stroke-opacity=".25"/><circle cx="-9" cy="-24" r="1.5" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="10" cy="-23" r="3.5" stroke="#ffb45e" fill="none" stroke-opacity=".25"/><circle cx="10" cy="-23" r="1.5" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="1" cy="-12" r="3.5" stroke="#ffb45e" fill="none" stroke-opacity=".25"/><circle cx="1" cy="-12" r="1.5" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="2" cy="9" r="3.5" stroke="#ffb45e" fill="none" stroke-opacity=".25"/><circle cx="2" cy="9" r="1.5" stroke="#ffb45e" fill="#ffb45e" class="spark"/></g><path d="M-42 -42V-53H-30M30 -53H42V-42M42 45V55H30M-30 55H-42V45" fill="none" stroke="#438fff" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /></g></g><path d="M30 204H550" fill="none" stroke="#2d4e77" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#78cfff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">READ THE EVIDENCE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M24 1H556" stroke="url(#accent)" opacity=".55"/><path d="M24 275H556" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 6.6 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Pose research</title><desc id="desc">Train and compare body keypoints. Live accuracy differs from benchmarks. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#245360"/><text x="30" y="39" font-size="11" fill="#72eee0" text-anchor="start" class="mono">RUVIEW / 03</text><text x="30" y="94" font-size="29" fill="#ecfafb" text-anchor="start" >Pose research</text><text x="30" y="136" font-size="16" fill="#9abac5" text-anchor="start" >Train and compare body keypoints.</text><text x="30" y="164" font-size="16" fill="#9abac5" text-anchor="start" >Live accuracy differs from benchmarks.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(474 168) scale(0.83)"><circle cx="0" cy="-28" r="8" stroke="#72eee0" fill="none" /><path d="M0 -20V12M-24 -6L0 -14L24 -6M0 12L-18 38M0 12L18 38" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="-14" r="3" stroke="#70b6ff" fill="#041117" class="breathe"/><circle cx="0" cy="12" r="3" stroke="#70b6ff" fill="#041117" class="breathe"/><circle cx="-24" cy="-6" r="3" stroke="#70b6ff" fill="#041117" class="breathe"/><circle cx="24" cy="-6" r="3" stroke="#70b6ff" fill="#041117" class="breathe"/><circle cx="-18" cy="38" r="3" stroke="#70b6ff" fill="#041117" class="breathe"/><circle cx="18" cy="38" r="3" stroke="#70b6ff" fill="#041117" class="breathe"/></g><path d="M30 204H550" fill="none" stroke="#245360" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#72eee0" text-anchor="start" class="mono">READ THE EVIDENCE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Pose research</title><desc id="desc">Train and compare body keypoints. Live accuracy differs from benchmarks. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#245360"/><text x="30" y="39" font-size="11" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">RUVIEW / 03</text><text x="30" y="94" font-size="29" fill="#ecfafb" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Pose research</text><text x="30" y="136" font-size="16" fill="#9abac5" text-anchor="start" font-weight="500" >Train and compare body keypoints.</text><text x="30" y="164" font-size="16" fill="#9abac5" text-anchor="start" font-weight="500" >Live accuracy differs from benchmarks.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 161) scale(0.53)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0a2733" stroke="#286171"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#366d80"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#5fe3d8" fill-opacity=".12" stroke="#72eee0" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#70b6ff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#91f6df"/></g></g><g transform="translate(474 155) scale(0.68)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><circle cx="0" cy="-28" r="8" stroke="#72eee0" fill="none" /><path d="M0 -20V12M-24 -6L0 -14L24 -6M0 12L-18 38M0 12L18 38" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="-14" r="3" stroke="#70b6ff" fill="#041117" class="breathe"/><circle cx="0" cy="12" r="3" stroke="#70b6ff" fill="#041117" class="breathe"/><circle cx="-24" cy="-6" r="3" stroke="#70b6ff" fill="#041117" class="breathe"/><circle cx="24" cy="-6" r="3" stroke="#70b6ff" fill="#041117" class="breathe"/><circle cx="-18" cy="38" r="3" stroke="#70b6ff" fill="#041117" class="breathe"/><circle cx="18" cy="38" r="3" stroke="#70b6ff" fill="#041117" class="breathe"/></g></g><path d="M30 204H550" fill="none" stroke="#245360" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">READ THE EVIDENCE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 4.9 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Presence & motion</title><desc id="desc">Explore changes in room occupancy. Inspect motion features from CSI. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="30" y="39" font-size="11" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">RUVIEW / 01</text><text x="30" y="94" font-size="29" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Presence & motion</text><text x="30" y="136" font-size="16" fill="#b0c2db" text-anchor="start" font-weight="500" >Explore changes in room occupancy.</text><text x="30" y="164" font-size="16" fill="#b0c2db" text-anchor="start" font-weight="500" >Inspect motion features from CSI.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 161) scale(0.53)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><g transform="translate(474 155) scale(0.68)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><circle cx="0" cy="-28" r="8" stroke="#b8f36c" fill="none" /><path d="M0 -20V12M-24 -6L0 -14L24 -6M0 12L-18 38M0 12L18 38" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="-14" r="2.3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="0" cy="12" r="2.3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="-24" cy="-6" r="2.3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="24" cy="-6" r="2.3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><ellipse cx="0" cy="14" rx="40" ry="13" fill="none" stroke="#438fff" stroke-dasharray="3 8" class="flow"/></g></g><path d="M30 204H550" fill="none" stroke="#2d4e77" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#78cfff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">SENSING GUIDE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
After Width: | Height: | Size: 5.1 KiB |
@@ -0,0 +1,17 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Presence & motion</title><desc id="desc">Explore changes in room occupancy. Inspect motion features from CSI. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
|
||||
.sweep{animation:sweep 5s ease-in-out infinite;transform-origin:0 0}
|
||||
@keyframes sweep{0%,100%{transform:rotate(-20deg)}50%{transform:rotate(155deg)}}
|
||||
|
||||
|
||||
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="30" y="39" font-size="11" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">RUVIEW / 01</text><text x="30" y="89" font-size="25" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Presence & motion</text><text x="30" y="126" font-size="14" fill="#b0c2db" text-anchor="start" font-weight="500" >Explore changes in room occupancy.</text><text x="30" y="151" font-size="14" fill="#b0c2db" text-anchor="start" font-weight="500" >Inspect motion features from CSI.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 144) scale(1.12)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><ellipse rx="18" ry="8.64" fill="none" stroke="#78cfff" opacity=".4"/><ellipse rx="32" ry="15.36" fill="none" stroke="#78cfff" opacity=".4"/><ellipse rx="46" ry="22.08" fill="none" stroke="#78cfff" opacity=".4"/><path d="M-46 0H46M0 -23V23" fill="none" stroke="#438fff" stroke-width="0.7" stroke-linecap="round" stroke-linejoin="round" /><g class="sweep"><path d="M0 0L40 -18" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="34" cy="-15" r="3" stroke="#b8f36c" fill="#b8f36c"/></g><circle cx="-20" cy="8" r="3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="12" cy="13" r="3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="23" cy="-8" r="3" stroke="#ffb45e" fill="#ffb45e" class="spark"/></g></g><path d="M30 204H550" fill="none" stroke="#2d4e77" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#78cfff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">SENSING GUIDE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M24 1H556" stroke="url(#accent)" opacity=".55"/><path d="M24 275H556" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 3.7 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Presence & motion</title><desc id="desc">Explore changes in room occupancy. Inspect motion features from CSI. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#245360"/><text x="30" y="39" font-size="11" fill="#72eee0" text-anchor="start" class="mono">RUVIEW / 01</text><text x="30" y="94" font-size="29" fill="#ecfafb" text-anchor="start" >Presence & motion</text><text x="30" y="136" font-size="16" fill="#9abac5" text-anchor="start" >Explore changes in room occupancy.</text><text x="30" y="164" font-size="16" fill="#9abac5" text-anchor="start" >Inspect motion features from CSI.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(474 168) scale(0.83)"><circle cx="0" cy="-28" r="8" stroke="#72eee0" fill="none" /><path d="M0 -20V12M-24 -6L0 -14L24 -6M0 12L-18 38M0 12L18 38" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><ellipse cx="0" cy="14" rx="40" ry="13" fill="none" stroke="#70b6ff" stroke-dasharray="3 8" class="flow"/></g><path d="M30 204H550" fill="none" stroke="#245360" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#72eee0" text-anchor="start" class="mono">SENSING GUIDE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Presence & motion</title><desc id="desc">Explore changes in room occupancy. Inspect motion features from CSI. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#245360"/><text x="30" y="39" font-size="11" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">RUVIEW / 01</text><text x="30" y="94" font-size="29" fill="#ecfafb" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Presence & motion</text><text x="30" y="136" font-size="16" fill="#9abac5" text-anchor="start" font-weight="500" >Explore changes in room occupancy.</text><text x="30" y="164" font-size="16" fill="#9abac5" text-anchor="start" font-weight="500" >Inspect motion features from CSI.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 161) scale(0.53)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0a2733" stroke="#286171"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#366d80"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#5fe3d8" fill-opacity=".12" stroke="#72eee0" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#70b6ff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#91f6df"/></g></g><g transform="translate(474 155) scale(0.68)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><circle cx="0" cy="-28" r="8" stroke="#72eee0" fill="none" /><path d="M0 -20V12M-24 -6L0 -14L24 -6M0 12L-18 38M0 12L18 38" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><ellipse cx="0" cy="14" rx="40" ry="13" fill="none" stroke="#70b6ff" stroke-dasharray="3 8" class="flow"/></g></g><path d="M30 204H550" fill="none" stroke="#245360" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">SENSING GUIDE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 4.5 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Breathing & vital signals</title><desc id="desc">Inspect phase-derived signal trends. Validate estimates on your hardware. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="30" y="39" font-size="11" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">RUVIEW / 02</text><text x="30" y="94" font-size="29" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Breathing & vital signals</text><text x="30" y="136" font-size="16" fill="#b0c2db" text-anchor="start" font-weight="500" >Inspect phase-derived signal trends.</text><text x="30" y="164" font-size="16" fill="#b0c2db" text-anchor="start" font-weight="500" >Validate estimates on your hardware.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 161) scale(0.53)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><g transform="translate(474 155) scale(0.68)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M-42 6H-27L-18 -10L-9 25L3 -25L14 10L24 -3L31 6H43" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/><path d="M-42 31H43" fill="none" stroke="#233f63" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /></g></g><path d="M30 204H550" fill="none" stroke="#2d4e77" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#78cfff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">SIGNAL PIPELINE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
After Width: | Height: | Size: 4.8 KiB |
@@ -0,0 +1,17 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Breathing & vital signals</title><desc id="desc">Inspect phase-derived signal trends. Validate estimates on your hardware. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
|
||||
.cursor{animation:cursor 5s ease-in-out infinite}
|
||||
@keyframes cursor{0%,100%{transform:translateX(-35px);opacity:.3}50%{transform:translateX(35px);opacity:1}}
|
||||
|
||||
|
||||
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="30" y="39" font-size="11" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">RUVIEW / 02</text><text x="30" y="89" font-size="25" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Breathing & vital signals</text><text x="30" y="126" font-size="14" fill="#b0c2db" text-anchor="start" font-weight="500" >Inspect phase-derived signal trends.</text><text x="30" y="151" font-size="14" fill="#b0c2db" text-anchor="start" font-weight="500" >Validate estimates on your hardware.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 144) scale(1.12)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><path d="M-49 -14C-34 -14 -30 -35 -17 -35S0 7 14 7S34 -14 49 -14" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="trace" style="animation-delay:-0s" opacity="1.0"/><path d="M-49 -2C-34 -2 -30 -23 -17 -23S0 19 14 19S34 -2 49 -2" fill="none" stroke="#b8f36c" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="trace" style="animation-delay:-1s" opacity="0.8"/><path d="M-49 10C-34 10 -30 -11 -17 -11S0 31 14 31S34 10 49 10" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="trace" style="animation-delay:-2s" opacity="0.6"/><g class="cursor"><path d="M0 -42V40" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="-8" r="3" stroke="#ffb45e" fill="#ffb45e"/></g></g></g><path d="M30 204H550" fill="none" stroke="#2d4e77" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#78cfff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">SIGNAL PIPELINE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M24 1H556" stroke="url(#accent)" opacity=".55"/><path d="M24 275H556" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Breathing & vital signals</title><desc id="desc">Inspect phase-derived signal trends. Validate estimates on your hardware. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#245360"/><text x="30" y="39" font-size="11" fill="#72eee0" text-anchor="start" class="mono">RUVIEW / 02</text><text x="30" y="94" font-size="29" fill="#ecfafb" text-anchor="start" >Breathing & vital signals</text><text x="30" y="136" font-size="16" fill="#9abac5" text-anchor="start" >Inspect phase-derived signal trends.</text><text x="30" y="164" font-size="16" fill="#9abac5" text-anchor="start" >Validate estimates on your hardware.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(474 168) scale(0.83)"><path d="M-42 6H-27L-18 -10L-9 25L3 -25L14 10L24 -3L31 6H43" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/><path d="M-42 31H43" fill="none" stroke="#214451" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /></g><path d="M30 204H550" fill="none" stroke="#245360" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#72eee0" text-anchor="start" class="mono">SIGNAL PIPELINE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="580" height="276" viewBox="0 0 580 276" role="img" aria-labelledby="title desc"><title id="title">Breathing & vital signals</title><desc id="desc">Inspect phase-derived signal trends. Validate estimates on your hardware. Conceptual animation; follow the linked source for capability limits.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="579" height="275" rx="18" fill="url(#wash)" stroke="#245360"/><text x="30" y="39" font-size="11" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">RUVIEW / 02</text><text x="30" y="94" font-size="29" fill="#ecfafb" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Breathing & vital signals</text><text x="30" y="136" font-size="16" fill="#9abac5" text-anchor="start" font-weight="500" >Inspect phase-derived signal trends.</text><text x="30" y="164" font-size="16" fill="#9abac5" text-anchor="start" font-weight="500" >Validate estimates on your hardware.</text><circle cx="465" cy="170" r="110" fill="url(#halo)"/><g transform="translate(480 161) scale(0.53)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0a2733" stroke="#286171"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#366d80"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#5fe3d8" fill-opacity=".12" stroke="#72eee0" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#70b6ff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#91f6df"/></g></g><g transform="translate(474 155) scale(0.68)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M-42 6H-27L-18 -10L-9 25L3 -25L14 10L24 -3L31 6H43" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/><path d="M-42 31H43" fill="none" stroke="#214451" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /></g></g><path d="M30 204H550" fill="none" stroke="#245360" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M30 204H550" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity=".5"/><text x="30" y="245" font-size="12" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">SIGNAL PIPELINE</text><path d="M510 239H540M532 231L540 239L532 247" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
Before Width: | Height: | Size: 2.8 KiB After Width: | Height: | Size: 4.5 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">Choose your next step</title><desc id="desc">Animated section heading. GUIDES / HARDWARE / INTEGRATIONS</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(76 75) scale(0.75)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M0 0L0 -33" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="0" cy="-33" r="7" stroke="#78cfff" fill="none" class="breathe"/><path d="M0 0L-35 23" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="-35" cy="23" r="7" stroke="#78cfff" fill="none" class="breathe"/><path d="M0 0L35 23" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="35" cy="23" r="7" stroke="#78cfff" fill="none" class="breathe"/><circle cx="0" cy="0" r="15" stroke="#78cfff" fill="none" /><path d="M-6 0L-1 5L8 -6" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></g></g><text x="140" y="63" font-size="34" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Choose your next step</text><text x="142" y="99" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">GUIDES / HARDWARE / INTEGRATIONS</text><g transform="translate(1012 84) scale(0.55)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#78cfff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
@@ -0,0 +1,22 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">Choose your next step</title><desc id="desc">Animated section heading. GUIDES / HARDWARE / INTEGRATIONS</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{stroke-dasharray:3 14;animation:flow 8s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
|
||||
|
||||
|
||||
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(76 75) scale(0.75)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><path d="M0 0L0 -36" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M0 -43l7 4v8l-7 4l-7 -4v-8Z" fill="#102749" stroke="#78cfff" class="breathe"/><path d="M0 0L-38 -10" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M-38 -17l7 4v8l-7 4l-7 -4v-8Z" fill="#102749" stroke="#78cfff" class="breathe"/><path d="M0 0L-23 31" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M-23 24l7 4v8l-7 4l-7 -4v-8Z" fill="#102749" stroke="#78cfff" class="breathe"/><path d="M0 0L30 28" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M30 21l7 4v8l-7 4l-7 -4v-8Z" fill="#102749" stroke="#78cfff" class="breathe"/><path d="M0 0L39 -17" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M39 -24l7 4v8l-7 4l-7 -4v-8Z" fill="#102749" stroke="#78cfff" class="breathe"/><circle cx="0" cy="0" r="15" stroke="#b8f36c" fill="none" /><path d="M-6 0L-1 5L8 -6" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></g></g><text x="140" y="63" font-size="34" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Choose your next step</text><text x="142" y="99" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">GUIDES / HARDWARE / INTEGRATIONS</text><g transform="translate(1012 84) scale(0.55)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#78cfff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M24 1H1176" stroke="url(#accent)" opacity=".55"/><path d="M24 147H1176" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 4.8 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">Choose your next step</title><desc id="desc">Animated section heading. GUIDES / HARDWARE / INTEGRATIONS</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(76 75) scale(0.75)"><path d="M0 0L0 -33" fill="none" stroke="#70b6ff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="0" cy="-33" r="7" stroke="#72eee0" fill="none" class="breathe"/><path d="M0 0L-35 23" fill="none" stroke="#70b6ff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="-35" cy="23" r="7" stroke="#72eee0" fill="none" class="breathe"/><path d="M0 0L35 23" fill="none" stroke="#70b6ff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="35" cy="23" r="7" stroke="#72eee0" fill="none" class="breathe"/><circle cx="0" cy="0" r="15" stroke="#72eee0" fill="none" /><path d="M-6 0L-1 5L8 -6" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></g><text x="140" y="63" font-size="34" fill="#ecfafb" text-anchor="start" >Choose your next step</text><text x="142" y="99" font-size="12" fill="#72eee0" text-anchor="start" class="mono">GUIDES / HARDWARE / INTEGRATIONS</text><path d="M865 44Q960 10 1124 52" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.65"/><path d="M865 61Q960 38 1124 69" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.55"/><path d="M865 78Q960 66 1124 86" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.45"/><path d="M865 95Q960 94 1124 103" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.35"/><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">Choose your next step</title><desc id="desc">Animated section heading. GUIDES / HARDWARE / INTEGRATIONS</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(76 75) scale(0.75)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M0 0L0 -33" fill="none" stroke="#70b6ff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="0" cy="-33" r="7" stroke="#72eee0" fill="none" class="breathe"/><path d="M0 0L-35 23" fill="none" stroke="#70b6ff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="-35" cy="23" r="7" stroke="#72eee0" fill="none" class="breathe"/><path d="M0 0L35 23" fill="none" stroke="#70b6ff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="35" cy="23" r="7" stroke="#72eee0" fill="none" class="breathe"/><circle cx="0" cy="0" r="15" stroke="#72eee0" fill="none" /><path d="M-6 0L-1 5L8 -6" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></g></g><text x="140" y="63" font-size="34" fill="#ecfafb" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Choose your next step</text><text x="142" y="99" font-size="12" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">GUIDES / HARDWARE / INTEGRATIONS</text><g transform="translate(1012 84) scale(0.55)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0a2733" stroke="#286171"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#366d80"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#5fe3d8" fill-opacity=".12" stroke="#72eee0" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#70b6ff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#91f6df"/></g></g><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 4.3 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">Intelligence at the edge</title><desc id="desc">Animated section heading. SENSORS / FIRMWARE / MODULES</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(76 75) scale(0.75)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><rect x="-25" y="-25" width="50" height="50" rx="7" fill="#0c2242" stroke="#78cfff"/><rect x="-13" y="-13" width="26" height="26" rx="3" fill="none" stroke="#438fff"/><path d="M-16 -36V-25M-16 25V36M-36 -16H-25M25 -16H36" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M0 -36V-25M0 25V36M-36 0H-25M25 0H36" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M16 -36V-25M16 25V36M-36 16H-25M25 16H36" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="0" r="4" stroke="#78cfff" fill="#78cfff" class="breathe"/></g></g><text x="140" y="63" font-size="34" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Intelligence at the edge</text><text x="142" y="99" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">SENSORS / FIRMWARE / MODULES</text><g transform="translate(1012 84) scale(0.55)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#78cfff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1,22 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">Intelligence at the edge</title><desc id="desc">Animated section heading. SENSORS / FIRMWARE / MODULES</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{stroke-dasharray:3 14;animation:flow 8s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
|
||||
.layer{animation:layer 6s ease-in-out infinite}
|
||||
@keyframes layer{50%{transform:translateY(-5px)}}
|
||||
|
||||
|
||||
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(76 75) scale(0.75)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><g class="layer" style="animation-delay:-0.0s"><path d="M-38 -23L0 -42L38 -23L0 -4Z" fill="#102749" stroke="#78cfff"/><path d="M-38 -23V-16L0 3L38 -16V-23M0 -4V3" fill="#09172c" stroke="#315685"/></g><g class="layer" style="animation-delay:-0.7s"><path d="M-38 -10L0 -29L38 -10L0 9Z" fill="#102749" stroke="#438fff"/><path d="M-38 -10V-3L0 16L38 -3V-10M0 9V16" fill="#09172c" stroke="#315685"/></g><g class="layer" style="animation-delay:-1.4s"><path d="M-38 3L0 -16L38 3L0 22Z" fill="#102749" stroke="#438fff"/><path d="M-38 3V10L0 29L38 10V3M0 22V29" fill="#09172c" stroke="#315685"/></g><path d="M-12 -31L0 -37L12 -31L0 -25Z" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M-26 22V39" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M0 22V39" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M26 22V39" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/></g></g><text x="140" y="63" font-size="34" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Intelligence at the edge</text><text x="142" y="99" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">SENSORS / FIRMWARE / MODULES</text><g transform="translate(1012 84) scale(0.55)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#78cfff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M24 1H1176" stroke="url(#accent)" opacity=".55"/><path d="M24 147H1176" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 4.6 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">Intelligence at the edge</title><desc id="desc">Animated section heading. SENSORS / FIRMWARE / MODULES</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(76 75) scale(0.75)"><rect x="-25" y="-25" width="50" height="50" rx="7" fill="#0a2630" stroke="#72eee0"/><rect x="-13" y="-13" width="26" height="26" rx="3" fill="none" stroke="#70b6ff"/><path d="M-16 -36V-25M-16 25V36M-36 -16H-25M25 -16H36" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M0 -36V-25M0 25V36M-36 0H-25M25 0H36" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M16 -36V-25M16 25V36M-36 16H-25M25 16H36" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="0" r="4" stroke="#72eee0" fill="#72eee0" class="breathe"/></g><text x="140" y="63" font-size="34" fill="#ecfafb" text-anchor="start" >Intelligence at the edge</text><text x="142" y="99" font-size="12" fill="#72eee0" text-anchor="start" class="mono">SENSORS / FIRMWARE / MODULES</text><path d="M865 44Q960 10 1124 52" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.65"/><path d="M865 61Q960 38 1124 69" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.55"/><path d="M865 78Q960 66 1124 86" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.45"/><path d="M865 95Q960 94 1124 103" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.35"/><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">Intelligence at the edge</title><desc id="desc">Animated section heading. SENSORS / FIRMWARE / MODULES</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(76 75) scale(0.75)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><rect x="-25" y="-25" width="50" height="50" rx="7" fill="#0a2630" stroke="#72eee0"/><rect x="-13" y="-13" width="26" height="26" rx="3" fill="none" stroke="#70b6ff"/><path d="M-16 -36V-25M-16 25V36M-36 -16H-25M25 -16H36" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M0 -36V-25M0 25V36M-36 0H-25M25 0H36" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M16 -36V-25M16 25V36M-36 16H-25M25 16H36" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="0" r="4" stroke="#72eee0" fill="#72eee0" class="breathe"/></g></g><text x="140" y="63" font-size="34" fill="#ecfafb" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Intelligence at the edge</text><text x="142" y="99" font-size="12" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">SENSORS / FIRMWARE / MODULES</text><g transform="translate(1012 84) scale(0.55)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0a2733" stroke="#286171"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#366d80"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#5fe3d8" fill-opacity=".12" stroke="#72eee0" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#70b6ff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#91f6df"/></g></g><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
Before Width: | Height: | Size: 3.1 KiB After Width: | Height: | Size: 4.1 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">Evidence before accuracy claims</title><desc id="desc">Animated section heading. BENCHMARKS / REPRODUCERS / LIMITS</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(76 75) scale(0.75)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M0 -38L31 -20V18L0 38L-31 18V-20ZM-31 -20L0 -1L31 -20M0 -1V38" fill="none" stroke="#78cfff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M-15 1L-5 11L17 -12" fill="none" stroke="#438fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/></g></g><text x="140" y="63" font-size="34" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Evidence before accuracy claims</text><text x="142" y="99" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">BENCHMARKS / REPRODUCERS / LIMITS</text><g transform="translate(1012 84) scale(0.55)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#78cfff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,22 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">Evidence before accuracy claims</title><desc id="desc">Animated section heading. BENCHMARKS / REPRODUCERS / LIMITS</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{stroke-dasharray:3 14;animation:flow 8s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
|
||||
.layer{animation:layer 6s ease-in-out infinite}
|
||||
@keyframes layer{50%{transform:translateY(-5px)}}
|
||||
|
||||
|
||||
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(76 75) scale(0.75)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><g class="layer" style="animation-delay:-0.0s"><path d="M-38 -23L0 -42L38 -23L0 -4Z" fill="#102749" stroke="#78cfff"/><path d="M-38 -23V-16L0 3L38 -16V-23M0 -4V3" fill="#09172c" stroke="#315685"/></g><g class="layer" style="animation-delay:-0.7s"><path d="M-38 -10L0 -29L38 -10L0 9Z" fill="#102749" stroke="#438fff"/><path d="M-38 -10V-3L0 16L38 -3V-10M0 9V16" fill="#09172c" stroke="#315685"/></g><g class="layer" style="animation-delay:-1.4s"><path d="M-38 3L0 -16L38 3L0 22Z" fill="#102749" stroke="#438fff"/><path d="M-38 3V10L0 29L38 10V3M0 22V29" fill="#09172c" stroke="#315685"/></g><path d="M-12 -31L0 -37L12 -31L0 -25Z" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M-26 22V39" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M0 22V39" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M26 22V39" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/></g></g><text x="140" y="63" font-size="34" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Evidence before accuracy claims</text><text x="142" y="99" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">BENCHMARKS / REPRODUCERS / LIMITS</text><g transform="translate(1012 84) scale(0.55)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#78cfff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M24 1H1176" stroke="url(#accent)" opacity=".55"/><path d="M24 147H1176" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 4.6 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">Evidence before accuracy claims</title><desc id="desc">Animated section heading. BENCHMARKS / REPRODUCERS / LIMITS</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(76 75) scale(0.75)"><path d="M0 -38L31 -20V18L0 38L-31 18V-20ZM-31 -20L0 -1L31 -20M0 -1V38" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M-15 1L-5 11L17 -12" fill="none" stroke="#70b6ff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/></g><text x="140" y="63" font-size="34" fill="#ecfafb" text-anchor="start" >Evidence before accuracy claims</text><text x="142" y="99" font-size="12" fill="#72eee0" text-anchor="start" class="mono">BENCHMARKS / REPRODUCERS / LIMITS</text><path d="M865 44Q960 10 1124 52" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.65"/><path d="M865 61Q960 38 1124 69" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.55"/><path d="M865 78Q960 66 1124 86" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.45"/><path d="M865 95Q960 94 1124 103" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.35"/><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">Evidence before accuracy claims</title><desc id="desc">Animated section heading. BENCHMARKS / REPRODUCERS / LIMITS</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(76 75) scale(0.75)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M0 -38L31 -20V18L0 38L-31 18V-20ZM-31 -20L0 -1L31 -20M0 -1V38" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M-15 1L-5 11L17 -12" fill="none" stroke="#70b6ff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/></g></g><text x="140" y="63" font-size="34" fill="#ecfafb" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Evidence before accuracy claims</text><text x="142" y="99" font-size="12" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">BENCHMARKS / REPRODUCERS / LIMITS</text><g transform="translate(1012 84) scale(0.55)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0a2733" stroke="#286171"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#366d80"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#5fe3d8" fill-opacity=".12" stroke="#72eee0" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#70b6ff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#91f6df"/></g></g><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">Explore the capabilities</title><desc id="desc">Animated section heading. SENSING / MODELS / AUTOMATION</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(76 75) scale(0.75)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><circle cx="0" cy="-28" r="8" stroke="#b8f36c" fill="none" /><path d="M0 -20V12M-24 -6L0 -14L24 -6M0 12L-18 38M0 12L18 38" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="-14" r="2.3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="0" cy="12" r="2.3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="-24" cy="-6" r="2.3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="24" cy="-6" r="2.3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><ellipse cx="0" cy="14" rx="40" ry="13" fill="none" stroke="#438fff" stroke-dasharray="3 8" class="flow"/></g></g><text x="140" y="63" font-size="34" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Explore the capabilities</text><text x="142" y="99" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">SENSING / MODELS / AUTOMATION</text><g transform="translate(1012 84) scale(0.55)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#78cfff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
After Width: | Height: | Size: 4.4 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">Explore the capabilities</title><desc id="desc">Animated section heading. SENSING / MODELS / AUTOMATION</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{stroke-dasharray:3 14;animation:flow 8s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
|
||||
.sweep{animation:sweep 5s ease-in-out infinite;transform-origin:0 0}
|
||||
@keyframes sweep{0%,100%{transform:rotate(-20deg)}50%{transform:rotate(155deg)}}
|
||||
|
||||
|
||||
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(76 75) scale(0.75)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><ellipse rx="18" ry="8.64" fill="none" stroke="#78cfff" opacity=".4"/><ellipse rx="32" ry="15.36" fill="none" stroke="#78cfff" opacity=".4"/><ellipse rx="46" ry="22.08" fill="none" stroke="#78cfff" opacity=".4"/><path d="M-46 0H46M0 -23V23" fill="none" stroke="#438fff" stroke-width="0.7" stroke-linecap="round" stroke-linejoin="round" /><g class="sweep"><path d="M0 0L40 -18" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="34" cy="-15" r="3" stroke="#b8f36c" fill="#b8f36c"/></g><circle cx="-20" cy="8" r="3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="12" cy="13" r="3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="23" cy="-8" r="3" stroke="#ffb45e" fill="#ffb45e" class="spark"/></g></g><text x="140" y="63" font-size="34" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Explore the capabilities</text><text x="142" y="99" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">SENSING / MODELS / AUTOMATION</text><g transform="translate(1012 84) scale(0.55)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#78cfff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M24 1H1176" stroke="url(#accent)" opacity=".55"/><path d="M24 147H1176" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 4.3 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">Explore the capabilities</title><desc id="desc">Animated section heading. SENSING / MODELS / AUTOMATION</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(76 75) scale(0.75)"><circle cx="0" cy="-28" r="8" stroke="#72eee0" fill="none" /><path d="M0 -20V12M-24 -6L0 -14L24 -6M0 12L-18 38M0 12L18 38" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><ellipse cx="0" cy="14" rx="40" ry="13" fill="none" stroke="#70b6ff" stroke-dasharray="3 8" class="flow"/></g><text x="140" y="63" font-size="34" fill="#ecfafb" text-anchor="start" >Explore the capabilities</text><text x="142" y="99" font-size="12" fill="#72eee0" text-anchor="start" class="mono">SENSING / MODELS / AUTOMATION</text><path d="M865 44Q960 10 1124 52" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.65"/><path d="M865 61Q960 38 1124 69" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.55"/><path d="M865 78Q960 66 1124 86" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.45"/><path d="M865 95Q960 94 1124 103" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.35"/><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">Explore the capabilities</title><desc id="desc">Animated section heading. SENSING / MODELS / AUTOMATION</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(76 75) scale(0.75)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><circle cx="0" cy="-28" r="8" stroke="#72eee0" fill="none" /><path d="M0 -20V12M-24 -6L0 -14L24 -6M0 12L-18 38M0 12L18 38" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><ellipse cx="0" cy="14" rx="40" ry="13" fill="none" stroke="#70b6ff" stroke-dasharray="3 8" class="flow"/></g></g><text x="140" y="63" font-size="34" fill="#ecfafb" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Explore the capabilities</text><text x="142" y="99" font-size="12" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">SENSING / MODELS / AUTOMATION</text><g transform="translate(1012 84) scale(0.55)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0a2733" stroke="#286171"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#366d80"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#5fe3d8" fill-opacity=".12" stroke="#72eee0" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#70b6ff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#91f6df"/></g></g><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.8 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">From recordings to models</title><desc id="desc">Animated section heading. CAPTURE / TRAIN / EVALUATE</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(76 75) scale(0.75)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M-42 6H-27L-18 -10L-9 25L3 -25L14 10L24 -3L31 6H43" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/><path d="M-42 31H43" fill="none" stroke="#233f63" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /></g></g><text x="140" y="63" font-size="34" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >From recordings to models</text><text x="142" y="99" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">CAPTURE / TRAIN / EVALUATE</text><g transform="translate(1012 84) scale(0.55)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#78cfff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
After Width: | Height: | Size: 4.0 KiB |
@@ -0,0 +1,23 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">From recordings to models</title><desc id="desc">Animated section heading. CAPTURE / TRAIN / EVALUATE</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{stroke-dasharray:3 14;animation:flow 8s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
|
||||
.cursor{animation:cursor 5s ease-in-out infinite}
|
||||
@keyframes cursor{0%,100%{transform:translateX(-35px);opacity:.3}50%{transform:translateX(35px);opacity:1}}
|
||||
|
||||
|
||||
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(76 75) scale(0.75)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><path d="M-49 -14C-34 -14 -30 -35 -17 -35S0 7 14 7S34 -14 49 -14" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="trace" style="animation-delay:-0s" opacity="1.0"/><path d="M-49 -2C-34 -2 -30 -23 -17 -23S0 19 14 19S34 -2 49 -2" fill="none" stroke="#b8f36c" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="trace" style="animation-delay:-1s" opacity="0.8"/><path d="M-49 10C-34 10 -30 -11 -17 -11S0 31 14 31S34 10 49 10" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="trace" style="animation-delay:-2s" opacity="0.6"/><g class="cursor"><path d="M0 -42V40" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="-8" r="3" stroke="#ffb45e" fill="#ffb45e"/></g></g></g><text x="140" y="63" font-size="34" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >From recordings to models</text><text x="142" y="99" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">CAPTURE / TRAIN / EVALUATE</text><g transform="translate(1012 84) scale(0.55)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#78cfff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M24 1H1176" stroke="url(#accent)" opacity=".55"/><path d="M24 147H1176" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 4.5 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">From recordings to models</title><desc id="desc">Animated section heading. CAPTURE / TRAIN / EVALUATE</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(76 75) scale(0.75)"><path d="M-42 6H-27L-18 -10L-9 25L3 -25L14 10L24 -3L31 6H43" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/><path d="M-42 31H43" fill="none" stroke="#214451" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /></g><text x="140" y="63" font-size="34" fill="#ecfafb" text-anchor="start" >From recordings to models</text><text x="142" y="99" font-size="12" fill="#72eee0" text-anchor="start" class="mono">CAPTURE / TRAIN / EVALUATE</text><path d="M865 44Q960 10 1124 52" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.65"/><path d="M865 61Q960 38 1124 69" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.55"/><path d="M865 78Q960 66 1124 86" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.45"/><path d="M865 95Q960 94 1124 103" fill="none" stroke="#72eee0" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow" opacity="0.35"/><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="148" viewBox="0 0 1200 148" role="img" aria-labelledby="title desc"><title id="title">From recordings to models</title><desc id="desc">Animated section heading. CAPTURE / TRAIN / EVALUATE</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="147" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(76 75) scale(0.75)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M-42 6H-27L-18 -10L-9 25L3 -25L14 10L24 -3L31 6H43" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/><path d="M-42 31H43" fill="none" stroke="#214451" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /></g></g><text x="140" y="63" font-size="34" fill="#ecfafb" text-anchor="start" font-weight="650" letter-spacing="-0.6" >From recordings to models</text><text x="142" y="99" font-size="12" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">CAPTURE / TRAIN / EVALUATE</text><g transform="translate(1012 84) scale(0.55)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0a2733" stroke="#286171"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#366d80"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#5fe3d8" fill-opacity=".12" stroke="#72eee0" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#70b6ff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#91f6df"/></g></g><path d="M1140 70H1160M1152 62L1160 70L1152 78" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></svg>
|
||||
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 3.7 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="450" viewBox="0 0 1200 450" role="img" aria-labelledby="title desc"><title id="title">RuView: spaces become signals</title><desc id="desc">Animated conceptual RF field and room geometry. Explore the RuView user guide. This illustration is not a sensor measurement.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="449" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="48" y="44" font-size="13" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">RUVIEW / COGNITUM ECOSYSTEM</text><text x="48" y="121" font-size="76" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >RuView</text><text x="48" y="184" font-size="38" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Spaces become signals.</text><text x="48" y="234" font-size="32" fill="#78cfff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Signals become understanding.</text><text x="50" y="279" font-size="18" fill="#b0c2db" text-anchor="start" font-weight="500" >Explore RF sensing, edge inference and local automation.</text><rect x="48" y="326" width="270" height="52" rx="12" fill="url(#accent)"/><text x="72" y="359" font-size="20" fill="#071124" text-anchor="start" font-weight="500" >Explore the guide</text><path d="M272 352H292M285 345L292 352L285 359" fill="none" stroke="#071124" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><text x="49" y="419" font-size="11" fill="#b0c2db" text-anchor="start" font-weight="500" class="mono">RF SENSING / EDGE MODELS / LOCAL ACTION</text><circle cx="950" cy="235" r="235" fill="url(#halo)"/><path d="M750 282L610 443" fill="none" stroke="#162f50" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M790 282L690 443" fill="none" stroke="#162f50" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M830 282L770 443" fill="none" stroke="#162f50" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M870 282L850 443" fill="none" stroke="#162f50" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M910 282L930 443" fill="none" stroke="#162f50" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M950 282L1010 443" fill="none" stroke="#162f50" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M990 282L1090 443" fill="none" stroke="#162f50" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M1030 282L1170 443" fill="none" stroke="#162f50" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M1070 282L1250 443" fill="none" stroke="#162f50" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M680 306H1168" fill="none" stroke="#162f50" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M680 336H1168" fill="none" stroke="#162f50" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M680 372H1168" fill="none" stroke="#162f50" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M680 418H1168" fill="none" stroke="#162f50" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M803 157L1008 106L1137 177L930 236ZM803 157V314L930 391L1137 324V177M930 236V391M1008 106V268L803 314M1008 268L1137 324" fill="none" stroke="#5490c5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></g><g transform="translate(0 80)"><g class="scan"><path d="M803 157L1008 106L1137 177L930 236Z" fill="#78cfff" fill-opacity=".1" stroke="#78cfff" stroke-opacity=".6"/></g></g><g transform="translate(974 282)"><g class="orbit"><ellipse rx="135" ry="36" fill="none" stroke="#438fff" stroke-dasharray="3 11" opacity=".4"/><circle cx="135" cy="0" r="4" fill="#9ee7ff"/></g></g><g transform="translate(974 312)"><ellipse rx="140" ry="44" fill="none" stroke="#438fff" class="wave" style="animation-delay:-0.0s"/></g><g transform="translate(974 312)"><ellipse rx="140" ry="44" fill="none" stroke="#438fff" class="wave" style="animation-delay:-1.3s"/></g><g transform="translate(974 312)"><ellipse rx="140" ry="44" fill="none" stroke="#438fff" class="wave" style="animation-delay:-2.6s"/></g><circle cx="849" cy="198" r="1.7" stroke="#438fff" fill="#438fff" class="spark" style="animation-delay:-0.0s"/><circle cx="893" cy="198" r="1.7" stroke="#438fff" fill="#438fff" class="spark" style="animation-delay:-0.5s"/><circle cx="937" cy="198" r="1.7" stroke="#438fff" fill="#438fff" class="spark" style="animation-delay:-1.0s"/><circle cx="981" cy="198" r="1.7" stroke="#438fff" fill="#438fff" class="spark" style="animation-delay:-1.5s"/><circle cx="1025" cy="198" r="1.7" stroke="#438fff" fill="#438fff" class="spark" style="animation-delay:-2.0s"/><circle cx="1069" cy="198" r="1.7" stroke="#438fff" fill="#438fff" class="spark" style="animation-delay:-2.5s"/><circle cx="849" cy="233" r="1.7" stroke="#438fff" fill="#438fff" class="spark" style="animation-delay:-3.0s"/><circle cx="893" cy="233" r="1.7" stroke="#438fff" fill="#438fff" class="spark" style="animation-delay:-0.0s"/><circle cx="937" cy="233" r="1.7" stroke="#438fff" fill="#438fff" class="spark" style="animation-delay:-0.5s"/><circle cx="981" cy="233" r="1.7" stroke="#43Line truncated
|
||||
|
After Width: | Height: | Size: 12 KiB |
@@ -0,0 +1,31 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="450" viewBox="0 0 1200 450" role="img" aria-labelledby="title desc"><title id="title">RuView: invisible signals, visible possibility</title><desc id="desc">Animated conceptual RF room with an articulated pose, moving scan plane and propagating waves. Illustration only, not a sensor measurement.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
|
||||
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
|
||||
|
||||
.limb{animation:limb 3s ease-in-out infinite;transform-origin:0 -18px}
|
||||
.stride{animation:limb 3s ease-in-out infinite reverse;transform-origin:5px 16px}
|
||||
.layer{animation:layer 6s ease-in-out infinite}
|
||||
|
||||
|
||||
@keyframes limb{0%,100%{transform:rotate(-9deg)}50%{transform:rotate(9deg)}}
|
||||
@keyframes layer{50%{transform:translateY(-5px)}}
|
||||
|
||||
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
|
||||
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="449" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="48" y="45" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">RUVIEW / SPATIAL INTELLIGENCE</text><text x="44" y="140" font-size="59" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Invisible signals.</text><text x="44" y="204" font-size="55" fill="#b8f36c" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Visible possibility.</text><text x="48" y="256" font-size="18" fill="#b0c2db" text-anchor="start" font-weight="500" >Explore the space between sensing and understanding.</text><text x="48" y="284" font-size="18" fill="#b0c2db" text-anchor="start" font-weight="500" >WiFi sensing. Edge models. Local automation.</text><rect x="48" y="327" width="240" height="49" rx="24" fill="url(#accent)"/><text x="72" y="358" font-size="18" fill="#071124" text-anchor="start" font-weight="500" >Enter the field</text><path d="M246 351H266M259 344L266 351L259 358" fill="none" stroke="#071124" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><text x="49" y="418" font-size="10" fill="#b0c2db" text-anchor="start" font-weight="500" class="mono">RF SENSING / OPEN RESEARCH / EDGE COMPUTE</text><circle cx="955" cy="230" r="234" fill="url(#halo)"/><path d="M785 270L648 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M809 270L739 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M833 270L830 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M857 270L921 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M881 270L1012 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M905 270L1103 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M929 270L1194 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M768 286H1110" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M741 317H1126" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M714 348H1142" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M687 379H1158" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M660 410H1174" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M780 295L963 211L1148 303L960 400Z" fill="#0d2342" fill-opacity=".65" stroke="#315685"/><path d="M780 295V153L963 70L1148 162V303M780 153L960 244L1148 162M960 244V400" fill="none" stroke="#438fff" opacity=".55"/><path d="M963 70V211L1148 303V162Z" fill="#438fff" fill-opacity=".05"/><g class="scan"><path d="M780 286L963 202L1148 294L960 390Z" fill="#78cfff" fill-opacity=".08" stroke="#78cfff" stroke-opacity=".6"/></g><g transform="translate(957 320)"><ellipse rx="90" ry="30" fill="none" stroke="#78cfff" class="wave" style="animation-delay:-0.0s"/></g><g transform="translate(957 320)"><ellipse rx="116" ry="38" fill="none" stroke="#78cfff" class="wave" style="animation-delay:-1.3s"/></g><g transform="translate(957 320)"><ellipse rx="142" ry="46" fill="none" stroke="#78cfff" class="wave" style="animation-delay:-2.6s"/></g><g transform="translate(959 236) scale(1.8)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><circle cx="0" cy="-32" r="7" stroke="#b8f36c" fill="none" /><path d="M0 -25L-3 -2L5 16" fill="none" stroke="#b8f36c" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" /><g class="limb"><path d="M-1 -18L-19 -5L-31 -14M-1 -18L18 -11L28 -25" fill="none" stroke="#b8f36c" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" /></g><g class="stride"><path d="M5 16L-12 28L-22 47M5 16L24 28L33 44" fill="none" stroke="#b8f36c" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" /></g><circle cx="-1" cy="-18" r="2.8" stroke="#ffb45e" fill="#ffb45e"/><circle cx="-3" cy="-2" r="2.8" stroke="#ffb45e" fill="#ffb45e"/><circle cx="5" cy="16" r="2.8" stroke="#ffb45e" fill="#ffb45e"/><circle cx="-19" cy="-5" r="2.8" stroke="#ffb45e" fill="#ffb45e"/><circle cx="18" cy="-11" r="2.8" stroke="#ffb45e" fill="#ffb45e"/><path d="M-42 -42V-50H-30M30 -50H42V-42M42 45V53H30M-30 53H-42V45" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /></g></g><g transform="translate(771 324) scale(0.72)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><g class="layer" style="animation-delay:-0.0s"><path d="M-38 -23L0 -42L38 -2Line truncated
|
||||
|
After Width: | Height: | Size: 9.0 KiB |
@@ -0,0 +1,31 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="450" viewBox="0 0 1200 450" role="img" aria-labelledby="title desc"><title id="title">RuView: invisible signals, visible possibility</title><desc id="desc">Animated conceptual RF room with an anatomical human silhouette, moving scan plane and propagating waves. Illustration only, not a sensor measurement.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
|
||||
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
|
||||
|
||||
.limb{animation:limb 3s ease-in-out infinite;transform-origin:0 -18px}
|
||||
.stride{animation:limb 3s ease-in-out infinite reverse;transform-origin:5px 16px}
|
||||
.layer{animation:layer 6s ease-in-out infinite}
|
||||
|
||||
|
||||
@keyframes limb{0%,100%{transform:rotate(-3deg)}50%{transform:rotate(3deg)}}
|
||||
@keyframes layer{50%{transform:translateY(-5px)}}
|
||||
|
||||
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
|
||||
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="449" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="48" y="45" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">RUVIEW / SPATIAL INTELLIGENCE</text><text x="44" y="140" font-size="59" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Invisible signals.</text><text x="44" y="204" font-size="55" fill="#b8f36c" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Visible possibility.</text><text x="48" y="256" font-size="18" fill="#b0c2db" text-anchor="start" font-weight="500" >Explore the space between sensing and understanding.</text><text x="48" y="284" font-size="18" fill="#b0c2db" text-anchor="start" font-weight="500" >WiFi sensing. Edge models. Local automation.</text><rect x="48" y="327" width="240" height="49" rx="24" fill="url(#accent)"/><text x="72" y="358" font-size="18" fill="#071124" text-anchor="start" font-weight="500" >Enter the field</text><path d="M246 351H266M259 344L266 351L259 358" fill="none" stroke="#071124" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><text x="49" y="418" font-size="10" fill="#b0c2db" text-anchor="start" font-weight="500" class="mono">RF SENSING / OPEN RESEARCH / EDGE COMPUTE</text><circle cx="955" cy="230" r="234" fill="url(#halo)"/><path d="M785 270L648 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M809 270L739 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M833 270L830 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M857 270L921 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M881 270L1012 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M905 270L1103 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M929 270L1194 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M768 286H1110" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M741 317H1126" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M714 348H1142" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M687 379H1158" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M660 410H1174" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M780 295L963 211L1148 303L960 400Z" fill="#0d2342" fill-opacity=".65" stroke="#315685"/><path d="M780 295V153L963 70L1148 162V303M780 153L960 244L1148 162M960 244V400" fill="none" stroke="#438fff" opacity=".55"/><path d="M963 70V211L1148 303V162Z" fill="#438fff" fill-opacity=".05"/><g class="scan"><path d="M780 286L963 202L1148 294L960 390Z" fill="#78cfff" fill-opacity=".08" stroke="#78cfff" stroke-opacity=".6"/></g><g transform="translate(957 320)"><ellipse rx="90" ry="30" fill="none" stroke="#78cfff" class="wave" style="animation-delay:-0.0s"/></g><g transform="translate(957 320)"><ellipse rx="116" ry="38" fill="none" stroke="#78cfff" class="wave" style="animation-delay:-1.3s"/></g><g transform="translate(957 320)"><ellipse rx="142" ry="46" fill="none" stroke="#78cfff" class="wave" style="animation-delay:-2.6s"/></g><g transform="translate(959 236) scale(1.8)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><defs><linearGradient id="humanSurface" x1="0" y1="0" x2="1" y2=".3"><stop stop-color="#153d48"/><stop offset=".42" stop-color="#338469"/><stop offset=".7" stop-color="#83c766"/><stop offset="1" stop-color="#163c4a"/></linearGradient><linearGradient id="humanRear"><stop stop-color="#0f2d40"/><stop offset="1" stop-color="#397a68"/></linearGradient></defs><g stroke="#88d783" stroke-width=".65" stroke-linejoin="round"><g class="limb"><path d="M-9 -25C-15 -25 -18 -19 -21 -13L-29 -5C-32 -3 -33 1 -30 3L-25 2L-14 -7C-9 -12 -6 -19 -6 -23Z" fill="url(#humanRear)"/></g><g class="stride"><path d="M-6 5C-13 10 -15 18 -17 26L-25 43L-29 47C-30 49 -28 51 -24 51L-17 50L-16 46L-8 30C-4 23 2 15 3 10Z" fill="url(#humanRear)"/><path d="M3 5C10 7 14 17 19 23C24 28 23 35 24 41L29 45C32 47 32 49 28 50L20 49C17 48 17 46 17 42L14 32C8 28 2 23 -3 16L-4 10Z" fill="url(#humanSurface)"/></g><g class="limb"><path d="M7 -26C14 -25 17 -20 20 -13L24 -17L29 -24C30 -27 33 -29 35 -26L35 -22L30 -16L24 -8C22 -5 18 -5 16 -8L8 -17Z" fill="url(#humanSurface)"/></g><path d="M-4 -32L4 -33L6 -28C11 -27 14 -24 13 -19L9 -5L10 7C10 13 2 15 -5 12L-10 7L-8 -5L-12 -20C-13 -25 -8 -28 -5 -28Z" fill="url(#humanSurface)"/><path d="M-6 -42C-6 -48 2 -51 7 -47C10Line truncated
|
||||
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1,35 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="450" viewBox="0 0 1200 450" role="img" aria-labelledby="title desc"><title id="title">RuView: invisible signals, visible possibility</title><desc id="desc">Animated conceptual RF room with an anatomical human silhouette, moving scan plane and propagating waves. Illustration only, not a sensor measurement.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
|
||||
.layer{animation:layer 6s ease-in-out infinite}
|
||||
@keyframes layer{50%{transform:translateY(-5px)}}
|
||||
|
||||
|
||||
.humanTurn{transform-box:fill-box;transform-origin:center 85%;animation:humanTurn 8s ease-in-out infinite}
|
||||
.humanBody{animation:humanBody 1.6s ease-in-out infinite}
|
||||
.armFar{transform-origin:-9px -24px;animation:armSwing 1.6s ease-in-out infinite}
|
||||
.armNear{transform-origin:10px -23px;animation:armSwing 1.6s ease-in-out infinite;animation-delay:-.8s}
|
||||
.legFar{transform-origin:-3px 9px;animation:legSwing 1.6s ease-in-out infinite;animation-delay:-.8s}
|
||||
.legNear{transform-origin:2px 9px;animation:legSwing 1.6s ease-in-out infinite}
|
||||
.humanLight{animation:humanLight 8s ease-in-out infinite}
|
||||
.gaitShadow{transform-box:fill-box;transform-origin:center;animation:gaitShadow 1.6s ease-in-out infinite}
|
||||
@keyframes humanTurn{0%,100%{transform:perspective(450px) rotateY(-24deg) rotateZ(-2deg)}50%{transform:perspective(450px) rotateY(24deg) rotateZ(2deg)}}
|
||||
@keyframes humanBody{0%,50%,100%{transform:translateY(0)}25%,75%{transform:translateY(-2px)}}
|
||||
@keyframes armSwing{0%,100%{transform:rotate(-17deg)}50%{transform:rotate(17deg)}}
|
||||
@keyframes legSwing{0%,100%{transform:rotate(-12deg)}50%{transform:rotate(12deg)}}
|
||||
@keyframes humanLight{0%,100%{opacity:.1}50%{opacity:.45}}
|
||||
@keyframes gaitShadow{0%,50%,100%{transform:scaleX(1);opacity:.7}25%,75%{transform:scaleX(.85);opacity:.45}}
|
||||
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="449" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="48" y="45" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">RUVIEW / SPATIAL INTELLIGENCE</text><text x="44" y="140" font-size="59" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Invisible signals.</text><text x="44" y="204" font-size="55" fill="#b8f36c" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Visible possibility.</text><text x="48" y="256" font-size="18" fill="#b0c2db" text-anchor="start" font-weight="500" >Explore the space between sensing and understanding.</text><text x="48" y="284" font-size="18" fill="#b0c2db" text-anchor="start" font-weight="500" >WiFi sensing. Edge models. Local automation.</text><rect x="48" y="327" width="240" height="49" rx="24" fill="url(#accent)"/><text x="72" y="358" font-size="18" fill="#071124" text-anchor="start" font-weight="500" >Enter the field</text><path d="M246 351H266M259 344L266 351L259 358" fill="none" stroke="#071124" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><text x="49" y="418" font-size="10" fill="#b0c2db" text-anchor="start" font-weight="500" class="mono">RF SENSING / OPEN RESEARCH / EDGE COMPUTE</text><circle cx="955" cy="230" r="234" fill="url(#halo)"/><path d="M785 270L648 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M809 270L739 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M833 270L830 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M857 270L921 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M881 270L1012 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M905 270L1103 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M929 270L1194 432" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M768 286H1110" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M741 317H1126" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M714 348H1142" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M687 379H1158" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M660 410H1174" fill="none" stroke="#1b3558" stroke-width="0.8" stroke-linecap="round" stroke-linejoin="round" /><path d="M780 295L963 211L1148 303L960 400Z" fill="#0d2342" fill-opacity=".65" stroke="#315685"/><path d="M780 295V153L963 70L1148 162V303M780 153L960 244L1148 162M960 244V400" fill="none" stroke="#438fff" opacity=".55"/><path d="M963 70V211L1148 303V162Z" fill="#438fff" fill-opacity=".05"/><g class="scan"><path d="M780 286L963 202L1148 294L960 390Z" fill="#78cfff" fill-opacity=".08" stroke="#78cfff" stroke-opacity=".6"/></g><g transform="translate(957 320)"><ellipse rx="90" ry="30" fill="none" stroke="#78cfff" class="wave" style="animation-delay:-0.0s"/></g><g transform="translate(957 320)"><ellipse rx="116" ry="38" fill="none" stroke="#78cfff" class="wave" style="animation-delay:-1.3s"/></g><g transform="translate(957 320)"><ellipse rx="142" ry="46" fill="none" stroke="#78cfff" class="wave" style="animation-delay:-2.6s"/></g><g transform="translate(959 236) scale(1.8)"><ellipse cy="53" rx="37" ry="7" fill="#020816" class="gaitShadow"/><g class="humanBody"><defs><linearGradient id="humanSurface" x1="0" y1="0" x2="1" y2=".3"><stop stop-color="#153d48"/><stop offset=".42" stop-color="#338469"/><stop offset=".7" stop-color="#83c766"/><stop offset="1" stop-color="#163c4a"/></linearGradient><linearGradient id="humanRear"><stop stop-color="#0f2d40"/><stop offset="1" stop-color="#397a68"/></linearGradient></defs><g class="humanTurn"><g stroke="#88d783" stroke-width=".65" stroke-linejoin="round"><g class="armFar"><path d="M-9 -25C-15 -25 -18 -19 -21 -13L-29 -5C-32 -3 -33 1 -30 3L-25 2L-14 -7C-9 -12 -6 -19 -6 -23Z" fill="url(#humanRear)"/></g><g class="legFar"><path d="M-6 5C-13 10 -15 18 -17 26L-25 43L-29 47C-30 49 -28 51 -24 51L-17 50L-16 46L-8 30C-4 23 2 15 3 10Z" fill="url(#humanRear)"/><circle cx="-15" cy="26" r="1.8" fill="#ffb45e"/></g><g class="legNear"><path d="M3 5C10 7 14 17 19 23C24 28 23 35 24 41L29 45C32 47 32 49 28 50L20 49C17 48 17 46 17 42L14 32C8 28 2 23 -3 16L-4 10Z" fill="url(#humanSurface)"/><circle cx="19" cy="28" r="1.8" fill="#ffb45e"/></g><g class="armNear"><path d="M7 -26C14 -25 17 -20 20 -13L24 -17L29 -24C30 -27 33 -29 35 -26L35 -22L30 -16L24 -8C22 -5 18 -5 16 -8L8 -17Z" fill="url(#humanSurface)"/></g><path d="M-4 -32L4 -33L6 -28C11Line truncated
|
||||
|
After Width: | Height: | Size: 12 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="450" viewBox="0 0 1200 450" role="img" aria-labelledby="title desc"><title id="title">RuView: spaces become signals</title><desc id="desc">Animated conceptual RF field and room geometry. Explore the RuView user guide. This illustration is not a sensor measurement.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="1199" height="449" rx="18" fill="url(#wash)" stroke="#245360"/><text x="48" y="44" font-size="13" fill="#72eee0" text-anchor="start" class="mono">RUVIEW / COGNITUM ECOSYSTEM</text><text x="48" y="121" font-size="76" fill="#ecfafb" text-anchor="start" >RuView</text><text x="48" y="184" font-size="38" fill="#ecfafb" text-anchor="start" >Spaces become signals.</text><text x="48" y="234" font-size="32" fill="#72eee0" text-anchor="start" >Signals become understanding.</text><text x="50" y="279" font-size="18" fill="#9abac5" text-anchor="start" >Explore RF sensing, edge inference and local automation.</text><rect x="48" y="326" width="270" height="52" rx="12" fill="url(#accent)"/><text x="72" y="359" font-size="20" fill="#041117" text-anchor="start" >Explore the guide</text><path d="M272 352H292M285 345L292 352L285 359" fill="none" stroke="#041117" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><text x="49" y="419" font-size="11" fill="#9abac5" text-anchor="start" class="mono">RF SENSING / EDGE MODELS / LOCAL ACTION</text><circle cx="950" cy="235" r="235" fill="url(#halo)"/><path d="M750 282L610 443" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M790 282L690 443" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M830 282L770 443" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M870 282L850 443" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M910 282L930 443" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M950 282L1010 443" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M990 282L1090 443" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M1030 282L1170 443" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M1070 282L1250 443" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M680 306H1168" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M680 336H1168" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M680 372H1168" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M680 418H1168" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M803 157L1008 106L1137 177L930 236ZM803 157V314L930 391L1137 324V177M930 236V391M1008 106V268L803 314M1008 268L1137 324" fill="none" stroke="#4b9ba4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><g transform="translate(976 265) scale(1.35)"><circle cx="0" cy="-28" r="8" stroke="#72eee0" fill="none" /><path d="M0 -20V12M-24 -6L0 -14L24 -6M0 12L-18 38M0 12L18 38" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><ellipse cx="0" cy="14" rx="40" ry="13" fill="none" stroke="#70b6ff" stroke-dasharray="3 8" class="flow"/></g><g transform="translate(780 349) scale(0.7)"><rect x="-25" y="-25" width="50" height="50" rx="7" fill="#0a2630" stroke="#72eee0"/><rect x="-13" y="-13" width="26" height="26" rx="3" fill="none" stroke="#70b6ff"/><path d="M-16 -36V-25M-16 25V36M-36 -16H-25M25 Line truncated
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="450" viewBox="0 0 1200 450" role="img" aria-labelledby="title desc"><title id="title">RuView: spaces become signals</title><desc id="desc">Animated conceptual RF field and room geometry. Explore the RuView user guide. This illustration is not a sensor measurement.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="449" rx="18" fill="url(#wash)" stroke="#245360"/><text x="48" y="44" font-size="13" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">RUVIEW / COGNITUM ECOSYSTEM</text><text x="48" y="121" font-size="76" fill="#ecfafb" text-anchor="start" font-weight="650" letter-spacing="-0.6" >RuView</text><text x="48" y="184" font-size="38" fill="#ecfafb" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Spaces become signals.</text><text x="48" y="234" font-size="32" fill="#72eee0" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Signals become understanding.</text><text x="50" y="279" font-size="18" fill="#9abac5" text-anchor="start" font-weight="500" >Explore RF sensing, edge inference and local automation.</text><rect x="48" y="326" width="270" height="52" rx="12" fill="url(#accent)"/><text x="72" y="359" font-size="20" fill="#041117" text-anchor="start" font-weight="500" >Explore the guide</text><path d="M272 352H292M285 345L292 352L285 359" fill="none" stroke="#041117" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><text x="49" y="419" font-size="11" fill="#9abac5" text-anchor="start" font-weight="500" class="mono">RF SENSING / EDGE MODELS / LOCAL ACTION</text><circle cx="950" cy="235" r="235" fill="url(#halo)"/><path d="M750 282L610 443" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M790 282L690 443" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M830 282L770 443" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M870 282L850 443" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M910 282L930 443" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M950 282L1010 443" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M990 282L1090 443" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M1030 282L1170 443" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M1070 282L1250 443" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M680 306H1168" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M680 336H1168" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M680 372H1168" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><path d="M680 418H1168" fill="none" stroke="#12343d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M803 157L1008 106L1137 177L930 236ZM803 157V314L930 391L1137 324V177M930 236V391M1008 106V268L803 314M1008 268L1137 324" fill="none" stroke="#4b9ba4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></g><g transform="translate(0 80)"><g class="scan"><path d="M803 157L1008 106L1137 177L930 236Z" fill="#72eee0" fill-opacity=".1" stroke="#72eee0" stroke-opacity=".6"/></g></g><g transform="translate(974 282)"><g class="orbit"><ellipse rx="135" ry="36" fill="none" stroke="#70b6ff" stroke-dasharray="3 11" opacity=".4"/><circle cx="135" cy="0" r="4" fill="#91f6df"/></g></g><circle cx="849" cy="198" r="1.7" stroke="#70b6ff" fill="#70b6ff" class="spark" style="animation-delay:-0.0s"/><circle cx="893" cy="198" r="1.7" stroke="#70b6ff" fill="#70b6ff" class="spark" style="animation-delay:-0.5s"/><circle cx="937" cy="198" r="1.7" stroke="#70b6ff" fill="#70b6ff" class="spark" style="animation-delay:-1.0s"/><circle cx="981" cy="198" r="1.7" stroke="#70b6ff" fill="#70b6ff" class="spark" style="animation-delay:-1.5s"/><circle cx="1025" cy="198" r="1.7" stroke="#70b6ff" fill="#70b6ff" class="spark" style="animation-delay:-2.0s"/><circle cx="1069" cy="198" r="1.7" stroke="#70b6ff" fill="#70b6ff" class="spark" style="animation-delay:-2.5s"/><circle cx="849" cy="233" r="1.7" stroke="#70b6ff" fill="#70b6ff" class="spark" style="animation-delay:-3.0s"/><circle cx="893" cy="233" r="1.7" stroke="#70b6ff" fill="#70b6ff" class="spark" style="animation-delay:-0.0s"/><circle cx="937" cy="233" r="1.7" stroke="#70b6ff" fill="#70b6ff" class="spark" style="animation-delay:-0.5s"/><circle cx="981" cy="233" r="1.7" stroke="#70b6ff" fill="#70b6ff" class="spark" style="animation-delay:-1.0s"/><circle cx="1025" cy="233" r="1.7" stroke="#70b6ff" fill="#70b6ff" class="spark" style="animation-delay:-1.5s"/><circle cx="1069" cy="233" r="1.7" stroke="#70b6ff" fill="#70b6ff" class="spark" style="animation-delay:-2.0s"/><circle cx="849" cy="268" r="1.7" stroke="#70b6ff" fill="#70b6ff" class="spark" style="animation-delay:-2.5s"/><circlLine truncated
|
||||
|
Before Width: | Height: | Size: 6.8 KiB After Width: | Height: | Size: 11 KiB |
@@ -1,143 +1,143 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"style": "Cognitum cyan on midnight",
|
||||
"style": "RuView spatial v4: navy glass, electric blue, lime and amber",
|
||||
"motion": "Decorative CSS; prefers-reduced-motion supported; no script or remote dependencies",
|
||||
"assets": [
|
||||
{
|
||||
"file": "assets/readme/hero.svg",
|
||||
"title": "RuView: spaces become signals",
|
||||
"file": "assets/readme/hero-spatial-v6.svg",
|
||||
"title": "RuView: invisible signals, visible possibility",
|
||||
"target": "docs/user-guide.md",
|
||||
"width": 1200,
|
||||
"height": 450
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/nav-start.svg",
|
||||
"file": "assets/readme/nav-start-spatial-v4.svg",
|
||||
"title": "Start here",
|
||||
"target": "docs/user-guide.md",
|
||||
"width": 180,
|
||||
"height": 116
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/nav-hardware.svg",
|
||||
"file": "assets/readme/nav-hardware-spatial-v4.svg",
|
||||
"title": "Hardware",
|
||||
"target": "firmware/esp32-csi-node/README.md",
|
||||
"width": 180,
|
||||
"height": 116
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/nav-models.svg",
|
||||
"file": "assets/readme/nav-models-spatial-v4.svg",
|
||||
"title": "Models",
|
||||
"target": "https://huggingface.co/ruvnet/wifi-densepose-pretrained",
|
||||
"width": 180,
|
||||
"height": 116
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/nav-agents.svg",
|
||||
"file": "assets/readme/nav-agents-spatial-v4.svg",
|
||||
"title": "AI toolkit",
|
||||
"target": "harness/ruview/README.md",
|
||||
"width": 180,
|
||||
"height": 116
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/nav-home.svg",
|
||||
"file": "assets/readme/nav-home-spatial-v4.svg",
|
||||
"title": "Smart home",
|
||||
"target": "docs/integrations/home-assistant.md",
|
||||
"width": 180,
|
||||
"height": 116
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/nav-seed.svg",
|
||||
"file": "assets/readme/nav-seed-spatial-v4.svg",
|
||||
"title": "Cognitum",
|
||||
"target": "https://cognitum.one/seed",
|
||||
"width": 180,
|
||||
"height": 116
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/feature-presence.svg",
|
||||
"file": "assets/readme/feature-presence-spatial-v4.svg",
|
||||
"title": "Presence & motion",
|
||||
"target": "docs/user-guide.md",
|
||||
"width": 580,
|
||||
"height": 276
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/feature-vitals.svg",
|
||||
"file": "assets/readme/feature-vitals-spatial-v4.svg",
|
||||
"title": "Breathing & vital signals",
|
||||
"target": "v2/crates/wifi-densepose-signal/README.md",
|
||||
"width": 580,
|
||||
"height": 276
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/feature-pose.svg",
|
||||
"file": "assets/readme/feature-pose-spatial-v6.svg",
|
||||
"title": "Pose research",
|
||||
"target": "docs/benchmarks/pose-estimation-cog.md",
|
||||
"width": 580,
|
||||
"height": 276
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/feature-edge.svg",
|
||||
"file": "assets/readme/feature-edge-spatial-v4.svg",
|
||||
"title": "Edge sensor mesh",
|
||||
"target": "firmware/esp32-csi-node/README.md",
|
||||
"width": 580,
|
||||
"height": 276
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/feature-automation.svg",
|
||||
"file": "assets/readme/feature-automation-spatial-v4.svg",
|
||||
"title": "Local automation",
|
||||
"target": "docs/integrations/home-assistant.md",
|
||||
"width": 580,
|
||||
"height": 276
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/feature-agents.svg",
|
||||
"file": "assets/readme/feature-agents-spatial-v4.svg",
|
||||
"title": "A guided AI operator",
|
||||
"target": "harness/ruview/README.md",
|
||||
"width": 580,
|
||||
"height": 276
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/overview.svg",
|
||||
"file": "assets/readme/overview-spatial-v4.svg",
|
||||
"title": "RuView sensing overview",
|
||||
"target": "docs/user-guide.md",
|
||||
"width": 1200,
|
||||
"height": 396
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/header-features.svg",
|
||||
"file": "assets/readme/header-features-spatial-v4.svg",
|
||||
"title": "Explore the capabilities",
|
||||
"target": "docs/user-guide.md",
|
||||
"width": 1200,
|
||||
"height": 148
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/header-edge.svg",
|
||||
"file": "assets/readme/header-edge-spatial-v4.svg",
|
||||
"title": "Intelligence at the edge",
|
||||
"target": "firmware/esp32-csi-node/README.md",
|
||||
"width": 1200,
|
||||
"height": 148
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/header-learning.svg",
|
||||
"file": "assets/readme/header-learning-spatial-v4.svg",
|
||||
"title": "From recordings to models",
|
||||
"target": "docs/user-guide.md",
|
||||
"width": 1200,
|
||||
"height": 148
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/header-evidence.svg",
|
||||
"file": "assets/readme/header-evidence-spatial-v4.svg",
|
||||
"title": "Evidence before accuracy claims",
|
||||
"target": "docs/benchmarks/pose-estimation-cog.md",
|
||||
"width": 1200,
|
||||
"height": 148
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/header-docs.svg",
|
||||
"file": "assets/readme/header-docs-spatial-v4.svg",
|
||||
"title": "Choose your next step",
|
||||
"target": "docs/user-guide.md",
|
||||
"width": 1200,
|
||||
"height": 148
|
||||
},
|
||||
{
|
||||
"file": "assets/readme/cognitum.svg",
|
||||
"file": "assets/readme/cognitum-spatial-v4.svg",
|
||||
"title": "Explore Cognitum One",
|
||||
"target": "https://cognitum.one",
|
||||
"width": 1200,
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">AI toolkit</title><desc id="desc">Open AI toolkit. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(90 45) scale(0.65)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M0 0L0 -33" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="0" cy="-33" r="7" stroke="#78cfff" fill="none" class="breathe"/><path d="M0 0L-35 23" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="-35" cy="23" r="7" stroke="#78cfff" fill="none" class="breathe"/><path d="M0 0L35 23" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="35" cy="23" r="7" stroke="#78cfff" fill="none" class="breathe"/><circle cx="0" cy="0" r="15" stroke="#78cfff" fill="none" /><path d="M-6 0L-1 5L8 -6" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></g></g><text x="90" y="98" font-size="16" fill="#f3f7ff" text-anchor="middle" font-weight="500" >AI toolkit</text></svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">AI toolkit</title><desc id="desc">Open AI toolkit. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
|
||||
|
||||
|
||||
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(90 45) scale(0.65)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><path d="M0 0L0 -36" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M0 -43l7 4v8l-7 4l-7 -4v-8Z" fill="#102749" stroke="#78cfff" class="breathe"/><path d="M0 0L-38 -10" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M-38 -17l7 4v8l-7 4l-7 -4v-8Z" fill="#102749" stroke="#78cfff" class="breathe"/><path d="M0 0L-23 31" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M-23 24l7 4v8l-7 4l-7 -4v-8Z" fill="#102749" stroke="#78cfff" class="breathe"/><path d="M0 0L30 28" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M30 21l7 4v8l-7 4l-7 -4v-8Z" fill="#102749" stroke="#78cfff" class="breathe"/><path d="M0 0L39 -17" fill="none" stroke="#438fff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M39 -24l7 4v8l-7 4l-7 -4v-8Z" fill="#102749" stroke="#78cfff" class="breathe"/><circle cx="0" cy="0" r="15" stroke="#b8f36c" fill="none" /><path d="M-6 0L-1 5L8 -6" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /></g></g><text x="90" y="98" font-size="16" fill="#f3f7ff" text-anchor="middle" font-weight="500" >AI toolkit</text><path d="M24 1H156" stroke="url(#accent)" opacity=".55"/><path d="M24 115H156" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 2.9 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">AI toolkit</title><desc id="desc">Open AI toolkit. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(90 45) scale(0.65)"><path d="M0 0L0 -33" fill="none" stroke="#70b6ff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="0" cy="-33" r="7" stroke="#72eee0" fill="none" class="breathe"/><path d="M0 0L-35 23" fill="none" stroke="#70b6ff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="-35" cy="23" r="7" stroke="#72eee0" fill="none" class="breathe"/><path d="M0 0L35 23" fill="none" stroke="#70b6ff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="35" cy="23" r="7" stroke="#72eee0" fill="none" class="breathe"/><circle cx="0" cy="0" r="15" stroke="#72eee0" fill="none" /><path d="M-6 0L-1 5L8 -6" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></g><text x="90" y="98" font-size="16" fill="#ecfafb" text-anchor="middle" >AI toolkit</text></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">AI toolkit</title><desc id="desc">Open AI toolkit. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(90 45) scale(0.65)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M0 0L0 -33" fill="none" stroke="#70b6ff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="0" cy="-33" r="7" stroke="#72eee0" fill="none" class="breathe"/><path d="M0 0L-35 23" fill="none" stroke="#70b6ff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="-35" cy="23" r="7" stroke="#72eee0" fill="none" class="breathe"/><path d="M0 0L35 23" fill="none" stroke="#70b6ff" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="35" cy="23" r="7" stroke="#72eee0" fill="none" class="breathe"/><circle cx="0" cy="0" r="15" stroke="#72eee0" fill="none" /><path d="M-6 0L-1 5L8 -6" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></g></g><text x="90" y="98" font-size="16" fill="#ecfafb" text-anchor="middle" font-weight="500" >AI toolkit</text></svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Hardware</title><desc id="desc">Open Hardware. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(90 45) scale(0.65)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><rect x="-25" y="-25" width="50" height="50" rx="7" fill="#0c2242" stroke="#78cfff"/><rect x="-13" y="-13" width="26" height="26" rx="3" fill="none" stroke="#438fff"/><path d="M-16 -36V-25M-16 25V36M-36 -16H-25M25 -16H36" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M0 -36V-25M0 25V36M-36 0H-25M25 0H36" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M16 -36V-25M16 25V36M-36 16H-25M25 16H36" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="0" r="4" stroke="#78cfff" fill="#78cfff" class="breathe"/></g></g><text x="90" y="98" font-size="16" fill="#f3f7ff" text-anchor="middle" font-weight="500" >Hardware</text></svg>
|
||||
|
After Width: | Height: | Size: 3.0 KiB |
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Hardware</title><desc id="desc">Open Hardware. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
|
||||
.layer{animation:layer 6s ease-in-out infinite}
|
||||
@keyframes layer{50%{transform:translateY(-5px)}}
|
||||
|
||||
|
||||
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(90 45) scale(0.65)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><g class="layer" style="animation-delay:-0.0s"><path d="M-38 -23L0 -42L38 -23L0 -4Z" fill="#102749" stroke="#78cfff"/><path d="M-38 -23V-16L0 3L38 -16V-23M0 -4V3" fill="#09172c" stroke="#315685"/></g><g class="layer" style="animation-delay:-0.7s"><path d="M-38 -10L0 -29L38 -10L0 9Z" fill="#102749" stroke="#438fff"/><path d="M-38 -10V-3L0 16L38 -3V-10M0 9V16" fill="#09172c" stroke="#315685"/></g><g class="layer" style="animation-delay:-1.4s"><path d="M-38 3L0 -16L38 3L0 22Z" fill="#102749" stroke="#438fff"/><path d="M-38 3V10L0 29L38 10V3M0 22V29" fill="#09172c" stroke="#315685"/></g><path d="M-12 -31L0 -37L12 -31L0 -25Z" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M-26 22V39" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M0 22V39" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M26 22V39" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/></g></g><text x="90" y="98" font-size="16" fill="#f3f7ff" text-anchor="middle" font-weight="500" >Hardware</text><path d="M24 1H156" stroke="url(#accent)" opacity=".55"/><path d="M24 115H156" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Hardware</title><desc id="desc">Open Hardware. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(90 45) scale(0.65)"><rect x="-25" y="-25" width="50" height="50" rx="7" fill="#0a2630" stroke="#72eee0"/><rect x="-13" y="-13" width="26" height="26" rx="3" fill="none" stroke="#70b6ff"/><path d="M-16 -36V-25M-16 25V36M-36 -16H-25M25 -16H36" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M0 -36V-25M0 25V36M-36 0H-25M25 0H36" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M16 -36V-25M16 25V36M-36 16H-25M25 16H36" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="0" r="4" stroke="#72eee0" fill="#72eee0" class="breathe"/></g><text x="90" y="98" font-size="16" fill="#ecfafb" text-anchor="middle" >Hardware</text></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Hardware</title><desc id="desc">Open Hardware. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(90 45) scale(0.65)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><rect x="-25" y="-25" width="50" height="50" rx="7" fill="#0a2630" stroke="#72eee0"/><rect x="-13" y="-13" width="26" height="26" rx="3" fill="none" stroke="#70b6ff"/><path d="M-16 -36V-25M-16 25V36M-36 -16H-25M25 -16H36" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M0 -36V-25M0 25V36M-36 0H-25M25 0H36" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M16 -36V-25M16 25V36M-36 16H-25M25 16H36" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="0" r="4" stroke="#72eee0" fill="#72eee0" class="breathe"/></g></g><text x="90" y="98" font-size="16" fill="#ecfafb" text-anchor="middle" font-weight="500" >Hardware</text></svg>
|
||||
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 2.8 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Smart home</title><desc id="desc">Open Smart home. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(90 45) scale(0.65)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M-37 -3L0 -34L37 -3M-28 -10V32H28V-10M-9 32V8H9V32" fill="none" stroke="#78cfff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="-9" r="5" stroke="#438fff" fill="none" class="breathe"/></g></g><text x="90" y="98" font-size="16" fill="#f3f7ff" text-anchor="middle" font-weight="500" >Smart home</text></svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Smart home</title><desc id="desc">Open Smart home. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
|
||||
|
||||
|
||||
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(90 45) scale(0.65)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><path d="M-43 12L0 -10L43 12L0 35Z" fill="#102749" stroke="#438fff"/><path d="M-32 17V-16L0 -35L32 -16V17M-32 -16L0 3L32 -16M0 3V34" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /><path d="M-22 20L-10 13L12 25M-10 13V-5" fill="none" stroke="#b8f36c" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="flow"/><circle cx="-22" cy="20" r="4" stroke="#ffb45e" fill="#102749" class="breathe"/><circle cx="-10" cy="-5" r="4" stroke="#ffb45e" fill="#102749" class="breathe"/><circle cx="12" cy="25" r="4" stroke="#ffb45e" fill="#102749" class="breathe"/></g></g><text x="90" y="98" font-size="16" fill="#f3f7ff" text-anchor="middle" font-weight="500" >Smart home</text><path d="M24 1H156" stroke="url(#accent)" opacity=".55"/><path d="M24 115H156" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 2.3 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Smart home</title><desc id="desc">Open Smart home. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(90 45) scale(0.65)"><path d="M-37 -3L0 -34L37 -3M-28 -10V32H28V-10M-9 32V8H9V32" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="-9" r="5" stroke="#70b6ff" fill="none" class="breathe"/></g><text x="90" y="98" font-size="16" fill="#ecfafb" text-anchor="middle" >Smart home</text></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Smart home</title><desc id="desc">Open Smart home. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(90 45) scale(0.65)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M-37 -3L0 -34L37 -3M-28 -10V32H28V-10M-9 32V8H9V32" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="-9" r="5" stroke="#70b6ff" fill="none" class="breathe"/></g></g><text x="90" y="98" font-size="16" fill="#ecfafb" text-anchor="middle" font-weight="500" >Smart home</text></svg>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Models</title><desc id="desc">Open Models. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(90 45) scale(0.65)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M-42 6H-27L-18 -10L-9 25L3 -25L14 10L24 -3L31 6H43" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/><path d="M-42 31H43" fill="none" stroke="#233f63" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /></g></g><text x="90" y="98" font-size="16" fill="#f3f7ff" text-anchor="middle" font-weight="500" >Models</text></svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Models</title><desc id="desc">Open Models. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
|
||||
.cursor{animation:cursor 5s ease-in-out infinite}
|
||||
@keyframes cursor{0%,100%{transform:translateX(-35px);opacity:.3}50%{transform:translateX(35px);opacity:1}}
|
||||
|
||||
|
||||
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(90 45) scale(0.65)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><path d="M-49 -14C-34 -14 -30 -35 -17 -35S0 7 14 7S34 -14 49 -14" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="trace" style="animation-delay:-0s" opacity="1.0"/><path d="M-49 -2C-34 -2 -30 -23 -17 -23S0 19 14 19S34 -2 49 -2" fill="none" stroke="#b8f36c" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="trace" style="animation-delay:-1s" opacity="0.8"/><path d="M-49 10C-34 10 -30 -11 -17 -11S0 31 14 31S34 10 49 10" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="trace" style="animation-delay:-2s" opacity="0.6"/><g class="cursor"><path d="M0 -42V40" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="-8" r="3" stroke="#ffb45e" fill="#ffb45e"/></g></g></g><text x="90" y="98" font-size="16" fill="#f3f7ff" text-anchor="middle" font-weight="500" >Models</text><path d="M24 1H156" stroke="url(#accent)" opacity=".55"/><path d="M24 115H156" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Models</title><desc id="desc">Open Models. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(90 45) scale(0.65)"><path d="M-42 6H-27L-18 -10L-9 25L3 -25L14 10L24 -3L31 6H43" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/><path d="M-42 31H43" fill="none" stroke="#214451" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /></g><text x="90" y="98" font-size="16" fill="#ecfafb" text-anchor="middle" >Models</text></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Models</title><desc id="desc">Open Models. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(90 45) scale(0.65)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M-42 6H-27L-18 -10L-9 25L3 -25L14 10L24 -3L31 6H43" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/><path d="M-42 31H43" fill="none" stroke="#214451" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /></g></g><text x="90" y="98" font-size="16" fill="#ecfafb" text-anchor="middle" font-weight="500" >Models</text></svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.4 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Cognitum</title><desc id="desc">Open Cognitum. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(90 45) scale(0.65)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M0 -38L31 -20V18L0 38L-31 18V-20ZM-31 -20L0 -1L31 -20M0 -1V38" fill="none" stroke="#78cfff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M-15 1L-5 11L17 -12" fill="none" stroke="#438fff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/></g></g><text x="90" y="98" font-size="16" fill="#f3f7ff" text-anchor="middle" font-weight="500" >Cognitum</text></svg>
|
||||
|
After Width: | Height: | Size: 2.6 KiB |
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Cognitum</title><desc id="desc">Open Cognitum. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
|
||||
.layer{animation:layer 6s ease-in-out infinite}
|
||||
@keyframes layer{50%{transform:translateY(-5px)}}
|
||||
|
||||
|
||||
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(90 45) scale(0.65)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><g class="layer" style="animation-delay:-0.0s"><path d="M-38 -23L0 -42L38 -23L0 -4Z" fill="#102749" stroke="#78cfff"/><path d="M-38 -23V-16L0 3L38 -16V-23M0 -4V3" fill="#09172c" stroke="#315685"/></g><g class="layer" style="animation-delay:-0.7s"><path d="M-38 -10L0 -29L38 -10L0 9Z" fill="#102749" stroke="#438fff"/><path d="M-38 -10V-3L0 16L38 -3V-10M0 9V16" fill="#09172c" stroke="#315685"/></g><g class="layer" style="animation-delay:-1.4s"><path d="M-38 3L0 -16L38 3L0 22Z" fill="#102749" stroke="#438fff"/><path d="M-38 3V10L0 29L38 10V3M0 22V29" fill="#09172c" stroke="#315685"/></g><path d="M-12 -31L0 -37L12 -31L0 -25Z" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M-26 22V39" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M0 22V39" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/><path d="M26 22V39" fill="none" stroke="#ffb45e" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" class="flow"/></g></g><text x="90" y="98" font-size="16" fill="#f3f7ff" text-anchor="middle" font-weight="500" >Cognitum</text><path d="M24 1H156" stroke="url(#accent)" opacity=".55"/><path d="M24 115H156" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 2.8 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Cognitum</title><desc id="desc">Open Cognitum. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(90 45) scale(0.65)"><path d="M0 -38L31 -20V18L0 38L-31 18V-20ZM-31 -20L0 -1L31 -20M0 -1V38" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M-15 1L-5 11L17 -12" fill="none" stroke="#70b6ff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/></g><text x="90" y="98" font-size="16" fill="#ecfafb" text-anchor="middle" >Cognitum</text></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Cognitum</title><desc id="desc">Open Cognitum. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(90 45) scale(0.65)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M0 -38L31 -20V18L0 38L-31 18V-20ZM-31 -20L0 -1L31 -20M0 -1V38" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><path d="M-15 1L-5 11L17 -12" fill="none" stroke="#70b6ff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/></g></g><text x="90" y="98" font-size="16" fill="#ecfafb" text-anchor="middle" font-weight="500" >Cognitum</text></svg>
|
||||
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 2.5 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Start here</title><desc id="desc">Open Start here. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(90 45) scale(0.65)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M-10.4 -5.2Q0 -18.2 10.4 -5.2" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="breathe" style="animation-delay:-0.65s"/><path d="M-20.0 -10.0Q0 -35.0 20.0 -10.0" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="breathe" style="animation-delay:-1.25s"/><path d="M-30.400000000000002 -15.200000000000001Q0 -53.199999999999996 30.400000000000002 -15.200000000000001" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="breathe" style="animation-delay:-1.9s"/><circle cx="0" cy="20" r="4" stroke="#78cfff" fill="#78cfff"/><path d="M0 12V-1" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></g></g><text x="90" y="98" font-size="16" fill="#f3f7ff" text-anchor="middle" font-weight="500" >Start here</text></svg>
|
||||
|
After Width: | Height: | Size: 3.1 KiB |
@@ -0,0 +1,15 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Start here</title><desc id="desc">Open Start here. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
|
||||
.sweep{animation:sweep 5s ease-in-out infinite;transform-origin:0 0}
|
||||
@keyframes sweep{0%,100%{transform:rotate(-20deg)}50%{transform:rotate(155deg)}}
|
||||
|
||||
|
||||
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#2d4e77"/><g transform="translate(90 45) scale(0.65)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><ellipse rx="18" ry="8.64" fill="none" stroke="#78cfff" opacity=".4"/><ellipse rx="32" ry="15.36" fill="none" stroke="#78cfff" opacity=".4"/><ellipse rx="46" ry="22.08" fill="none" stroke="#78cfff" opacity=".4"/><path d="M-46 0H46M0 -23V23" fill="none" stroke="#438fff" stroke-width="0.7" stroke-linecap="round" stroke-linejoin="round" /><g class="sweep"><path d="M0 0L40 -18" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="34" cy="-15" r="3" stroke="#b8f36c" fill="#b8f36c"/></g><circle cx="-20" cy="8" r="3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="12" cy="13" r="3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="23" cy="-8" r="3" stroke="#ffb45e" fill="#ffb45e" class="spark"/></g></g><text x="90" y="98" font-size="16" fill="#f3f7ff" text-anchor="middle" font-weight="500" >Start here</text><path d="M24 1H156" stroke="url(#accent)" opacity=".55"/><path d="M24 115H156" stroke="#438fff" opacity=".15"/></svg>
|
||||
|
After Width: | Height: | Size: 2.5 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Start here</title><desc id="desc">Open Start here. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(90 45) scale(0.65)"><path d="M-10.4 -5.2Q0 -18.2 10.4 -5.2" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="breathe" style="animation-delay:-0.65s"/><path d="M-20.0 -10.0Q0 -35.0 20.0 -10.0" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="breathe" style="animation-delay:-1.25s"/><path d="M-30.400000000000002 -15.200000000000001Q0 -53.199999999999996 30.400000000000002 -15.200000000000001" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="breathe" style="animation-delay:-1.9s"/><circle cx="0" cy="20" r="4" stroke="#72eee0" fill="#72eee0"/><path d="M0 12V-1" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></g><text x="90" y="98" font-size="16" fill="#ecfafb" text-anchor="middle" >Start here</text></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="180" height="116" viewBox="0 0 180 116" role="img" aria-labelledby="title desc"><title id="title">Start here</title><desc id="desc">Open Start here. Animated navigation icon.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="179" height="115" rx="18" fill="url(#wash)" stroke="#245360"/><g transform="translate(90 45) scale(0.65)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M-10.4 -5.2Q0 -18.2 10.4 -5.2" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="breathe" style="animation-delay:-0.65s"/><path d="M-20.0 -10.0Q0 -35.0 20.0 -10.0" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="breathe" style="animation-delay:-1.25s"/><path d="M-30.400000000000002 -15.200000000000001Q0 -53.199999999999996 30.400000000000002 -15.200000000000001" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="breathe" style="animation-delay:-1.9s"/><circle cx="0" cy="20" r="4" stroke="#72eee0" fill="#72eee0"/><path d="M0 12V-1" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></g></g><text x="90" y="98" font-size="16" fill="#ecfafb" text-anchor="middle" font-weight="500" >Start here</text></svg>
|
||||
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 2.9 KiB |
@@ -0,0 +1,21 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="396" viewBox="0 0 1200 396" role="img" aria-labelledby="title desc"><title id="title">RuView sensing overview</title><desc id="desc">Conceptual sequence: capture RF, extract context, act locally. Links to architecture and user documentation.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="395" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="36" y="44" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">SIGNAL TO CONTEXT</text><text x="36" y="89" font-size="34" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >A sensing loop you can inspect.</text><rect x="36" y="120" width="336" height="220" rx="14" fill="#091b35" stroke="#315685"/><text x="60" y="153" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" >01</text><text x="60" y="188" font-size="26" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Capture RF</text><text x="60" y="216" font-size="15" fill="#b0c2db" text-anchor="start" font-weight="500" >CSI from supported hardware</text><g transform="translate(305 278) scale(0.35)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><g transform="translate(305 270) scale(0.5)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M-10.4 -5.2Q0 -18.2 10.4 -5.2" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="breathe" style="animation-delay:-0.65s"/><path d="M-20.0 -10.0Q0 -35.0 20.0 -10.0" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="breathe" style="animation-delay:-1.25s"/><path d="M-30.400000000000002 -15.200000000000001Q0 -53.199999999999996 30.400000000000002 -15.200000000000001" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="breathe" style="animation-delay:-1.9s"/><circle cx="0" cy="20" r="4" stroke="#78cfff" fill="#78cfff"/><path d="M0 12V-1" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></g></g><text x="60" y="311" font-size="13" fill="#b0c2db" text-anchor="start" font-weight="500" >Provision and calibrate</text><path d="M380 229H420M412 221L420 229L412 237" fill="none" stroke="#78cfff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="flow"/><rect x="432" y="120" width="336" height="220" rx="14" fill="#091b35" stroke="#315685"/><text x="456" y="153" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" >02</text><text x="456" y="188" font-size="26" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Extract context</text><text x="456" y="216" font-size="15" fill="#b0c2db" text-anchor="start" font-weight="500" >Signal processing and models</text><g transform="translate(701 278) scale(0.35)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><g transform="translate(701 270) scale(0.5)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#142c50" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#2e527d" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g Line truncated
|
||||
|
After Width: | Height: | Size: 9.7 KiB |
@@ -0,0 +1,28 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="396" viewBox="0 0 1200 396" role="img" aria-labelledby="title desc"><title id="title">RuView sensing overview</title><desc id="desc">Conceptual sequence: capture RF, extract context, act locally. Links to architecture and user documentation.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#102749"/><stop offset=".55" stop-color="#071429"/><stop offset="1" stop-color="#03091a"/></linearGradient><linearGradient id="accent"><stop stop-color="#9ee7ff"/><stop offset="1" stop-color="#348bff"/></linearGradient><radialGradient id="halo"><stop stop-color="#348bff" stop-opacity=".17"/><stop offset="1" stop-color="#348bff" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI Variable Display","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{stroke-dasharray:3 14;animation:flow 8s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
.wave{transform-box:fill-box;transform-origin:center;animation:wave 4s ease-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
|
||||
.sweep{animation:sweep 5s ease-in-out infinite;transform-origin:0 0}
|
||||
.cursor{animation:cursor 5s ease-in-out infinite}
|
||||
@keyframes sweep{0%,100%{transform:rotate(-20deg)}50%{transform:rotate(155deg)}}
|
||||
@keyframes cursor{0%,100%{transform:translateX(-35px);opacity:.3}50%{transform:translateX(35px);opacity:1}}
|
||||
|
||||
|
||||
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@keyframes wave{0%{transform:scale(.5);opacity:.7}100%{transform:scale(1.3);opacity:0}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="395" rx="18" fill="url(#wash)" stroke="#2d4e77"/><text x="36" y="44" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" class="mono">SIGNAL TO CONTEXT</text><text x="36" y="89" font-size="34" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >A sensing loop you can inspect.</text><rect x="36" y="120" width="336" height="220" rx="14" fill="#091b35" stroke="#315685"/><text x="60" y="153" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" >01</text><text x="60" y="188" font-size="26" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Capture RF</text><text x="60" y="216" font-size="15" fill="#b0c2db" text-anchor="start" font-weight="500" >CSI from supported hardware</text><g transform="translate(305 278) scale(0.35)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><g transform="translate(305 270) scale(0.5)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><ellipse rx="18" ry="8.64" fill="none" stroke="#78cfff" opacity=".4"/><ellipse rx="32" ry="15.36" fill="none" stroke="#78cfff" opacity=".4"/><ellipse rx="46" ry="22.08" fill="none" stroke="#78cfff" opacity=".4"/><path d="M-46 0H46M0 -23V23" fill="none" stroke="#438fff" stroke-width="0.7" stroke-linecap="round" stroke-linejoin="round" /><g class="sweep"><path d="M0 0L40 -18" fill="none" stroke="#b8f36c" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="34" cy="-15" r="3" stroke="#b8f36c" fill="#b8f36c"/></g><circle cx="-20" cy="8" r="3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="12" cy="13" r="3" stroke="#ffb45e" fill="#ffb45e" class="spark"/><circle cx="23" cy="-8" r="3" stroke="#ffb45e" fill="#ffb45e" class="spark"/></g></g><text x="60" y="311" font-size="13" fill="#b0c2db" text-anchor="start" font-weight="500" >Provision and calibrate</text><path d="M380 229H420M412 221L420 229L412 237" fill="none" stroke="#78cfff" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="flow"/><rect x="432" y="120" width="336" height="220" rx="14" fill="#091b35" stroke="#315685"/><text x="456" y="153" font-size="12" fill="#78cfff" text-anchor="start" font-weight="500" >02</text><text x="456" y="188" font-size="26" fill="#f3f7ff" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Extract context</text><text x="456" y="216" font-size="15" fill="#b0c2db" text-anchor="start" font-weight="500" >Signal processing and models</text><g transform="translate(701 278) scale(0.35)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0b1b35" stroke="#2e5b8e"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#4473a5"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#438fff" fill-opacity=".12" stroke="#78cfff" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#438fff" fill="#438fff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#438fff" fill="#438fff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#438fff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#9ee7ff"/></g><ellipse cx="0" cy="36" rx="85" ry="28" fill="none" stroke="#438fff" class="wave"/></g><g transform="translate(701 270) scale(0.5)"><ellipse cy="53" rx="48" ry="8" fill="#020816" opacity=".65"/><g class="float"><path d="M-49 -14C-34 -14 -30 -35 -17 -35S0 7 14 7S34 -14 49 -14" fill="none" stroke="#78cfff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="trace" style="animation-delay:-0s" opacity="1.0"/><path d="M-49 -2C-34 -2 -30 -23 -17 -23S0 19 14 19S34 -2 49 -2" fillLine truncated
|
||||
|
After Width: | Height: | Size: 11 KiB |
@@ -1 +1,19 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="396" viewBox="0 0 1200 396" role="img" aria-labelledby="title desc"><title id="title">RuView sensing overview</title><desc id="desc">Conceptual sequence: capture RF, extract context, act locally. Links to architecture and user documentation.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>text{font-family:Arial,Helvetica,sans-serif}.mono{font-family:ui-monospace,Consolas,monospace;letter-spacing:2px}.flow{stroke-dasharray:5 12;animation:flow 12s linear infinite}.breathe{animation:breathe 6s ease-in-out infinite}.trace{stroke-dasharray:400;stroke-dashoffset:0;animation:trace 10s ease-in-out infinite}.float{animation:float 9s ease-in-out infinite}@keyframes flow{to{stroke-dashoffset:-136}}@keyframes breathe{50%{opacity:.4}}@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}@keyframes float{50%{transform:translateY(-6px)}}@media(prefers-reduced-motion:reduce){*{animation:none!important}}</style><rect x=".5" y=".5" width="1199" height="395" rx="18" fill="url(#wash)" stroke="#245360"/><text x="36" y="44" font-size="12" fill="#72eee0" text-anchor="start" class="mono">SIGNAL TO CONTEXT</text><text x="36" y="89" font-size="34" fill="#ecfafb" text-anchor="start" >A sensing loop you can inspect.</text><rect x="36" y="120" width="336" height="220" rx="14" fill="#071b23" stroke="#285360"/><text x="60" y="153" font-size="12" fill="#72eee0" text-anchor="start" >01</text><text x="60" y="188" font-size="26" fill="#ecfafb" text-anchor="start" >Capture RF</text><text x="60" y="216" font-size="15" fill="#9abac5" text-anchor="start" >CSI from supported hardware</text><g transform="translate(305 281) scale(0.62)"><path d="M-10.4 -5.2Q0 -18.2 10.4 -5.2" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="breathe" style="animation-delay:-0.65s"/><path d="M-20.0 -10.0Q0 -35.0 20.0 -10.0" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="breathe" style="animation-delay:-1.25s"/><path d="M-30.400000000000002 -15.200000000000001Q0 -53.199999999999996 30.400000000000002 -15.200000000000001" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="breathe" style="animation-delay:-1.9s"/><circle cx="0" cy="20" r="4" stroke="#72eee0" fill="#72eee0"/><path d="M0 12V-1" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></g><text x="60" y="311" font-size="13" fill="#9abac5" text-anchor="start" >Provision and calibrate</text><path d="M380 229H420M412 221L420 229L412 237" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="flow"/><rect x="432" y="120" width="336" height="220" rx="14" fill="#071b23" stroke="#285360"/><text x="456" y="153" font-size="12" fill="#72eee0" text-anchor="start" >02</text><text x="456" y="188" font-size="26" fill="#ecfafb" text-anchor="start" >Extract context</text><text x="456" y="216" font-size="15" fill="#9abac5" text-anchor="start" >Signal processing and models</text><g transform="translate(701 281) scale(0.62)"><path d="M-42 6H-27L-18 -10L-9 25L3 -25L14 10L24 -3L31 6H43" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="trace"/><path d="M-42 31H43" fill="none" stroke="#214451" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /></g><text x="456" y="311" font-size="13" fill="#9abac5" text-anchor="start" >Compare against real data</text><path d="M776 229H816M808 221L816 229L808 237" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="flow"/><rect x="828" y="120" width="336" height="220" rx="14" fill="#071b23" stroke="#285360"/><text x="852" y="153" font-size="12" fill="#72eee0" text-anchor="start" >03</text><text x="852" y="188" font-size="26" fill="#ecfafb" text-anchor="start" >Act locally</text><text x="852" y="216" font-size="15" fill="#9abac5" text-anchor="start" >Events, APIs and integrations</text><g transform="translate(1097 281) scale(0.62)"><path d="M-37 -3L0 -34L37 -3M-28 -10V32H28V-10M-9 32V8H9V32" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" /><circle cx="0" cy="-9" r="5" stroke="#70b6ff" fill="none" class="breathe"/></g><text x="852" y="311" font-size="13" fill="#9abac5" text-anchor="start" >Apply rules and review outputs</text><text x="36" y="373" font-size="11" fill="#9abac5" text-anchor="start" class="mono">CONCEPTUALine truncated
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="396" viewBox="0 0 1200 396" role="img" aria-labelledby="title desc"><title id="title">RuView sensing overview</title><desc id="desc">Conceptual sequence: capture RF, extract context, act locally. Links to architecture and user documentation.</desc><defs><linearGradient id="wash" x1="0" y1="0" x2="1" y2="1"><stop stop-color="#0b303a"/><stop offset=".55" stop-color="#06171e"/><stop offset="1" stop-color="#030c13"/></linearGradient><linearGradient id="accent"><stop stop-color="#91f6df"/><stop offset="1" stop-color="#26bad7"/></linearGradient><radialGradient id="halo"><stop stop-color="#35d4db" stop-opacity=".17"/><stop offset="1" stop-color="#35d4db" stop-opacity="0"/></radialGradient></defs><style>
|
||||
text{font-family:"Avenir Next","Segoe UI",Helvetica,Arial,sans-serif}
|
||||
.mono{font-family:"SFMono-Regular",Consolas,"Liberation Mono",monospace;letter-spacing:1.6px}
|
||||
.float{animation:float 5s ease-in-out infinite}
|
||||
.flow{stroke-dasharray:5 12;animation:flow 5s linear infinite}
|
||||
.breathe{animation:breathe 4s ease-in-out infinite}
|
||||
.trace{stroke-dasharray:400;animation:trace 6s ease-in-out infinite}
|
||||
.scan{animation:scan 7s ease-in-out infinite}
|
||||
.orbit{transform-box:fill-box;transform-origin:center;animation:orbit 18s linear infinite}
|
||||
.spark{animation:spark 3.5s ease-in-out infinite}
|
||||
@keyframes float{0%,100%{transform:translate(0,0)}50%{transform:translate(3px,-10px)}}
|
||||
@keyframes scan{0%,100%{transform:translateY(0)}50%{transform:translateY(-76px)}}
|
||||
@keyframes orbit{to{transform:rotate(360deg)}}
|
||||
@keyframes spark{50%{opacity:.25}}
|
||||
@keyframes flow{to{stroke-dashoffset:-136}}
|
||||
@keyframes breathe{50%{opacity:.4}}
|
||||
@keyframes trace{0%,100%{stroke-dashoffset:0}50%{stroke-dashoffset:90}}
|
||||
@media(prefers-reduced-motion:reduce){*{animation:none!important}}
|
||||
</style><rect x=".5" y=".5" width="1199" height="395" rx="18" fill="url(#wash)" stroke="#245360"/><text x="36" y="44" font-size="12" fill="#72eee0" text-anchor="start" font-weight="500" class="mono">SIGNAL TO CONTEXT</text><text x="36" y="89" font-size="34" fill="#ecfafb" text-anchor="start" font-weight="650" letter-spacing="-0.6" >A sensing loop you can inspect.</text><rect x="36" y="120" width="336" height="220" rx="14" fill="#071b23" stroke="#285360"/><text x="60" y="153" font-size="12" fill="#72eee0" text-anchor="start" font-weight="500" >01</text><text x="60" y="188" font-size="26" fill="#ecfafb" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Capture RF</text><text x="60" y="216" font-size="15" fill="#9abac5" text-anchor="start" font-weight="500" >CSI from supported hardware</text><g transform="translate(305 278) scale(0.35)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0a2733" stroke="#286171"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#366d80"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#5fe3d8" fill-opacity=".12" stroke="#72eee0" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#70b6ff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#91f6df"/></g></g><g transform="translate(305 270) scale(0.5)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M-10.4 -5.2Q0 -18.2 10.4 -5.2" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="breathe" style="animation-delay:-0.65s"/><path d="M-20.0 -10.0Q0 -35.0 20.0 -10.0" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="breathe" style="animation-delay:-1.25s"/><path d="M-30.400000000000002 -15.200000000000001Q0 -53.199999999999996 30.400000000000002 -15.200000000000001" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="breathe" style="animation-delay:-1.9s"/><circle cx="0" cy="20" r="4" stroke="#72eee0" fill="#72eee0"/><path d="M0 12V-1" fill="none" stroke="#72eee0" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" /></g></g><text x="60" y="311" font-size="13" fill="#9abac5" text-anchor="start" font-weight="500" >Provision and calibrate</text><path d="M380 229H420M412 221L420 229L412 237" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="flow"/><rect x="432" y="120" width="336" height="220" rx="14" fill="#071b23" stroke="#285360"/><text x="456" y="153" font-size="12" fill="#72eee0" text-anchor="start" font-weight="500" >02</text><text x="456" y="188" font-size="26" fill="#ecfafb" text-anchor="start" font-weight="650" letter-spacing="-0.6" >Extract context</text><text x="456" y="216" font-size="15" fill="#9abac5" text-anchor="start" font-weight="500" >Signal processing and models</text><g transform="translate(701 278) scale(0.35)"><path d="M-82 36L0 -6L82 36L0 78Z" fill="#0a2733" stroke="#286171"/><path d="M-82 36V-52L0 -94L82 -52V36M-82 -52L0 -10L82 -52M0 -10V78" fill="none" stroke="#366d80"/><g class="scan"><path d="M-81 32L0 -10L81 32L0 74Z" fill="#5fe3d8" fill-opacity=".12" stroke="#72eee0" stroke-opacity=".7"/></g><circle cx="-40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="-15" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="40" cy="6" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="-20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="20" cy="30" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><circle cx="0" cy="49" r="2" stroke="#70b6ff" fill="#70b6ff" class="spark"/><g class="orbit"><ellipse cx="0" cy="4" rx="102" ry="30" fill="none" stroke="#70b6ff" stroke-opacity=".4" stroke-dasharray="2 8"/><circle cx="102" cy="4" r="4" fill="#91f6df"/></g></g><g transform="translate(701 270) scale(0.5)"><ellipse cx="0" cy="48" rx="43" ry="12" fill="#133a45" opacity=".5"/><path d="M-43 48L0 27L43 48L0 69Z" fill="none" stroke="#285c68" stroke-width="1" stroke-linecap="round" stroke-linejoin="round" /><g class="float"><path d="M-42 6H-27L-18 -10L-9 25L3 -25L14 10L24 -3L31 6H43" fill="none" stroke="#72eee0" stroke-width="2" stroke-linecap="round" stroke-linejoin="roundLine truncated
|
||||
|
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 9.3 KiB |
@@ -97,7 +97,7 @@ export class NvAppStore extends LitElement {
|
||||
.chip .swatch {
|
||||
width: 7px; height: 7px; border-radius: 50%;
|
||||
}
|
||||
.chip .count { color: var(--ink-3); font-size: 10px; }
|
||||
.chip .count { color: var(--ink-2); font-size: 10px; }
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
|
||||
@@ -149,7 +149,7 @@ export class NvAppStore extends LitElement {
|
||||
.badge.budget { color: var(--accent-2); border-color: oklch(0.78 0.12 195 / 0.3); }
|
||||
.badge.rt-running { color: var(--ok); border-color: oklch(0.78 0.14 145 / 0.5); background: oklch(0.78 0.14 145 / 0.08); }
|
||||
.badge.rt-simulated { color: var(--accent); border-color: oklch(0.78 0.14 70 / 0.5); background: oklch(0.78 0.14 70 / 0.08); }
|
||||
.badge.rt-mesh-only { color: var(--ink-3); border-color: var(--line); }
|
||||
.badge.rt-mesh-only { color: var(--ink-2); border-color: var(--line); }
|
||||
.events-feed {
|
||||
background: var(--bg-2);
|
||||
border: 1px solid var(--line);
|
||||
@@ -388,7 +388,7 @@ export class NvAppStore extends LitElement {
|
||||
<div class="card-foot">
|
||||
<span class="events">${app.crate}</span>
|
||||
${evCount > 0 ? html`<span class="card-events-count">⚡ ${evCount} ev</span>` : ''}
|
||||
<span class="toggle ${active ? 'on' : ''}" role="switch"
|
||||
<span class="toggle ${active ? 'on' : ''}" role="switch" aria-label=${`Enable ${app.name}`}
|
||||
aria-checked=${active}
|
||||
data-app-toggle=${app.id}
|
||||
@click=${() => this.toggle(app)}></span>
|
||||
|
||||
@@ -66,7 +66,7 @@ export class NvConsole extends LitElement {
|
||||
border-left: 2px solid transparent;
|
||||
}
|
||||
.line:hover { background: var(--bg-1); }
|
||||
.ts { color: var(--ink-4); font-size: 10.5px; padding-top: 1px; }
|
||||
.ts { color: var(--ink-3); font-size: 10.5px; padding-top: 1px; }
|
||||
.lvl {
|
||||
font-size: 10px; font-weight: 600;
|
||||
text-transform: uppercase; letter-spacing: 0.04em; padding-top: 1px;
|
||||
|
||||
@@ -394,7 +394,7 @@ export class NvGhostMurmur extends LitElement {
|
||||
<span class="lbl">Distance from sensor</span>
|
||||
<span class="val" id="demo-dist-val">${this.formatDistance(this.distanceM)}</span>
|
||||
</div>
|
||||
<input type="range" id="demo-distance"
|
||||
<input type="range" id="demo-distance" aria-label="Distance from sensor"
|
||||
min="-2" max="5" step="0.05"
|
||||
.value=${String(Math.log10(this.distanceM))}
|
||||
@input=${(e: Event) => { this.distanceM = Math.pow(10, +(e.target as HTMLInputElement).value); }} />
|
||||
@@ -407,7 +407,7 @@ export class NvGhostMurmur extends LitElement {
|
||||
<span class="lbl">Heart dipole moment</span>
|
||||
<span class="val" id="demo-moment-val">${m.toExponential(2)} A·m²</span>
|
||||
</div>
|
||||
<input type="range" id="demo-moment"
|
||||
<input type="range" id="demo-moment" aria-label="Heart dipole moment"
|
||||
min="-10" max="-6" step="0.05"
|
||||
.value=${String(this.momentLog10)}
|
||||
@input=${(e: Event) => { this.momentLog10 = +(e.target as HTMLInputElement).value; }} />
|
||||
@@ -536,7 +536,7 @@ export class NvGhostMurmur extends LitElement {
|
||||
</div>
|
||||
|
||||
<h2>RuView's three-tier mesh — what is actually buildable</h2>
|
||||
<div class="architecture"> ┌──────────────────────────┐
|
||||
<div class="architecture" tabindex="0" role="group" aria-label="RuView three-tier mesh architecture"> ┌──────────────────────────┐
|
||||
│ Tier 3 — NV-diamond │ Range: 0.1–2 m (lab)
|
||||
│ magnetometer ring │ Status: nvsim simulator only
|
||||
│ (close-confirm) │ Hardware: $$$ (≥$8k DNV-B1)
|
||||
|
||||
@@ -423,7 +423,7 @@ export class NvInspector extends LitElement {
|
||||
role="tab" aria-selected=${this.tab === 'witness'}
|
||||
@click=${() => this.tab = 'witness'}>Witness</button>
|
||||
</div>
|
||||
<div class="body" role="tabpanel">
|
||||
<div class="body" role="tabpanel" tabindex="0">
|
||||
${this.renderHeader()}
|
||||
${this.tab === 'signal' ? this.renderSignalTab()
|
||||
: this.tab === 'frame' ? this.renderFrameTab()
|
||||
|
||||
@@ -185,9 +185,20 @@ export class NvOnboarding extends LitElement {
|
||||
z-index: 240;
|
||||
display: grid; place-items: center;
|
||||
opacity: 0; pointer-events: none;
|
||||
transition: opacity 0.18s;
|
||||
/* A dismissed modal must leave the rendering + accessibility tree, not
|
||||
just fade to opacity 0. While the 0.18s fade-out runs, automated
|
||||
scanners (and mid-animation users) still see the panel, and contrast
|
||||
tooling composites the fading text over the dimmed backdrop and
|
||||
reports false failures. Delaying visibility by the fade duration keeps
|
||||
the animation intact while making the closed state truly absent. */
|
||||
visibility: hidden;
|
||||
transition: opacity 0.18s, visibility 0s linear 0.18s;
|
||||
}
|
||||
:host([open]) {
|
||||
opacity: 1; pointer-events: auto;
|
||||
visibility: visible;
|
||||
transition: opacity 0.18s, visibility 0s;
|
||||
}
|
||||
:host([open]) { opacity: 1; pointer-events: auto; }
|
||||
.card {
|
||||
background: var(--bg-1);
|
||||
border: 1px solid var(--line-2);
|
||||
@@ -329,6 +340,11 @@ export class NvOnboarding extends LitElement {
|
||||
this.open = true;
|
||||
this.setAttribute('open', '');
|
||||
}
|
||||
// Signals that the async storage read above has settled. Until it is set,
|
||||
// "the tour was already seen" and "the decision has not been made yet"
|
||||
// are indistinguishable, so a caller can act in the gap and then watch the
|
||||
// modal open underneath it.
|
||||
this.setAttribute('ready', '');
|
||||
}
|
||||
override disconnectedCallback(): void {
|
||||
super.disconnectedCallback();
|
||||
|
||||
@@ -172,7 +172,7 @@ export class NvSettingsDrawer extends LitElement {
|
||||
<div class="desc">Stops the rotating diamond, animated field lines, and chart easing. Auto-on if your system has the prefers-reduced-motion preference set.</div>
|
||||
</div>
|
||||
<span class="toggle ${motionReduced.value ? 'on' : ''}"
|
||||
role="switch" aria-checked=${motionReduced.value}
|
||||
role="switch" aria-checked=${motionReduced.value} aria-label="Reduce motion"
|
||||
@click=${() => motionReduced.value = !motionReduced.value}></span>
|
||||
</div>
|
||||
</div>
|
||||
@@ -185,7 +185,7 @@ export class NvSettingsDrawer extends LitElement {
|
||||
<div class="desc">When you change a Tunables slider or load a new scene, push the change to the worker without a manual restart.</div>
|
||||
</div>
|
||||
<span class="toggle ${autoUpdate.value ? 'on' : ''}"
|
||||
role="switch" aria-checked=${autoUpdate.value}
|
||||
role="switch" aria-checked=${autoUpdate.value} aria-label="Auto-rerun on edit"
|
||||
@click=${() => autoUpdate.value = !autoUpdate.value}></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -15,13 +15,32 @@ test.describe('axe-core a11y smoke', () => {
|
||||
for (const view of VIEWS) {
|
||||
test(`view: ${view}`, async ({ page }) => {
|
||||
await page.goto('/');
|
||||
// Dismiss the welcome modal if it auto-shows.
|
||||
await page.evaluate(() => {
|
||||
const sr = (document.querySelector('nv-app') as HTMLElement & { shadowRoot: ShadowRoot }).shadowRoot;
|
||||
const ob = sr.querySelector('nv-onboarding') as HTMLElement | null;
|
||||
if (ob?.hasAttribute('open')) {
|
||||
(ob.shadowRoot?.querySelector('.skip') as HTMLElement | null)?.click();
|
||||
// Dismiss the welcome modal if it auto-shows, then wait until it has left
|
||||
// the accessibility tree. A single poll handles every race at once,
|
||||
// because each of these was observed as an intermittent failure across
|
||||
// chromium/firefox/webkit:
|
||||
// - `nv-app` may not have upgraded yet (no shadowRoot to query);
|
||||
// - Lit may not have upgraded `nv-onboarding` yet, so a `.skip` click
|
||||
// would be a silent no-op;
|
||||
// - `nv-onboarding` decides whether to open inside an async storage
|
||||
// read, so before its `ready` attribute is set "not open" only means
|
||||
// "not decided yet". Treating that as dismissed let the modal open
|
||||
// underneath the scan.
|
||||
// - the modal fades out over 180ms while staying in the DOM. Scanning
|
||||
// inside that window samples a half-transparent panel, and contrast
|
||||
// tooling composites the fading text over the dimmed backdrop into
|
||||
// false failures no user ever sees.
|
||||
await page.waitForFunction(() => {
|
||||
const app = document.querySelector('nv-app') as (HTMLElement & { shadowRoot: ShadowRoot }) | null;
|
||||
if (!app?.shadowRoot) return false;
|
||||
const ob = app.shadowRoot.querySelector('nv-onboarding') as HTMLElement | null;
|
||||
if (!ob) return true;
|
||||
if (!ob.shadowRoot || !ob.hasAttribute('ready')) return false;
|
||||
if (ob.hasAttribute('open')) {
|
||||
(ob.shadowRoot.querySelector('.skip') as HTMLElement | null)?.click();
|
||||
return false;
|
||||
}
|
||||
return getComputedStyle(ob).visibility === 'hidden';
|
||||
});
|
||||
// Navigate to the view via the rail button (except for home which is default).
|
||||
if (view !== 'home') {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
/// <reference types="vitest/config" />
|
||||
import { defineConfig } from 'vite';
|
||||
import { VitePWA } from 'vite-plugin-pwa';
|
||||
|
||||
@@ -66,6 +67,11 @@ export default defineConfig({
|
||||
},
|
||||
},
|
||||
},
|
||||
test: {
|
||||
// tests/ holds Playwright specs (run via `npm run test:e2e`), not Vitest.
|
||||
exclude: ['**/node_modules/**', '**/dist/**', 'tests/**'],
|
||||
passWithNoTests: true,
|
||||
},
|
||||
server: {
|
||||
port: 5173,
|
||||
strictPort: true,
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
# ADR-419: W1700K diagnostics and RuField evidence boundary
|
||||
|
||||
Status: implementation candidate; hardware validation pending.
|
||||
Date: 2026-10-14
|
||||
|
||||
## Decision
|
||||
|
||||
Use an original C consumer of the upstream MIT shared memory ABI, compact JSONL diagnostics, a local browser viewer, and a bounded RuField adapter. Spectrum quality is a separate modality and cannot contribute occupancy evidence.
|
||||
|
||||
Upstream: h0m3us3r/w1700k-sdr commit 0b0413e4309f0c083702fa4eb2d1c2bca4532163, host/stream/include/w1700k_stream.h. Do not copy the GPL recorder.
|
||||
|
||||
## Flow and invariants
|
||||
|
||||
1. The upstream daemon publishes native-endian IQ in ABI v1 shared memory.
|
||||
2. The consumer validates geometry, reads bounded slots, checks tags around copies, and calculates RMS and int16 rail fractions.
|
||||
3. JSONL retains source mode, sample rate, completion index, loss and quality reasons. Raw IQ stays on the host.
|
||||
4. The browser imports records locally. Importing live-labelled data is still file replay.
|
||||
5. RuField preserves source labels, produces untrusted diagnostics and excludes them from occupancy fusion.
|
||||
|
||||
Acceptance: synthetic ABI fixture through the real bridge, JSON validator, viewer and Rust adapter. Malformed geometry, truncation and invalid numbers fail closed. Unknown fields cannot smuggle IQ into downstream evidence.
|
||||
|
||||
## Configuration and timing
|
||||
|
||||
The header does not establish LO, gain, band or an atomic configuration generation. Caller frequency is advisory. All v1 records have config_verified=false and valid=false. Metrics remain useful for inspection; they cannot promote sensing claims.
|
||||
|
||||
Monotonic nanoseconds are host-local, not UTC or cross-device synchronization. Event slots consume no sample time. Unknown sample position remains null.
|
||||
|
||||
IQ scale is 8192. Flattening may legitimately exceed that amplitude; rail fraction counts int16 rails, not calibrated ADC clipping.
|
||||
|
||||
## Alternatives and limits
|
||||
|
||||
A second UDP receiver duplicates transport handling. Direct IQ into a CSI model violates its input contract. Upstream ci16 sidecars truncate gap indices and omit event positions, preventing exact arbitrary replay. Use shared memory and exact synthetic fixtures.
|
||||
|
||||
No CSI extraction, passive radar, pose, hardware accuracy lift or sustained 320 Msps benchmark is claimed. No router flashing or transmission.
|
||||
|
||||
## Security and release
|
||||
|
||||
Bound file sizes, offsets, record lengths and numeric ranges. Reject unknown diagnostic fields. Local file import performs no upload. Trust enrollment is separate from parsing; synthetic evidence stays labelled.
|
||||
|
||||
Publish tested source after independent review. Deploy only the viewer directory, preserving other Pages content. Roll back by reverting this change and its deployment.
|
||||
@@ -1,6 +1,14 @@
|
||||
# RuView README visual system
|
||||
|
||||
The README uses a Cognitum-inspired palette: midnight backgrounds, cyan gradients, wireframe geometry and restrained signal motion. It includes a hero, six feature cards, six navigation icons, an overview, five chapter headings and a Cognitum destination banner.
|
||||
The README matches the RuView header palette: deep navy backgrounds, electric blue
|
||||
RF fields, lime pose and signal accents, warm amber joints and soft white text,
|
||||
layered isometric geometry, moving scan planes, radar sweeps, articulated pose
|
||||
limbs, breathing curves and separated hardware layers. Each feature uses its own
|
||||
illustration rather than repeating the same wireframe cube. It includes a hero, six feature cards, six navigation icons, an overview,
|
||||
five chapter headings and a Cognitum destination banner. Headlines use a heavier
|
||||
Avenir Next / Segoe UI Variable Display / Segoe UI / Helvetica stack, with tighter spacing and a dedicated
|
||||
system monospace stack for labels. Fonts remain local fallbacks so the images
|
||||
also render without a network connection.
|
||||
|
||||
## Rebuild and check
|
||||
|
||||
@@ -22,15 +30,39 @@ local link targets stay inside the repository, including after symlink resolutio
|
||||
|
||||
The committed SVGs are the deployment artifacts. The manifest in
|
||||
`assets/readme/manifest.json` lists every destination.
|
||||
Spatial artwork uses `-spatial-v4.svg` filenames so GitHub's image proxy requests
|
||||
fresh content after the visual upgrade. Previous filenames remain available for
|
||||
existing links; new README wrappers use the versioned assets.
|
||||
|
||||
## Links and rendering
|
||||
|
||||
GitHub renders SVGs as images, so image-internal links are not used. Each SVG has an outer Markdown or HTML link in the README. Each card and navigation icon also has a plain text link for narrow screens and assistive technology. Source headings remain intact so existing README anchors still work.
|
||||
|
||||
Animation uses CSS only. All copy and essential geometry are visible without animation, JavaScript, remote fonts or remote images. `prefers-reduced-motion` disables decorative movement. The original GIF remains available in an expandable demo section; the existing ruOS promotion remains linked.
|
||||
Animation uses CSS only. Depth is projected directly into SVG geometry rather
|
||||
than relying on a WebGL canvas or CSS perspective. Translation is applied to
|
||||
inner groups so it cannot overwrite the outer icon placement. Projected orbit planes stay fixed while dashed signals move along them; scan
|
||||
planes sweep the wireframe volumes. Unused animation rules are removed from
|
||||
each image to keep the complete active SVG set below 100 KB.
|
||||
All copy and essential geometry are visible without animation, JavaScript,
|
||||
remote fonts or remote images. `prefers-reduced-motion` disables decorative
|
||||
movement. The original GIF remains at the top; the existing ruOS promotion
|
||||
remains linked. Existing asset dimensions and README links are preserved.
|
||||
|
||||
## Content boundaries
|
||||
|
||||
The overview and RF room are conceptual illustrations, not live measurements or benchmark evidence. Feature cards link to current source documentation. Pose research is identified as such, and the signal card directs readers to hardware validation. No new speed, accuracy, medical or device-support claims are introduced.
|
||||
|
||||
Sources: `docs/user-guide.md`, `docs/benchmarks/pose-estimation-cog.md`, `v2/crates/wifi-densepose-signal/README.md`, `firmware/esp32-csi-node/README.md`, `docs/integrations/home-assistant.md`, and `harness/ruview/README.md`.
|
||||
|
||||
The hero and pose card use `-spatial-v6.svg`: an original anatomical human
|
||||
silhouette with shaded torso, head, hands and feet, overlapping limbs, surface
|
||||
contours and amber joint markers. A 1.6-second gait drives opposing arm and leg swings, weight shifts and ground
|
||||
shadow compression. An 8-second CSS perspective turn and moving surface light
|
||||
give the anatomical illustration depth. Each limb pivots at its own shoulder
|
||||
or hip. The effect remains a projected SVG illustration, not a skinned WebGL
|
||||
model. The stylesheet disables gait and perspective movement for reduced motion.
|
||||
Chrome 154 validation confirmed the 3D transform, opposing limb transforms,
|
||||
animation in an HTML image, and a frozen standalone SVG under reduced motion.
|
||||
Reduced-motion emulation did not freeze the embedded image in that test; image
|
||||
preference propagation remains a browser-dependent limitation. This is vector artwork, not a real body scan or
|
||||
measured pose reconstruction. Other assets retain their v4 filenames.
|
||||
@@ -0,0 +1,45 @@
|
||||
# RuView RF diagnostics viewer
|
||||
|
||||
Open [index.html](index.html) in a modern browser, or use the deployed documentation page. This is a standalone local JSONL viewer, not a hardware connection. It has no runtime dependencies, telemetry, external assets, uploads or network API.
|
||||
|
||||
1. Run the producer described in [tools/w1700k](../../tools/w1700k/) and save its diagnostic JSONL output.
|
||||
2. Select **Import local JSONL**. Files must be at most 10 MiB, with 1 to 10,000 records.
|
||||
3. Inspect relative RMS, numeric rail fraction, reported missing samples, reason counts and the last 500 observations.
|
||||
4. Select **Clear** to remove the loaded observations, or **Load synthetic demo** for a deterministic synthetic example.
|
||||
|
||||
Raw IQ and unknown fields are rejected. The viewer accepts the frozen `ruview.rf_diagnostics.v1` schema and requires `valid:false`, `config_verified:false` and the `configuration_unverified` reason. Integers must fit JavaScript's safe integer range; larger producer counters are rejected rather than silently rounded.
|
||||
|
||||
## Evidence boundary
|
||||
|
||||
| Producer source_kind | Display |
|
||||
| --- | --- |
|
||||
| synthetic | SYNTHETIC |
|
||||
| captured_replay | CAPTURED REPLAY |
|
||||
| live | LIVE UNVERIFIED |
|
||||
|
||||
Every imported file is replay. Source labels are claims from the input and do not prove that a device was used. Mixed input displays all applicable labels. Nothing here establishes human presence, motion, pose, CSI or calibrated RF power.
|
||||
|
||||
RMS is relative dBFS; numeric rails are an advisory clipping indicator. Missing sample totals sum the producer's per record values. Record order is preserved rather than interpreted as synchronized time. Host monotonic timestamps cannot establish wall clock freshness. The producer TTL is displayed but is never converted into a valid or fresh verdict for file replay.
|
||||
|
||||
All text is rendered with textContent. A restrictive content security policy blocks network connections. Invalid imports clear the previous dataset to avoid showing stale values as the new result.
|
||||
|
||||
## Verification
|
||||
|
||||
From the repository root:
|
||||
|
||||
```sh
|
||||
node --test docs/rf-diagnostics/model.test.cjs
|
||||
node --check docs/rf-diagnostics/app.js
|
||||
```
|
||||
|
||||
Optional browser smoke with an existing Playwright installation and Chromium:
|
||||
|
||||
```sh
|
||||
PLAYWRIGHT_MODULE=/absolute/path/to/playwright node docs/rf-diagnostics/browser-smoke.cjs
|
||||
```
|
||||
|
||||
The browser test checks mobile layout, synthetic rendering, actual local file import, unverified live labels, malformed input rejection, clear and zero external requests. It does not establish physical hardware performance. No package is installed by these commands.
|
||||
|
||||
### Numeric contract
|
||||
|
||||
Sample rates are unknown (0), 40, 80, 160 or 320 Msps. Centre frequency is unknown or 2 to 8 GHz. Each record describes zero or 2048 samples; missing sample counts are multiples of 2048. Absolute sample start remains null. RMS is bounded to −200 through 16 dBFS to accommodate numeric filter headroom, and an available RMS requires an available rail fraction. Reason names are from the producer whitelist and cannot repeat.
|
||||
@@ -0,0 +1,48 @@
|
||||
'use strict';
|
||||
const M=RFDiagnostics,$=id=>document.getElementById(id),ns='http://www.w3.org/2000/svg';
|
||||
let generation=0;
|
||||
function render(rows){
|
||||
const last=rows.at(-1),kinds=[...new Set(rows.map(r=>M.modes[r.source_kind]))];
|
||||
$('mode').textContent=kinds.join(' + ')||'NO DATA';
|
||||
$('count').textContent=rows.length.toLocaleString();
|
||||
$('rms').textContent=last?.rms_dbfs==null?'—':last.rms_dbfs.toFixed(2);
|
||||
$('rails').textContent=last?.rail_fraction==null?'—':(last.rail_fraction*100).toFixed(3)+'%';
|
||||
$('gaps').textContent=rows.length?rows.reduce((n,r)=>n+BigInt(r.missing_samples),0n).toLocaleString():'—';
|
||||
const reasons=new Map();
|
||||
for(const r of rows)for(const reason of new Set(r.invalid_reasons))reasons.set(reason,(reasons.get(reason)||0)+1);
|
||||
$('reasons').replaceChildren();
|
||||
for(const [reason,count] of reasons){const li=document.createElement('li');li.textContent=reason+' · '+count.toLocaleString();$('reasons').append(li);}
|
||||
$('meta').textContent=last?[
|
||||
'Source: '+last.source_id,'Origin: '+last.source_kind+' (file/demo replay)',
|
||||
'Rate: '+(last.sample_rate_hz||'unknown')+' samples/s',
|
||||
'Centre: '+(last.center_frequency_hz??'unknown')+' Hz (unverified)',
|
||||
'Run / slot: '+last.run+' / '+last.slot_index,
|
||||
'Host monotonic ns: '+last.observed_monotonic_ns,
|
||||
'Absolute sample start: '+(last.sample_start??'unknown'),
|
||||
'Configuration verified: false','Valid for inference: false',
|
||||
'TTL: '+last.ttl_ms+' ms; replay freshness unknown'
|
||||
].join('\n'):'No observations';
|
||||
const tail=rows.slice(-500),values=tail.map(r=>r.rms_dbfs).filter(v=>v!==null);
|
||||
const low=Math.min(...values),high=Math.max(...values),span=high-low||1;
|
||||
$('chart').replaceChildren();
|
||||
let path='';
|
||||
tail.forEach((r,i)=>{
|
||||
if(r.rms_dbfs===null){path+=' ';return;}
|
||||
const x=20+i*960/Math.max(1,tail.length-1),y=220-200*(r.rms_dbfs-low)/span;
|
||||
path+=(i===0||tail[i-1].rms_dbfs===null?'M':'L')+x+','+y+' ';
|
||||
if(r.missing_samples){const dot=document.createElementNS(ns,'circle');dot.setAttribute('cx',x);dot.setAttribute('cy',y);dot.setAttribute('r',4);dot.setAttribute('fill','#ffb66c');$('chart').append(dot);}
|
||||
});
|
||||
const line=document.createElementNS(ns,'path');line.setAttribute('d',path);line.setAttribute('fill','none');line.setAttribute('stroke','#71e7db');line.setAttribute('stroke-width',2);$('chart').prepend(line);
|
||||
$('range').textContent=values.length?'Range '+low.toFixed(2)+' to '+high.toFixed(2)+' dBFS · '+tail.length+' latest records':'No RMS observations';
|
||||
$('chart').setAttribute('aria-label',$('range').textContent);
|
||||
}
|
||||
$('demo').onclick=()=>{generation++;render(M.demo());$('status').textContent='Synthetic demonstration loaded. No hardware evidence.';};
|
||||
$('clear').onclick=()=>{generation++;render([]);$('file').value='';$('status').textContent='Cleared. No observations retained.';};
|
||||
$('file').onchange=async()=>{
|
||||
const ticket=++generation,file=$('file').files[0];if(!file)return;
|
||||
try{
|
||||
if(file.size>M.MAX_BYTES)throw Error('File exceeds 10 MiB');
|
||||
const rows=M.parse(await file.text());if(ticket!==generation)return;
|
||||
render(rows);$('status').textContent='Imported '+rows.length+' records. Source labels are unverified claims; this is file replay.';
|
||||
}catch(e){if(ticket!==generation)return;render([]);$('status').textContent='Import rejected: '+e.message;}
|
||||
};
|
||||
@@ -0,0 +1,29 @@
|
||||
'use strict';
|
||||
const assert=require('node:assert/strict'),path=require('node:path');
|
||||
const {chromium}=require(process.env.PLAYWRIGHT_MODULE||'playwright');
|
||||
(async()=>{
|
||||
const browser=await chromium.launch({headless:true});
|
||||
try{
|
||||
const page=await browser.newPage({viewport:{width:390,height:844}});
|
||||
const errors=[],external=[];
|
||||
page.on('pageerror',e=>errors.push(e.message));
|
||||
page.on('request',r=>{if(/^https?:/.test(r.url()))external.push(r.url());});
|
||||
await page.goto('file://'+path.join(__dirname,'index.html'));
|
||||
await page.click('#demo');
|
||||
assert.equal(await page.textContent('#mode'),'SYNTHETIC');
|
||||
assert.equal(await page.textContent('#count'),'96');
|
||||
assert(await page.locator('#chart path').getAttribute('d'));
|
||||
assert.equal(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth),true);
|
||||
const r=require('./model.js').demo()[0];r.source_kind='live';
|
||||
await page.setInputFiles('#file',{name:'live.jsonl',mimeType:'application/json',buffer:Buffer.from(JSON.stringify(r))});
|
||||
await page.waitForFunction(()=>document.querySelector('#mode').textContent==='LIVE UNVERIFIED');
|
||||
assert.match(await page.textContent('#status'),/file replay/);
|
||||
await page.setInputFiles('#file',{name:'bad.jsonl',mimeType:'application/json',buffer:Buffer.from('{"iq":[1,2]}')});
|
||||
await page.waitForFunction(()=>document.querySelector('#status').textContent.startsWith('Import rejected'));
|
||||
assert.equal(await page.textContent('#count'),'0');
|
||||
await page.click('#demo');await page.click('#clear');
|
||||
assert.equal(await page.textContent('#count'),'0');
|
||||
assert.deepEqual(errors,[]);assert.deepEqual(external,[]);
|
||||
console.log('PASS mobile render, demo, JSONL import, unverified live label, malformed rejection, clear, zero external requests');
|
||||
}finally{await browser.close();}
|
||||
})().catch(e=>{console.error(e);process.exitCode=1});
|
||||
@@ -0,0 +1,12 @@
|
||||
<!doctype html>
|
||||
<html lang="en"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; script-src 'self'; style-src 'self'; connect-src 'none'; img-src 'none'; object-src 'none'; base-uri 'none'; form-action 'none'">
|
||||
<title>RuView RF Diagnostics</title><link rel="stylesheet" href="style.css"><script defer src="model.js"></script><script defer src="app.js"></script></head>
|
||||
<body><main><header><p class="eyebrow">COGNITUM / RUVIEW / W1700K</p><span id="mode" class="badge">NO DATA</span><h1>RF diagnostics.</h1><p class="intro">Understand the measurement before trusting the inference.</p></header>
|
||||
<section class="controls" aria-label="Import controls"><label class="button">Import local JSONL<input id="file" type="file" accept=".jsonl,.ndjson,.json,application/json"></label><button id="demo">Load synthetic demo</button><button id="clear">Clear</button><p>Local processing only. Maximum 10 MiB and 10,000 observations. No uploads.</p></section>
|
||||
<p id="status" role="status">Import bridge diagnostics or explore a clearly labelled synthetic example.</p>
|
||||
<aside>ADVISORY ONLY · Configuration is unverified. These records cannot establish presence, pose or CSI accuracy. Imported files are replay, never an active hardware connection.</aside>
|
||||
<section class="metrics" aria-label="Latest observation"><article><p>Latest RMS</p><strong id="rms">—</strong><small>relative dBFS, not calibrated RF power</small></article><article><p>Latest rails</p><strong id="rails">—</strong><small>samples at numeric limits</small></article><article><p>Reported missing</p><strong id="gaps">—</strong><small>sum of per record missing samples</small></article><article><p>Observations</p><strong id="count">0</strong><small>all v1 observations invalid</small></article></section>
|
||||
<section class="panel"><h2>Relative RMS history</h2><p>Record order, last 500 observations. Missing metrics break the line. Orange dots flag missing samples.</p><svg id="chart" viewBox="0 0 1000 240" role="img" aria-label="Relative RMS history; import a diagnostics file"></svg><p id="range">No observations</p></section>
|
||||
<div class="split"><section class="panel"><h2>Invalidation reasons</h2><ul id="reasons"><li>No observations</li></ul></section><section class="panel"><h2>Source metadata</h2><pre id="meta">No observations</pre></section></div>
|
||||
<footer>RuField evidence boundary · 1 second TTL is producer metadata. Host monotonic time is not wall time; file replay cannot establish freshness.</footer></main></body></html>
|
||||
@@ -0,0 +1,46 @@
|
||||
(function(root){
|
||||
'use strict';
|
||||
const MAX_BYTES=10*1024*1024, MAX_RECORDS=10000;
|
||||
const fields='schema source_kind source_id observed_monotonic_ns run slot_index sample_start sample_rate_hz center_frequency_hz config_verified valid invalid_reasons sample_count rms_dbfs rail_fraction missing_samples event_count overrun_slices ttl_ms'.split(' ');
|
||||
const modes={synthetic:'SYNTHETIC',live:'LIVE UNVERIFIED',captured_replay:'CAPTURED REPLAY'};
|
||||
function validate(r){
|
||||
const fail=(s)=>{throw Error(s)};
|
||||
if(!r||Array.isArray(r)||typeof r!=='object')fail('Expected an object');
|
||||
for(const k of Object.keys(r))if(!fields.includes(k))fail('Unsupported field: '+k);
|
||||
for(const k of fields)if(!Object.hasOwn(r,k))fail('Missing field: '+k);
|
||||
if(r.schema!=='ruview.rf_diagnostics.v1')fail('Unsupported schema');
|
||||
if(!Object.hasOwn(modes,r.source_kind)||r.source_id!=='w1700k')fail('Unsupported source');
|
||||
for(const k of ['observed_monotonic_ns','run','slot_index','sample_rate_hz','sample_count','missing_samples','event_count','overrun_slices','ttl_ms'])
|
||||
if(!Number.isSafeInteger(r[k])||r[k]<0)fail('Invalid '+k);
|
||||
if(r.ttl_ms!==1000)fail('Invalid rate or TTL');
|
||||
for(const k of ['sample_start','center_frequency_hz'])
|
||||
if(r[k]!==null&&(!Number.isSafeInteger(r[k])||r[k]<0))fail('Invalid '+k);
|
||||
if(r.rms_dbfs!==null&&(typeof r.rms_dbfs!=='number'||!Number.isFinite(r.rms_dbfs)))fail('Invalid RMS');
|
||||
if(r.rail_fraction!==null&&(typeof r.rail_fraction!=='number'||!Number.isFinite(r.rail_fraction)||r.rail_fraction<0||r.rail_fraction>1))fail('Invalid rail fraction');
|
||||
if(r.config_verified!==false||r.valid!==false)fail('v1 must remain unverified and invalid');
|
||||
if(!Array.isArray(r.invalid_reasons)||r.invalid_reasons.length>32||!r.invalid_reasons.includes('configuration_unverified')||r.invalid_reasons.some(x=>typeof x!=='string'||!/^[a-z_]{1,64}$/.test(x)))fail('Invalid reasons');
|
||||
if(![0,40000000,80000000,160000000,320000000].includes(r.sample_rate_hz))fail('Invalid sample rate');
|
||||
if(r.sample_start!==null)fail('Absolute sample start is unavailable in v1');
|
||||
if(r.center_frequency_hz!==null&&(r.center_frequency_hz<2000000000||r.center_frequency_hz>8000000000))fail('Invalid centre frequency');
|
||||
if(![0,2048].includes(r.sample_count)||r.missing_samples%2048!==0)fail('Invalid sample count');
|
||||
if(r.rms_dbfs!==null&&(r.rms_dbfs< -200||r.rms_dbfs>16||r.rail_fraction===null))fail('Invalid RMS or missing rail fraction');
|
||||
const reasons=['configuration_unverified','lost','event','overrun','stale','tag_mismatch','run_change'];
|
||||
if(new Set(r.invalid_reasons).size!==r.invalid_reasons.length||r.invalid_reasons.length>7||r.invalid_reasons.some(x=>!reasons.includes(x)))fail('Unsupported invalid reason');
|
||||
return r;
|
||||
}
|
||||
function parse(text){
|
||||
if(typeof text!=='string'||new TextEncoder().encode(text).length>MAX_BYTES)throw Error('File exceeds 10 MiB');
|
||||
const lines=text.split(/\r?\n/).filter(x=>x.trim());
|
||||
if(!lines.length||lines.length>MAX_RECORDS)throw Error('Expected 1 to 10,000 records');
|
||||
return lines.map((s,i)=>{try{return validate(JSON.parse(s))}catch(e){throw Error('Record '+(i+1)+': '+e.message)}});
|
||||
}
|
||||
function demo(){return Array.from({length:96},(_,i)=>({
|
||||
schema:'ruview.rf_diagnostics.v1',source_kind:'synthetic',source_id:'w1700k',
|
||||
observed_monotonic_ns:i*100000000,run:1,slot_index:i,sample_start:null,
|
||||
sample_rate_hz:40000000,center_frequency_hz:2437000000,config_verified:false,valid:false,
|
||||
invalid_reasons:i%23===0?['configuration_unverified','lost']:['configuration_unverified'],
|
||||
sample_count:2048,rms_dbfs:-34+Math.sin(i/8)*6,rail_fraction:i%23===0?0.03:0,
|
||||
missing_samples:i%23===0?2048:0,event_count:0,overrun_slices:0,ttl_ms:1000}));}
|
||||
const api={MAX_BYTES,MAX_RECORDS,modes,validate,parse,demo};
|
||||
if(typeof module==='object')module.exports=api;else root.RFDiagnostics=api;
|
||||
})(globalThis);
|
||||
@@ -0,0 +1,23 @@
|
||||
'use strict';
|
||||
const {test}=require('node:test'),assert=require('node:assert/strict'),M=require('./model.js');
|
||||
const good=()=>M.demo()[0];
|
||||
test('synthetic fixture and JSONL roundtrip',()=>assert.equal(M.parse(M.demo().map(JSON.stringify).join('\n')).length,96));
|
||||
test('captured and live labels remain unverified',()=>{for(const source_kind of ['live','captured_replay'])assert.equal(M.validate({...good(),source_kind}).valid,false);});
|
||||
test('reject malformed, empty, oversized and too many records',()=>{
|
||||
for(const s of ['', '[]','{',' '.repeat(M.MAX_BYTES+1),Array(10002).fill('{}').join('\n')])assert.throws(()=>M.parse(s));
|
||||
});
|
||||
test('reject inference promotion, raw IQ, nonfinite metrics and unsafe indices',()=>{
|
||||
for(const patch of [{valid:true},{config_verified:true},{iq:[1,2]},{rms_dbfs:Infinity},{rail_fraction:2},{rail_fraction:NaN},{observed_monotonic_ns:2**53},{source_kind:'__proto__'},{ttl_ms:0},{invalid_reasons:['<script>']},{invalid_reasons:[]}])assert.throws(()=>M.validate({...good(),...patch}));
|
||||
});
|
||||
test('reject missing fields with record index',()=>{
|
||||
const r=good();delete r.sample_start;assert.throws(()=>M.parse(JSON.stringify(r)),/Record 1: Missing field/);
|
||||
});
|
||||
test('allow unavailable measurements but retain invalid evidence',()=>{
|
||||
assert.equal(M.validate({...good(),rms_dbfs:null,rail_fraction:null,center_frequency_hz:null,sample_rate_hz:0}).valid,false);
|
||||
});
|
||||
|
||||
test('match producer and RuField numeric contract',()=>{
|
||||
for(const patch of [{sample_start:0},{sample_count:4096},{missing_samples:128},{sample_rate_hz:123},{center_frequency_hz:1999999999},{center_frequency_hz:8000000001},{rms_dbfs:17},{rms_dbfs:-201},{rms_dbfs:-6,rail_fraction:null},{invalid_reasons:['configuration_unverified','configuration_unverified']},{invalid_reasons:['configuration_unverified','other']}])
|
||||
assert.throws(()=>M.validate({...good(),...patch}));
|
||||
for(const patch of [{center_frequency_hz:7576000000},{rms_dbfs:15.05},{sample_count:0,rms_dbfs:null,rail_fraction:null},{sample_rate_hz:0},{missing_samples:2048}])M.validate({...good(),...patch});
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
:root{color-scheme:dark;font-family:Inter,ui-sans-serif,system-ui,sans-serif;background:#080f17;color:#d9e8ee}*{box-sizing:border-box}body{margin:0;background:radial-gradient(ellipse at 85% 0,#113642,transparent 50%)}main{max-width:1200px;margin:auto;padding:48px 28px}header{position:relative}.eyebrow{letter-spacing:.2em;color:#62d9da;font-size:12px;font-weight:700}h1{font-size:clamp(38px,7vw,76px);letter-spacing:-.055em;margin:18px 0}.intro{font-size:20px;color:#a4bcc6}.badge{display:inline-block;padding:8px 14px;border:1px solid #b89851;border-radius:32px;color:#f5d18c;font-size:12px;font-weight:bold}.controls{margin-top:32px;display:flex;gap:12px;flex-wrap:wrap;align-items:center}.controls p{flex-basis:100%;font-size:13px;color:#a4bcc6}button,.button{font:inherit;font-size:14px;border:1px solid #38616f;background:#152b37;color:#e7faff;border-radius:10px;padding:13px 18px;cursor:pointer}.button{background:#77e9dd;color:#082b2c;font-weight:700;position:relative;overflow:hidden}.button input{position:absolute;inset:0;width:100%;height:100%;opacity:0;cursor:pointer}button:hover,.button:hover{filter:brightness(1.2)}button:focus-visible,.button:focus-within{outline:3px solid #fff;outline-offset:3px}aside{border-left:3px solid #e5b85e;padding:16px 20px;background:#30291c;font-size:14px;line-height:1.6;color:#f5ddab}.metrics{display:grid;grid-template-columns:repeat(4,1fr);gap:14px;margin:24px 0}.metrics article,.panel{border:1px solid #25414d;background:#0d1c26;border-radius:14px;padding:22px}.metrics p{font-size:13px;color:#9cb6c1}.metrics strong{font-size:30px;color:#79eddf;display:block;margin:16px 0}.metrics small{color:#9cb6c1;font-size:11px}.panel{margin-bottom:18px}h2{font-size:18px;margin-top:0}.panel p,footer{color:#9cb6c1;font-size:13px;line-height:1.6}svg{width:100%;height:auto;display:block;background:linear-gradient(#132c37,#0d1c26);border-radius:8px}.split{display:grid;grid-template-columns:1fr 1fr;gap:18px}pre{white-space:pre-wrap;word-break:break-word;color:#b7d1da;line-height:1.7;font-size:12px}li{margin-bottom:10px;overflow-wrap:anywhere}#status{font-size:14px;min-height:22px}footer{margin:24px 0} @media(max-width:700px){main{padding:28px 16px}.metrics{grid-template-columns:1fr 1fr}.split{grid-template-columns:1fr;gap:0}.metrics article{padding:14px}.metrics strong{font-size:24px}}
|
||||
@@ -495,6 +495,93 @@ dropped. Without a password flag the script prompts on a terminal; in scripts
|
||||
and CI it doesn't prompt and fails if no password was given now or saved
|
||||
earlier for that port.
|
||||
|
||||
### Remote Configuration (no USB)
|
||||
|
||||
`provision.py` writes NVS over USB and is still how a **new** node is set up.
|
||||
Once a node is on the network, `config_push.py` changes the same parameters
|
||||
over HTTP, so a node count, server address or threshold no longer needs a
|
||||
ladder and a cable.
|
||||
|
||||
This is **not** OTA. OTA writes a firmware image to an app partition; this
|
||||
writes NVS parameters. They share the port and the PSK, nothing else.
|
||||
|
||||
```bash
|
||||
# what is this node running?
|
||||
python config_push.py --node <node-ip> --get
|
||||
|
||||
# change something and let the node restart to apply it
|
||||
python config_push.py --node <node-ip> --set tdm_node_count=9
|
||||
|
||||
# stage across the fleet, restart on your own schedule
|
||||
python config_push.py --fleet nodes.txt --set edge_tier=1 --no-reboot
|
||||
```
|
||||
|
||||
Config is read only at boot, so a change does nothing until the node restarts.
|
||||
`config_push.py` therefore restarts it by default; `--no-reboot` stages instead.
|
||||
|
||||
#### Why some parameters reboot-and-prove
|
||||
|
||||
Most settings are recoverable: a wrong `target_ip` sends CSI nowhere, but the
|
||||
node keeps its association and stays reachable, so the next push fixes it.
|
||||
|
||||
`wifi_ssid`, `wifi_password`, `csi_channel`, `channel_hop_count` and `dwell_ms`
|
||||
are not. A wrong value there means the node never rejoins the network and no
|
||||
push can ever reach it again -- worse than a bad OTA, which at least has a
|
||||
second partition to fall back to.
|
||||
|
||||
Those go through a **trial**. The node banks its current values, writes the new
|
||||
ones, and reboots. It keeps them only once it has actually re-associated; if it
|
||||
cannot within `trial_seconds` (default 120, max 900) it restores the bank and
|
||||
reboots itself. That is the app-level equivalent of bootloader rollback, and
|
||||
unlike bootloader rollback it can be delivered by OTA.
|
||||
|
||||
`GET /config` reports which parameters carry that risk, so tooling does not
|
||||
keep its own copy of the list to drift out of date.
|
||||
|
||||
#### The onboard LED
|
||||
|
||||
The WS2812 on the devkit is not a power light. It runs a **40 Hz square-wave
|
||||
gamma stimulus** (GENUS) whose colour is live CSI motion energy through a
|
||||
viridis colormap -- purple when still, yellow when moving. That makes it a
|
||||
useful bench indicator and a bad thing to have in a bedroom.
|
||||
|
||||
It used to be compile-time only (`CONFIG_LED_GAMMA_VIZ`), so darkening one node
|
||||
meant reflashing all of them. It is now runtime selectable per node:
|
||||
|
||||
| `led_mode` | name | behaviour |
|
||||
|---|---|---|
|
||||
| 0 | `off` | dark |
|
||||
| 1 | `steady` | motion colour, no flicker |
|
||||
| 2 | `flicker` | 40 Hz gamma stimulus (default) |
|
||||
|
||||
`led_brightness` is 0-100 and scales the colormap.
|
||||
|
||||
```bash
|
||||
python config_push.py --node <node-ip> --set led_mode=off
|
||||
python config_push.py --node <node-ip> --set led_mode=steady led_brightness=10
|
||||
```
|
||||
|
||||
Both are read by the LED callback on every cycle, so they are the one pair of
|
||||
parameters that applies **immediately with no restart** -- `config_push.py`
|
||||
does not reboot a node when the change is LED-only.
|
||||
|
||||
Defaults preserve existing behaviour: a node never told otherwise keeps
|
||||
flickering at full brightness exactly as before.
|
||||
|
||||
#### Endpoint
|
||||
|
||||
| Method | Path | Notes |
|
||||
|---|---|---|
|
||||
| `GET` | `/config` | Current values; `wifi_password` reported only as `(set)` |
|
||||
| `POST` | `/config` | JSON object of parameters to change |
|
||||
|
||||
Both require `Authorization: Bearer <ota-psk>` and fail closed when no PSK is
|
||||
provisioned. A body is validated in full before anything is committed, so one
|
||||
bad field changes nothing, and an unrecognised parameter is an error rather
|
||||
than a silently ignored setting.
|
||||
|
||||
Control fields, which are not stored: `trial_seconds`, `reboot`.
|
||||
|
||||
### OTA key provisioning
|
||||
|
||||
Create a random 256-bit key in a private file outside the repository. For
|
||||
|
||||
@@ -0,0 +1,205 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Read and change node configuration over the network -- no USB.
|
||||
|
||||
This is provisioning, not OTA. `ota_push.py` writes firmware images; this
|
||||
writes the same NVS parameters provision.py writes over USB, to a running
|
||||
node, through the authenticated server on port 8032.
|
||||
|
||||
# what is this node set to?
|
||||
python config_push.py --node <node-ip> --get
|
||||
|
||||
# a change that cannot orphan a node: written, then the node restarts so
|
||||
# it actually takes effect (config is only read at boot)
|
||||
python config_push.py --node <node-ip> --set tdm_node_count=9
|
||||
|
||||
# stage it across the fleet instead, and restart on your own schedule
|
||||
python config_push.py --fleet nodes.txt --set tdm_node_count=9 --no-reboot
|
||||
|
||||
# darken the bedroom node's LED -- takes effect at once, no restart
|
||||
python config_push.py --node <node-ip> --set led_mode=steady led_brightness=10
|
||||
python config_push.py --node <node-ip> --set led_mode=off
|
||||
|
||||
# the whole fleet at once
|
||||
python config_push.py --fleet nodes.txt --set edge_tier=1
|
||||
|
||||
Parameters that affect WiFi association (ssid, password, channel) are applied
|
||||
as a TRIAL: the node banks its current settings, reboots, and keeps the new
|
||||
ones only if it manages to re-associate. If it cannot, it restores the bank
|
||||
and reboots itself. That is what makes a remote SSID change safe; without it
|
||||
one typo means a ladder and a USB cable for every node.
|
||||
|
||||
The PSK is read from a file and never printed, logged, or passed on argv.
|
||||
"""
|
||||
import argparse
|
||||
import os
|
||||
import json
|
||||
import sys
|
||||
import urllib.error
|
||||
import urllib.request
|
||||
|
||||
# No default path: a secret location is site-specific, and baking one in
|
||||
# means shipping somebody's directory layout to everyone else. Set
|
||||
# RUVIEW_OTA_PSK_FILE once, or pass --psk-file.
|
||||
PSK_DEFAULT = os.environ.get("RUVIEW_OTA_PSK_FILE")
|
||||
PORT = 8032
|
||||
|
||||
# The onboard WS2812 defaults to a 40 Hz square-wave gamma stimulus at full
|
||||
# brightness. Fine on a bench, hostile in a bedroom.
|
||||
LED_MODES = {"off": 0, "steady": 1, "flicker": 2}
|
||||
|
||||
|
||||
def call(host, psk, method, body=None, timeout=20):
|
||||
url = "http://%s:%d/config" % (host, PORT)
|
||||
data = json.dumps(body).encode() if body is not None else None
|
||||
req = urllib.request.Request(url, data=data, method=method)
|
||||
req.add_header("Authorization", "Bearer " + psk)
|
||||
if data:
|
||||
req.add_header("Content-Type", "application/json")
|
||||
try:
|
||||
with urllib.request.urlopen(req, timeout=timeout) as r:
|
||||
return json.loads(r.read().decode() or "{}"), None
|
||||
except urllib.error.HTTPError as e:
|
||||
return None, "HTTP %d: %s" % (e.code, e.read().decode()[:200])
|
||||
except Exception as e: # noqa: BLE001 - report, never raise
|
||||
return None, str(e)
|
||||
|
||||
|
||||
def looks_armed(err):
|
||||
"""A trial push that reboots can lose its response on a congested node.
|
||||
|
||||
Observed on real hardware: the write succeeded, the node rebooted and
|
||||
reverted correctly, but curl saw nothing. Reporting that as a failure
|
||||
invites a re-push at exactly the wrong moment -- while a trial is pending
|
||||
and the banked values are still unproven.
|
||||
"""
|
||||
e = (err or "").lower()
|
||||
return any(k in e for k in ("timed out", "reset", "aborted",
|
||||
"expecting value", "remote end closed"))
|
||||
|
||||
|
||||
def coerce(text):
|
||||
"""Numbers stay numbers so the node's type check passes; else a string."""
|
||||
try:
|
||||
return int(text)
|
||||
except ValueError:
|
||||
pass
|
||||
try:
|
||||
return float(text)
|
||||
except ValueError:
|
||||
return text
|
||||
|
||||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser()
|
||||
g = ap.add_mutually_exclusive_group(required=True)
|
||||
g.add_argument("--node", help="node IP")
|
||||
g.add_argument("--fleet", help="file of node IPs, one per line")
|
||||
ap.add_argument("--get", action="store_true", help="read current config")
|
||||
ap.add_argument("--set", nargs="+", metavar="KEY=VALUE", default=None)
|
||||
ap.add_argument("--trial-seconds", type=int, default=None,
|
||||
help="how long a node may take to re-associate before reverting")
|
||||
ap.add_argument("--psk-file", default=PSK_DEFAULT,
|
||||
help="file holding the fleet OTA PSK; defaults to "
|
||||
"$RUVIEW_OTA_PSK_FILE")
|
||||
ap.add_argument("--no-reboot", action="store_true",
|
||||
help="write the values but leave the node running the old "
|
||||
"ones; config is only read at boot, so nothing takes "
|
||||
"effect until you restart it yourself")
|
||||
ap.add_argument("--yes", action="store_true",
|
||||
help="skip the confirmation for association-affecting changes")
|
||||
a = ap.parse_args()
|
||||
|
||||
if not a.get and not a.set:
|
||||
ap.error("choose --get or --set")
|
||||
|
||||
if not a.psk_file:
|
||||
print("no PSK file: pass --psk-file or set RUVIEW_OTA_PSK_FILE",
|
||||
file=sys.stderr)
|
||||
return 2
|
||||
try:
|
||||
psk = open(a.psk_file).read().strip()
|
||||
except OSError as e:
|
||||
print("cannot read PSK file: %s" % e, file=sys.stderr)
|
||||
return 2
|
||||
if not psk:
|
||||
print("PSK file is empty", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
hosts = [a.node] if a.node else [
|
||||
l.strip() for l in open(a.fleet) if l.strip() and not l.startswith("#")]
|
||||
|
||||
if a.get:
|
||||
for h in hosts:
|
||||
r, err = call(h, psk, "GET")
|
||||
if err:
|
||||
print("%-16s UNREACHABLE %s" % (h, err))
|
||||
continue
|
||||
cfg = r.get("config", {})
|
||||
flag = " [TRIAL PENDING]" if r.get("trial_pending") else ""
|
||||
print("\n%s%s" % (h, flag))
|
||||
for k in sorted(cfg):
|
||||
v = cfg[k]
|
||||
print(" %-22s %s" % (k, "-" if v is None else v))
|
||||
return 0
|
||||
|
||||
body = {}
|
||||
for item in a.set:
|
||||
if "=" not in item:
|
||||
ap.error("--set takes KEY=VALUE, got %r" % item)
|
||||
k, v = item.split("=", 1)
|
||||
k, v = k.strip(), v.strip()
|
||||
if k == "led_mode" and v.lower() in LED_MODES:
|
||||
body[k] = LED_MODES[v.lower()]
|
||||
else:
|
||||
body[k] = coerce(v)
|
||||
if a.trial_seconds:
|
||||
body["trial_seconds"] = a.trial_seconds
|
||||
if a.no_reboot:
|
||||
body["reboot"] = False
|
||||
|
||||
# Ask the first reachable node which parameters carry the orphaning risk,
|
||||
# rather than keeping a second copy of that list here that could drift
|
||||
# away from the firmware's.
|
||||
risky = []
|
||||
for h in hosts:
|
||||
probe, err = call(h, psk, "GET")
|
||||
if probe:
|
||||
risky = [k for k in probe.get("requires_trial", []) if k in body]
|
||||
break
|
||||
if risky and not a.yes:
|
||||
print("These change WiFi association: %s" % ", ".join(sorted(risky)))
|
||||
print("Each node will reboot and revert automatically if it cannot")
|
||||
print("re-associate. %d node(s) affected." % len(hosts))
|
||||
if input("proceed? [y/N] ").strip().lower() != "y":
|
||||
return 1
|
||||
|
||||
fail = 0
|
||||
for h in hosts:
|
||||
r, err = call(h, psk, "POST", body)
|
||||
if err:
|
||||
if risky and looks_armed(err):
|
||||
print("%-16s UNCONFIRMED the reply was lost; the trial has very "
|
||||
"likely armed." % h)
|
||||
print("%-16s Do NOT re-push. Wait ~%ds: it either "
|
||||
"re-associates or reverts itself."
|
||||
% ("", (a.trial_seconds or 120) + 30))
|
||||
continue
|
||||
print("%-16s FAILED %s" % (h, err))
|
||||
fail += 1
|
||||
continue
|
||||
n = r.get("changed", 0)
|
||||
if r.get("trial"):
|
||||
print("%-16s TRIAL %d field(s), reverts in %ds unless it re-associates"
|
||||
% (h, n, r.get("trial_seconds", 0)))
|
||||
elif r.get("note", "").startswith("applied immediately"):
|
||||
print("%-16s LIVE %d field(s) applied now, no restart" % (h, n))
|
||||
elif r.get("rebooting"):
|
||||
print("%-16s OK %d field(s), rebooting to apply" % (h, n))
|
||||
else:
|
||||
print("%-16s STAGED %d field(s) written, NOT in effect until restart"
|
||||
% (h, n))
|
||||
return 1 if fail else 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
@@ -1,5 +1,5 @@
|
||||
set(SRCS
|
||||
"main.c" "csi_collector.c" "node_log.c"
|
||||
"main.c" "csi_collector.c" "config_api.c" "node_log.c"
|
||||
"thermal.c" "stream_sender.c" "nvs_config.c"
|
||||
"serial_onboarding.c" "serial_onboarding_protocol.c"
|
||||
"edge_processing.c" "ota_update.c" "ota_health.c" "power_mgmt.c"
|
||||
@@ -61,6 +61,15 @@ if(IDF_VERSION_MAJOR GREATER_EQUAL 6)
|
||||
list(APPEND REQUIRES esp_driver_tsens)
|
||||
endif()
|
||||
|
||||
# cJSON (config_api.c) ships as the bundled `json` component up to ESP-IDF 5.x
|
||||
# and was moved OUT to the component manager in v6, where requiring `json`
|
||||
# fails outright: "Failed to resolve component 'json' required by component
|
||||
# 'main': unknown name." Declaring it unconditionally broke the v6.0.2 lane
|
||||
# while the 5.4 lane stayed green -- one toolchain is not the build matrix.
|
||||
if(IDF_VERSION_MAJOR LESS 6)
|
||||
list(APPEND REQUIRES json)
|
||||
endif()
|
||||
|
||||
# ADR-061: Mock CSI generator for QEMU testing + ADR-081 mock radio binding
|
||||
if(CONFIG_CSI_MOCK_ENABLED)
|
||||
list(APPEND SRCS "mock_csi.c" "rv_radio_ops_mock.c")
|
||||
|
||||
@@ -0,0 +1,626 @@
|
||||
/**
|
||||
* @file config_api.c
|
||||
* @brief Remote configuration endpoint. See config_api.h for the design.
|
||||
*/
|
||||
|
||||
#include "config_api.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include "cJSON.h"
|
||||
#include "ota_update.h"
|
||||
#include "nvs_config.h"
|
||||
#include "esp_log.h"
|
||||
#include "esp_system.h"
|
||||
#include "esp_timer.h"
|
||||
#include "esp_wifi.h"
|
||||
#include "nvs_flash.h"
|
||||
#include "nvs.h"
|
||||
|
||||
static const char *TAG = "config_api";
|
||||
|
||||
/* POST /link/reassoc state (handler near config_api_register). */
|
||||
#define REASSOC_MIN_INTERVAL_US (60LL * 1000 * 1000)
|
||||
static uint32_t s_reassoc_count;
|
||||
static int64_t s_reassoc_last_us;
|
||||
static esp_timer_handle_t s_reassoc_timer;
|
||||
|
||||
#define CFG_NS "csi_cfg" /* the live config nvs_config.c reads */
|
||||
#define TRIAL_NS "cfg_trial" /* trial state, separate so a wipe is easy */
|
||||
#define TRIAL_DEFAULT 120 /* s to prove association before reverting */
|
||||
#define TRIAL_MAX 900
|
||||
#define BODY_MAX 2048
|
||||
|
||||
typedef enum { T_STR, T_U8, T_U16, T_U32 } cfg_type_t;
|
||||
|
||||
/**
|
||||
* The writable surface.
|
||||
*
|
||||
* `nvs` MUST match the key nvs_config_load() reads, and nothing enforces that
|
||||
* -- a typo here writes a key nobody loads. The GET handler exists partly as
|
||||
* the check: it reads back through the same keys, so a setting that did not
|
||||
* take is visible immediately rather than silently ignored.
|
||||
*
|
||||
* `scale` handles the thresholds nvs_config.c stores as fixed point (u16 of
|
||||
* value*1000) while exposing a float; 0 means the value passes through.
|
||||
*
|
||||
* `assoc` marks the keys that can orphan a node, and is the ONLY thing that
|
||||
* decides whether a change goes through the trial path.
|
||||
*/
|
||||
typedef struct {
|
||||
const char *json;
|
||||
const char *nvs;
|
||||
cfg_type_t type;
|
||||
uint32_t lo, hi;
|
||||
size_t maxlen;
|
||||
float scale;
|
||||
bool assoc;
|
||||
} cfg_key_t;
|
||||
|
||||
static const cfg_key_t KEYS[] = {
|
||||
/* json nvs type lo hi len scale assoc */
|
||||
{ "wifi_ssid", "ssid", T_STR, 0, 0, 32, 0, true },
|
||||
{ "wifi_password", "password", T_STR, 0, 0, 64, 0, true },
|
||||
{ "channel_hop_count", "hop_count", T_U8, 1, 14, 0, 0, true },
|
||||
{ "dwell_ms", "dwell_ms", T_U32, 10, 60000, 0, 0, true },
|
||||
{ "csi_channel", "csi_channel", T_U8, 0, 14, 0, 0, true },
|
||||
|
||||
{ "target_ip", "target_ip", T_STR, 0, 0, 16, 0, false },
|
||||
{ "target_port", "target_port", T_U16, 1, 65535, 0, 0, false },
|
||||
{ "node_id", "node_id", T_U8, 0, 254, 0, 0, false },
|
||||
{ "tdm_slot_index", "tdm_slot", T_U8, 0, 63, 0, 0, false },
|
||||
{ "tdm_node_count", "tdm_nodes", T_U8, 1, 64, 0, 0, false },
|
||||
{ "beacon_period_ms", "beacon_ms", T_U16, 0, 10000, 0, 0, false },
|
||||
{ "edge_tier", "edge_tier", T_U8, 0, 2, 0, 0, false },
|
||||
{ "presence_thresh", "pres_thresh", T_U16, 0, 65535, 0, 1000, false },
|
||||
{ "fall_thresh", "fall_thresh", T_U16, 0, 65535, 0, 1000, false },
|
||||
{ "vital_window", "vital_win", T_U16, 8, 4096, 0, 0, false },
|
||||
{ "vital_interval_ms", "vital_int", T_U16, 100, 60000, 0, 0, false },
|
||||
{ "top_k_count", "subk_count", T_U8, 1, 64, 0, 0, false },
|
||||
{ "power_duty", "power_duty", T_U8, 10, 100, 0, 0, false },
|
||||
{ "zone_name", "zone_name", T_STR, 0, 0, 16, 0, false },
|
||||
{ "swarm_heartbeat_sec","swarm_hb", T_U16, 1, 65535, 0, 0, false },
|
||||
{ "swarm_ingest_sec", "swarm_ingest", T_U16, 1, 65535, 0, 0, false },
|
||||
{ "led_mode", "led_mode", T_U8, 0, 2, 0, 0, false },
|
||||
{ "led_brightness", "led_bright", T_U8, 0, 100, 0, 0, false },
|
||||
};
|
||||
#define NKEYS (sizeof(KEYS) / sizeof(KEYS[0]))
|
||||
|
||||
/* Body fields that control the request rather than naming a stored parameter.
|
||||
* They are not in KEYS, so every pass over the body must skip them. */
|
||||
/**
|
||||
* Parameters the running firmware re-reads every cycle, so a change takes
|
||||
* effect with no restart. Everything else is consumed once during init, which
|
||||
* is why a plain config push reboots by default.
|
||||
*/
|
||||
static bool is_live(const char *name)
|
||||
{
|
||||
return strcmp(name, "led_mode") == 0 ||
|
||||
strcmp(name, "led_brightness") == 0;
|
||||
}
|
||||
|
||||
static bool is_meta(const char *name)
|
||||
{
|
||||
return strcmp(name, "trial_seconds") == 0 ||
|
||||
strcmp(name, "reboot") == 0;
|
||||
}
|
||||
|
||||
static esp_timer_handle_t s_revert_timer = NULL;
|
||||
static bool s_trial_armed = false;
|
||||
|
||||
static const cfg_key_t *find_key(const char *json)
|
||||
{
|
||||
for (size_t i = 0; i < NKEYS; i++) {
|
||||
if (strcmp(KEYS[i].json, json) == 0) return &KEYS[i];
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- reads --- */
|
||||
|
||||
/**
|
||||
* Current NVS value as a cJSON node, or JSON null when the key is unset.
|
||||
*
|
||||
* `redact` must be false when banking values for a trial: the bank is written
|
||||
* back verbatim on revert, so a redacted password would be restored as the
|
||||
* literal string "(set)" and lock the node off the network permanently -- the
|
||||
* exact failure the trial exists to prevent.
|
||||
*/
|
||||
static cJSON *read_key(nvs_handle_t h, const cfg_key_t *k, bool redact)
|
||||
{
|
||||
if (k->type == T_STR) {
|
||||
char buf[96];
|
||||
size_t len = sizeof(buf);
|
||||
if (nvs_get_str(h, k->nvs, buf, &len) != ESP_OK) return cJSON_CreateNull();
|
||||
/* Never hand back key material, even to an authenticated caller: it
|
||||
* would end up in logs and terminal scrollback. Presence only. */
|
||||
if (redact && strcmp(k->json, "wifi_password") == 0) {
|
||||
return cJSON_CreateString(buf[0] ? "(set)" : "");
|
||||
}
|
||||
return cJSON_CreateString(buf);
|
||||
}
|
||||
uint32_t v = 0;
|
||||
esp_err_t e;
|
||||
if (k->type == T_U8) {
|
||||
uint8_t x; e = nvs_get_u8(h, k->nvs, &x); v = x;
|
||||
} else if (k->type == T_U16) {
|
||||
uint16_t x; e = nvs_get_u16(h, k->nvs, &x); v = x;
|
||||
} else {
|
||||
e = nvs_get_u32(h, k->nvs, &v);
|
||||
}
|
||||
if (e != ESP_OK) return cJSON_CreateNull();
|
||||
if (k->scale > 0) return cJSON_CreateNumber((double)v / k->scale);
|
||||
return cJSON_CreateNumber((double)v);
|
||||
}
|
||||
|
||||
/* --------------------------------------------------------------- writes --- */
|
||||
|
||||
/**
|
||||
* Validate and stage one value. Returns false with `err` set on a bad value.
|
||||
* Nothing is committed here; the caller commits once the whole body validates,
|
||||
* so a body with one bad field changes nothing at all.
|
||||
*/
|
||||
static bool write_key(nvs_handle_t h, const cfg_key_t *k, const cJSON *v,
|
||||
char *err, size_t errlen)
|
||||
{
|
||||
if (k->type == T_STR) {
|
||||
if (!cJSON_IsString(v)) {
|
||||
snprintf(err, errlen, "%s must be a string", k->json);
|
||||
return false;
|
||||
}
|
||||
if (strlen(v->valuestring) >= k->maxlen) {
|
||||
snprintf(err, errlen, "%s exceeds %u chars", k->json,
|
||||
(unsigned)(k->maxlen - 1));
|
||||
return false;
|
||||
}
|
||||
return nvs_set_str(h, k->nvs, v->valuestring) == ESP_OK;
|
||||
}
|
||||
|
||||
if (!cJSON_IsNumber(v)) {
|
||||
snprintf(err, errlen, "%s must be a number", k->json);
|
||||
return false;
|
||||
}
|
||||
double d = v->valuedouble;
|
||||
if (k->scale > 0) d *= k->scale;
|
||||
if (d < 0) d = 0;
|
||||
uint32_t val = (uint32_t)(d + 0.5);
|
||||
if (val < k->lo || val > k->hi) {
|
||||
snprintf(err, errlen, "%s out of range %lu..%lu", k->json,
|
||||
(unsigned long)k->lo, (unsigned long)k->hi);
|
||||
return false;
|
||||
}
|
||||
if (k->type == T_U8) return nvs_set_u8(h, k->nvs, (uint8_t)val) == ESP_OK;
|
||||
if (k->type == T_U16) return nvs_set_u16(h, k->nvs, (uint16_t)val) == ESP_OK;
|
||||
return nvs_set_u32(h, k->nvs, val) == ESP_OK;
|
||||
}
|
||||
|
||||
/** Restore one banked value; JSON null means the key was previously unset. */
|
||||
static void restore_key(nvs_handle_t h, const cfg_key_t *k, const cJSON *v)
|
||||
{
|
||||
if (cJSON_IsNull(v)) {
|
||||
nvs_erase_key(h, k->nvs); /* absence is a state worth restoring */
|
||||
return;
|
||||
}
|
||||
char err[64];
|
||||
write_key(h, k, v, err, sizeof(err));
|
||||
}
|
||||
|
||||
/* ---------------------------------------------------------------- trial --- */
|
||||
|
||||
/** Apply the banked values and reboot. Runs from the esp_timer task. */
|
||||
static void trial_revert(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
ESP_LOGE(TAG, "config trial FAILED to associate -- reverting");
|
||||
|
||||
nvs_handle_t t, c;
|
||||
if (nvs_open(TRIAL_NS, NVS_READWRITE, &t) != ESP_OK) {
|
||||
/* Cannot read the bank, so cannot revert. Rebooting would spin
|
||||
* forever; stay up so the node is at least diagnosable if it is
|
||||
* somehow still reachable. */
|
||||
ESP_LOGE(TAG, "trial namespace unreadable; cannot revert");
|
||||
return;
|
||||
}
|
||||
|
||||
size_t len = 0;
|
||||
char *json = NULL;
|
||||
if (nvs_get_str(t, "bank", NULL, &len) == ESP_OK && len > 1 &&
|
||||
(json = malloc(len)) != NULL &&
|
||||
nvs_get_str(t, "bank", json, &len) == ESP_OK &&
|
||||
nvs_open(CFG_NS, NVS_READWRITE, &c) == ESP_OK) {
|
||||
|
||||
cJSON *root = cJSON_Parse(json);
|
||||
if (root) {
|
||||
cJSON *it = NULL;
|
||||
cJSON_ArrayForEach(it, root) {
|
||||
const cfg_key_t *k = find_key(it->string);
|
||||
if (k) {
|
||||
restore_key(c, k, it);
|
||||
ESP_LOGW(TAG, "reverted %s", k->json);
|
||||
}
|
||||
}
|
||||
cJSON_Delete(root);
|
||||
}
|
||||
nvs_commit(c);
|
||||
nvs_close(c);
|
||||
}
|
||||
free(json);
|
||||
|
||||
/* Clear the trial BEFORE rebooting: if the restored config is itself
|
||||
* broken we must not loop reverting forever. One attempt, then the node
|
||||
* comes up on the old values and stays there. */
|
||||
nvs_erase_all(t);
|
||||
nvs_commit(t);
|
||||
nvs_close(t);
|
||||
|
||||
ESP_LOGE(TAG, "rebooting onto the previous configuration");
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
void config_trial_boot_check(void)
|
||||
{
|
||||
nvs_handle_t t;
|
||||
if (nvs_open(TRIAL_NS, NVS_READONLY, &t) != ESP_OK) return;
|
||||
|
||||
uint8_t armed = 0;
|
||||
uint32_t deadline = TRIAL_DEFAULT;
|
||||
nvs_get_u8(t, "armed", &armed);
|
||||
nvs_get_u32(t, "deadline", &deadline);
|
||||
nvs_close(t);
|
||||
if (!armed) return;
|
||||
|
||||
s_trial_armed = true;
|
||||
if (deadline == 0 || deadline > TRIAL_MAX) deadline = TRIAL_DEFAULT;
|
||||
ESP_LOGW(TAG, "config trial pending: %lu s to associate or revert",
|
||||
(unsigned long)deadline);
|
||||
|
||||
const esp_timer_create_args_t args = {
|
||||
.callback = trial_revert,
|
||||
.name = "cfg_trial",
|
||||
};
|
||||
if (esp_timer_create(&args, &s_revert_timer) == ESP_OK) {
|
||||
esp_timer_start_once(s_revert_timer, (uint64_t)deadline * 1000000ULL);
|
||||
} else {
|
||||
ESP_LOGE(TAG, "could not arm revert timer; trial cannot self-heal");
|
||||
}
|
||||
}
|
||||
|
||||
void config_trial_notify_connected(void)
|
||||
{
|
||||
if (!s_trial_armed) return;
|
||||
s_trial_armed = false;
|
||||
|
||||
if (s_revert_timer) {
|
||||
esp_timer_stop(s_revert_timer);
|
||||
esp_timer_delete(s_revert_timer);
|
||||
s_revert_timer = NULL;
|
||||
}
|
||||
|
||||
nvs_handle_t t;
|
||||
if (nvs_open(TRIAL_NS, NVS_READWRITE, &t) == ESP_OK) {
|
||||
nvs_erase_all(t);
|
||||
nvs_commit(t);
|
||||
nvs_close(t);
|
||||
}
|
||||
ESP_LOGI(TAG, "config trial COMMITTED -- association proved on new settings");
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------- handlers --- */
|
||||
|
||||
static esp_err_t recv_body(httpd_req_t *req, char **out)
|
||||
{
|
||||
if (req->content_len <= 0 || req->content_len > BODY_MAX) return ESP_FAIL;
|
||||
char *buf = malloc(req->content_len + 1);
|
||||
if (!buf) return ESP_ERR_NO_MEM;
|
||||
int got = 0;
|
||||
while (got < req->content_len) {
|
||||
int r = httpd_req_recv(req, buf + got, req->content_len - got);
|
||||
if (r <= 0) { free(buf); return ESP_FAIL; }
|
||||
got += r;
|
||||
}
|
||||
buf[got] = '\0';
|
||||
*out = buf;
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static esp_err_t config_get_handler(httpd_req_t *req)
|
||||
{
|
||||
if (!ota_auth_check(req)) {
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN,
|
||||
"Authentication required. Use: Authorization: Bearer <psk>");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
nvs_handle_t h;
|
||||
cJSON *root = cJSON_CreateObject();
|
||||
cJSON *vals = cJSON_AddObjectToObject(root, "config");
|
||||
if (nvs_open(CFG_NS, NVS_READONLY, &h) == ESP_OK) {
|
||||
for (size_t i = 0; i < NKEYS; i++) {
|
||||
cJSON_AddItemToObject(vals, KEYS[i].json, read_key(h, &KEYS[i], true));
|
||||
}
|
||||
nvs_close(h);
|
||||
}
|
||||
|
||||
/* Tell the caller which fields will force a reboot-and-prove cycle, so a
|
||||
* tool can warn before sending rather than discovering it afterwards. */
|
||||
cJSON *risky = cJSON_AddArrayToObject(root, "requires_trial");
|
||||
for (size_t i = 0; i < NKEYS; i++) {
|
||||
if (KEYS[i].assoc) cJSON_AddItemToArray(risky, cJSON_CreateString(KEYS[i].json));
|
||||
}
|
||||
cJSON_AddBoolToObject(root, "trial_pending", s_trial_armed);
|
||||
cJSON_AddNumberToObject(root, "reassoc_count", s_reassoc_count);
|
||||
cJSON_AddNumberToObject(root, "reassoc_last_s_ago",
|
||||
s_reassoc_last_us ? (double)((esp_timer_get_time() - s_reassoc_last_us) / 1000000) : -1);
|
||||
|
||||
char *out = cJSON_PrintUnformatted(root);
|
||||
cJSON_Delete(root);
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_sendstr(req, out ? out : "{}");
|
||||
free(out);
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
static void reboot_soon(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
esp_restart();
|
||||
}
|
||||
|
||||
static esp_err_t config_post_handler(httpd_req_t *req)
|
||||
{
|
||||
if (!ota_auth_check(req)) {
|
||||
ESP_LOGW(TAG, "config write rejected: authentication failed");
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN,
|
||||
"Authentication required. Use: Authorization: Bearer <psk>");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
if (s_trial_armed) {
|
||||
/* A second change stacked on an unproven one would bank the UNPROVEN
|
||||
* values as the fallback, so a revert would restore settings never
|
||||
* shown to work. Refuse until the pending trial resolves. */
|
||||
httpd_resp_set_status(req, "409 Conflict");
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_sendstr(req,
|
||||
"{\"error\":\"a config trial is still pending; "
|
||||
"wait for it to commit or revert\"}");
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
char *body = NULL;
|
||||
if (recv_body(req, &body) != ESP_OK) {
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Missing or oversized body");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
cJSON *root = cJSON_Parse(body);
|
||||
free(body);
|
||||
if (!root || !cJSON_IsObject(root)) {
|
||||
cJSON_Delete(root);
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, "Body must be a JSON object");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Pass 1: recognise every field and note whether any can orphan the node.
|
||||
* Rejecting an unknown key rather than ignoring it turns a typo into a
|
||||
* visible error instead of a setting that silently never applied. */
|
||||
bool needs_trial = false;
|
||||
bool all_live = true;
|
||||
char err[96] = {0};
|
||||
cJSON *it = NULL;
|
||||
cJSON_ArrayForEach(it, root) {
|
||||
if (is_meta(it->string)) continue;
|
||||
const cfg_key_t *k = find_key(it->string);
|
||||
if (!k) {
|
||||
snprintf(err, sizeof(err), "unknown parameter '%s'", it->string);
|
||||
break;
|
||||
}
|
||||
if (k->assoc) needs_trial = true;
|
||||
if (!is_live(k->json)) all_live = false;
|
||||
}
|
||||
if (err[0]) {
|
||||
cJSON_Delete(root);
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, err);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
/* Pass 2: bank the CURRENT values before anything is written, reading
|
||||
* through a separate read-only handle so pending writes cannot leak in. */
|
||||
cJSON *bank = NULL;
|
||||
if (needs_trial) {
|
||||
nvs_handle_t ro;
|
||||
bank = cJSON_CreateObject();
|
||||
if (nvs_open(CFG_NS, NVS_READONLY, &ro) == ESP_OK) {
|
||||
cJSON_ArrayForEach(it, root) {
|
||||
if (is_meta(it->string)) continue;
|
||||
const cfg_key_t *k = find_key(it->string);
|
||||
cJSON_AddItemToObject(bank, k->json, read_key(ro, k, false));
|
||||
}
|
||||
nvs_close(ro);
|
||||
}
|
||||
}
|
||||
|
||||
/* Pass 3: stage every write. Nothing is committed yet, so a single bad
|
||||
* value leaves the node exactly as it was. */
|
||||
nvs_handle_t rw;
|
||||
if (nvs_open(CFG_NS, NVS_READWRITE, &rw) != ESP_OK) {
|
||||
cJSON_Delete(root);
|
||||
cJSON_Delete(bank);
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "NVS unavailable");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
int changed = 0;
|
||||
cJSON_ArrayForEach(it, root) {
|
||||
if (is_meta(it->string)) continue;
|
||||
const cfg_key_t *k = find_key(it->string);
|
||||
if (!write_key(rw, k, it, err, sizeof(err))) {
|
||||
if (!err[0]) snprintf(err, sizeof(err), "could not write %s", k->json);
|
||||
break;
|
||||
}
|
||||
changed++;
|
||||
}
|
||||
if (err[0]) {
|
||||
nvs_close(rw); /* uncommitted writes are discarded */
|
||||
cJSON_Delete(root);
|
||||
cJSON_Delete(bank);
|
||||
httpd_resp_send_err(req, HTTPD_400_BAD_REQUEST, err);
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
cJSON *rb = cJSON_GetObjectItem(root, "reboot");
|
||||
bool reboot = cJSON_IsBool(rb) ? cJSON_IsTrue(rb) : !all_live;
|
||||
|
||||
/* Pass 4: arm the trial BEFORE committing the new config. If the bank
|
||||
* cannot be stored we must not proceed -- committing first would reboot
|
||||
* the node onto unproven settings with nothing to fall back to. */
|
||||
uint32_t deadline = TRIAL_DEFAULT;
|
||||
if (needs_trial) {
|
||||
cJSON *d = cJSON_GetObjectItem(root, "trial_seconds");
|
||||
if (cJSON_IsNumber(d) && d->valuedouble >= 30 && d->valuedouble <= TRIAL_MAX) {
|
||||
deadline = (uint32_t)d->valuedouble;
|
||||
}
|
||||
char *bj = cJSON_PrintUnformatted(bank);
|
||||
nvs_handle_t t;
|
||||
bool ok = bj && nvs_open(TRIAL_NS, NVS_READWRITE, &t) == ESP_OK;
|
||||
if (ok) {
|
||||
ok = nvs_set_str(t, "bank", bj) == ESP_OK &&
|
||||
nvs_set_u32(t, "deadline", deadline) == ESP_OK &&
|
||||
nvs_set_u8(t, "armed", 1) == ESP_OK &&
|
||||
nvs_commit(t) == ESP_OK;
|
||||
nvs_close(t);
|
||||
}
|
||||
free(bj);
|
||||
if (!ok) {
|
||||
nvs_close(rw);
|
||||
cJSON_Delete(root);
|
||||
cJSON_Delete(bank);
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR,
|
||||
"Could not bank the previous config; refusing to apply");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
}
|
||||
|
||||
esp_err_t ce = nvs_commit(rw);
|
||||
nvs_close(rw);
|
||||
|
||||
if (ce == ESP_OK) {
|
||||
/* Push live parameters into the running config so they apply now.
|
||||
* Values were range-checked in pass 3, so these are already valid. */
|
||||
cJSON *lm = cJSON_GetObjectItem(root, "led_mode");
|
||||
if (cJSON_IsNumber(lm)) g_nvs_config.led_mode = (uint8_t)lm->valuedouble;
|
||||
cJSON *lb = cJSON_GetObjectItem(root, "led_brightness");
|
||||
if (cJSON_IsNumber(lb)) g_nvs_config.led_brightness = (uint8_t)lb->valuedouble;
|
||||
}
|
||||
|
||||
cJSON_Delete(root);
|
||||
cJSON_Delete(bank);
|
||||
if (ce != ESP_OK) {
|
||||
httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "Commit failed");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
|
||||
cJSON *resp = cJSON_CreateObject();
|
||||
cJSON_AddNumberToObject(resp, "changed", changed);
|
||||
cJSON_AddBoolToObject(resp, "trial", needs_trial);
|
||||
cJSON_AddBoolToObject(resp, "rebooting", needs_trial || reboot);
|
||||
if (needs_trial) {
|
||||
cJSON_AddNumberToObject(resp, "trial_seconds", deadline);
|
||||
cJSON_AddStringToObject(resp, "note",
|
||||
"rebooting; reverts automatically unless the node re-associates");
|
||||
} else if (reboot) {
|
||||
cJSON_AddStringToObject(resp, "note", "written; rebooting to apply");
|
||||
} else if (all_live) {
|
||||
cJSON_AddStringToObject(resp, "note", "applied immediately; no restart needed");
|
||||
} else {
|
||||
cJSON_AddStringToObject(resp, "note",
|
||||
"written but NOT yet in effect; config is read only at boot");
|
||||
}
|
||||
char *out = cJSON_PrintUnformatted(resp);
|
||||
cJSON_Delete(resp);
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
httpd_resp_sendstr(req, out ? out : "{}");
|
||||
free(out);
|
||||
|
||||
if (needs_trial || reboot) {
|
||||
if (needs_trial) {
|
||||
ESP_LOGW(TAG, "config trial armed (%lu s); rebooting",
|
||||
(unsigned long)deadline);
|
||||
} else {
|
||||
ESP_LOGI(TAG, "config written (%d field(s)); rebooting to apply", changed);
|
||||
}
|
||||
/* Deferred so the response actually reaches the caller -- otherwise
|
||||
* the tool sees a dropped connection and cannot tell success from a
|
||||
* crash at the worst possible moment.
|
||||
*
|
||||
* 3 s, not 1 s: at 1 s, two of three trial pushes to a node under
|
||||
* sendto-ENOMEM congestion returned an empty body. The write had
|
||||
* succeeded and the node rebooted correctly, but the caller could not
|
||||
* tell that from a failure -- and the natural response to an apparent
|
||||
* failure is to push again, which is the worst thing to do to a node
|
||||
* that is mid-trial. */
|
||||
const esp_timer_create_args_t a = { .callback = reboot_soon, .name = "cfg_boot" };
|
||||
esp_timer_handle_t th;
|
||||
if (esp_timer_create(&a, &th) == ESP_OK) esp_timer_start_once(th, 3000000ULL);
|
||||
}
|
||||
return ESP_OK;
|
||||
}
|
||||
|
||||
/* POST /link/reassoc: drop and re-join the AP so rate control starts fresh.
|
||||
* Measured on a 9-node ESP32-C6 fleet: a node whose uplink PHY rate falls to
|
||||
* 1-2 Mbps stays there for 24-425 min without help, and that one slow node
|
||||
* fills the channel for everyone. A fresh association restored the rate every
|
||||
* time (15 of 15 requests that reached the node, 10-09..10-16). Detection is
|
||||
* left to the caller (e.g. a service watching the AP's per-client rx rate);
|
||||
* the 60 s floor here only stops a runaway caller. The reconnect is the
|
||||
* normal STA_DISCONNECTED path in main.c, with its usual backoff. */
|
||||
static void reassoc_timer_cb(void *arg)
|
||||
{
|
||||
(void)arg;
|
||||
esp_err_t r = esp_wifi_disconnect();
|
||||
ESP_LOGW(TAG, "link reassoc #%lu: esp_wifi_disconnect -> %s",
|
||||
(unsigned long)s_reassoc_count, esp_err_to_name(r));
|
||||
}
|
||||
|
||||
static esp_err_t link_reassoc_handler(httpd_req_t *req)
|
||||
{
|
||||
if (!ota_auth_check(req)) {
|
||||
httpd_resp_send_err(req, HTTPD_403_FORBIDDEN,
|
||||
"Authentication required. Use: Authorization: Bearer <psk>");
|
||||
return ESP_FAIL;
|
||||
}
|
||||
int64_t now = esp_timer_get_time();
|
||||
httpd_resp_set_type(req, "application/json");
|
||||
if (s_reassoc_last_us != 0 && now - s_reassoc_last_us < REASSOC_MIN_INTERVAL_US) {
|
||||
httpd_resp_set_status(req, "429 Too Many Requests");
|
||||
return httpd_resp_sendstr(req, "{\"error\":\"reassoc requested < 60 s ago\"}");
|
||||
}
|
||||
if (s_reassoc_timer == NULL) {
|
||||
const esp_timer_create_args_t args = { .callback = reassoc_timer_cb, .name = "reassoc" };
|
||||
if (esp_timer_create(&args, &s_reassoc_timer) != ESP_OK) {
|
||||
return httpd_resp_send_err(req, HTTPD_500_INTERNAL_SERVER_ERROR, "timer create failed");
|
||||
}
|
||||
}
|
||||
s_reassoc_count++;
|
||||
s_reassoc_last_us = now;
|
||||
/* Reply first; drop the link 200 ms later so the response gets out. */
|
||||
esp_err_t sent = httpd_resp_sendstr(req, "{\"status\":\"reassociating\"}");
|
||||
esp_timer_stop(s_reassoc_timer);
|
||||
esp_timer_start_once(s_reassoc_timer, 200 * 1000);
|
||||
return sent;
|
||||
}
|
||||
|
||||
esp_err_t config_api_register(httpd_handle_t server)
|
||||
{
|
||||
static const httpd_uri_t get_uri = {
|
||||
.uri = "/config", .method = HTTP_GET, .handler = config_get_handler,
|
||||
};
|
||||
static const httpd_uri_t post_uri = {
|
||||
.uri = "/config", .method = HTTP_POST, .handler = config_post_handler,
|
||||
};
|
||||
static const httpd_uri_t reassoc_uri = {
|
||||
.uri = "/link/reassoc", .method = HTTP_POST, .handler = link_reassoc_handler,
|
||||
};
|
||||
esp_err_t e = httpd_register_uri_handler(server, &get_uri);
|
||||
if (e != ESP_OK) return e;
|
||||
e = httpd_register_uri_handler(server, &post_uri);
|
||||
if (e != ESP_OK) return e;
|
||||
return httpd_register_uri_handler(server, &reassoc_uri);
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
/**
|
||||
* @file config_api.h
|
||||
* @brief Remote configuration over HTTP -- change node settings without USB.
|
||||
*
|
||||
* Deliberately NOT called "OTA". OTA in this firmware means a firmware image
|
||||
* written to an app partition by ota_update.c. This is provisioning: the same
|
||||
* parameters provision.py writes over USB, writable over the network instead,
|
||||
* on the same authenticated server and behind the same PSK.
|
||||
*
|
||||
* The parameters split by whether a bad value can ORPHAN a node:
|
||||
*
|
||||
* recoverable A wrong target_ip sends CSI nowhere, but the node keeps its
|
||||
* association and stays reachable on :8032, so the next config
|
||||
* push fixes it. Applied immediately.
|
||||
*
|
||||
* orphaning A wrong ssid, password or channel means the node never
|
||||
* rejoins the network and no push can ever reach it again --
|
||||
* strictly worse than a bad OTA, which at least has a second
|
||||
* partition to fall back to. These go through a trial: the old
|
||||
* values are banked, the new ones are written, and the node
|
||||
* reboots. It commits only on proving it can still associate;
|
||||
* otherwise it restores the bank and reboots itself.
|
||||
*
|
||||
* That trial is the app-level equivalent of bootloader rollback, and unlike
|
||||
* bootloader rollback it can itself be delivered by OTA.
|
||||
*/
|
||||
#pragma once
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_http_server.h"
|
||||
|
||||
/** Register /config and /config/trial on an already-running server. */
|
||||
esp_err_t config_api_register(httpd_handle_t server);
|
||||
|
||||
/**
|
||||
* Arm the revert timer if a config trial is pending.
|
||||
* Call early in app_main(), before WiFi starts.
|
||||
*/
|
||||
void config_trial_boot_check(void);
|
||||
|
||||
/**
|
||||
* Report a successful association, committing any pending trial.
|
||||
* Call from the IP_EVENT_STA_GOT_IP handler.
|
||||
*/
|
||||
void config_trial_notify_connected(void);
|
||||
@@ -1062,7 +1062,11 @@ static void send_feature_vector(void)
|
||||
static void process_frame(const edge_ring_slot_t *slot)
|
||||
{
|
||||
uint16_t n_subcarriers = slot->iq_len / 2;
|
||||
if (n_subcarriers == 0 || n_subcarriers > EDGE_MAX_SUBCARRIERS) return;
|
||||
if (n_subcarriers == 0) return;
|
||||
/* STBC frames carry LLTF + HT-LTF + a second STBC HT-LTF (192 entries on
|
||||
* S3). The first 128 line up with plain HT20 frames; analyse those instead
|
||||
* of dropping the frame (#2286). */
|
||||
if (n_subcarriers > EDGE_MAX_SUBCARRIERS) n_subcarriers = EDGE_MAX_SUBCARRIERS;
|
||||
|
||||
s_frame_count++;
|
||||
s_latest_rssi = slot->rssi;
|
||||
|
||||
@@ -12,3 +12,14 @@ dependencies:
|
||||
## Onboard WS2812 LED Disabling
|
||||
# 3.1.0 needs the IDF >= 5.5 API rmt_tx_switch_gpio; stay on 3.0.x for IDF 5.4
|
||||
espressif/led_strip: "~3.0.3"
|
||||
|
||||
## cJSON for config_api.c.
|
||||
## Bundled as the `json` component up to ESP-IDF 5.x and moved out to the
|
||||
## component manager in v6, so it is pulled ONLY on the v6+ lane -- on 5.4
|
||||
## `main/CMakeLists.txt` requires the built-in `json` instead. Taking it from
|
||||
## the registry on 5.x as well would mean two cJSON implementations in one
|
||||
## build.
|
||||
espressif/cjson:
|
||||
version: "^1.7.19"
|
||||
rules:
|
||||
- if: "idf_version >=6.0"
|
||||
@@ -25,6 +25,7 @@
|
||||
#include "thermal.h"
|
||||
#include "stream_sender.h"
|
||||
#include "nvs_config.h"
|
||||
#include "config_api.h"
|
||||
#include "serial_onboarding.h"
|
||||
#include "edge_processing.h"
|
||||
#include "ota_update.h"
|
||||
@@ -145,6 +146,8 @@ static void event_handler(void *arg, esp_event_base_t event_base,
|
||||
s_retry_num = 0;
|
||||
ota_health_note_got_ip();
|
||||
xEventGroupSetBits(s_wifi_event_group, WIFI_CONNECTED_BIT);
|
||||
/* Proof that a pending config trial produced a usable network. */
|
||||
config_trial_notify_connected();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -294,8 +297,31 @@ static led_strip_handle_t s_viz_led;
|
||||
static void led_gamma_40hz_cb(void *arg)
|
||||
{
|
||||
static bool on = false;
|
||||
static uint8_t last_mode = 0xFF;
|
||||
|
||||
/* Read live rather than latching at boot, so turning the LED down from
|
||||
* config_push.py takes effect immediately -- the point of the knob is
|
||||
* being able to darken a bedroom without waiting for a reboot. */
|
||||
uint8_t mode = g_nvs_config.led_mode;
|
||||
uint8_t bright = g_nvs_config.led_brightness;
|
||||
if (bright > 100) bright = 100;
|
||||
|
||||
if (mode == LED_MODE_OFF) {
|
||||
/* Blank once on the transition, then leave the RMT idle instead of
|
||||
* clocking out zeros eighty times a second for the node's lifetime. */
|
||||
if (last_mode != LED_MODE_OFF) {
|
||||
led_strip_set_pixel(s_viz_led, 0, 0, 0, 0);
|
||||
led_strip_refresh(s_viz_led);
|
||||
last_mode = mode;
|
||||
}
|
||||
return;
|
||||
}
|
||||
last_mode = mode;
|
||||
|
||||
on = !on;
|
||||
if (on) {
|
||||
/* STEADY keeps the motion colour but drops the square wave: same
|
||||
* information, no 40 Hz strobe. */
|
||||
if (on || mode == LED_MODE_STEADY) {
|
||||
edge_vitals_pkt_t v;
|
||||
float m = edge_get_vitals(&v) ? v.motion_energy : 0.0f;
|
||||
float norm = m / LED_MOTION_FULLSCALE;
|
||||
@@ -303,7 +329,10 @@ static void led_gamma_40hz_cb(void *arg)
|
||||
if (norm > 1.0f) norm = 1.0f;
|
||||
int idx = (int)(norm * 59.0f + 0.5f);
|
||||
const uint8_t *c = VIRIDIS_LUT[idx];
|
||||
led_strip_set_pixel(s_viz_led, 0, c[0], c[1], c[2]); /* R,G,B (driver maps to GRB) */
|
||||
led_strip_set_pixel(s_viz_led, 0, /* R,G,B (driver maps to GRB) */
|
||||
(uint32_t)c[0] * bright / 100,
|
||||
(uint32_t)c[1] * bright / 100,
|
||||
(uint32_t)c[2] * bright / 100);
|
||||
} else {
|
||||
led_strip_set_pixel(s_viz_led, 0, 0, 0, 0); /* off phase */
|
||||
}
|
||||
@@ -322,6 +351,10 @@ void app_main(void)
|
||||
ESP_ERROR_CHECK(ret);
|
||||
|
||||
/* Load runtime config (NVS overrides Kconfig defaults) */
|
||||
/* Before loading config: if the last change is still on trial this arms
|
||||
* the revert timer, so settings that cannot associate undo themselves. */
|
||||
config_trial_boot_check();
|
||||
|
||||
nvs_config_load(&g_nvs_config);
|
||||
|
||||
/* Capture node_id IMMEDIATELY — before wifi_init_sta() can corrupt
|
||||
|
||||
@@ -23,6 +23,9 @@ void nvs_config_load(nvs_config_t *cfg)
|
||||
return;
|
||||
}
|
||||
|
||||
cfg->led_mode = LED_MODE_FLICKER;
|
||||
cfg->led_brightness = 100;
|
||||
|
||||
/* Start with Kconfig compiled defaults */
|
||||
strlcpy(cfg->wifi_ssid, CONFIG_CSI_WIFI_SSID, sizeof(cfg->wifi_ssid));
|
||||
|
||||
@@ -313,6 +316,18 @@ void nvs_config_load(nvs_config_t *cfg)
|
||||
if (nvs_get_u16(handle, "swarm_hb", &cfg->swarm_heartbeat_sec) != ESP_OK) {
|
||||
cfg->swarm_heartbeat_sec = 30;
|
||||
}
|
||||
uint8_t led_mode_val = 0;
|
||||
if (nvs_get_u8(handle, "led_mode", &led_mode_val) == ESP_OK && led_mode_val <= 2) {
|
||||
cfg->led_mode = led_mode_val;
|
||||
ESP_LOGI(TAG, "NVS override: led_mode=%u", led_mode_val);
|
||||
}
|
||||
|
||||
uint8_t led_bright_val = 0;
|
||||
if (nvs_get_u8(handle, "led_bright", &led_bright_val) == ESP_OK && led_bright_val <= 100) {
|
||||
cfg->led_brightness = led_bright_val;
|
||||
ESP_LOGI(TAG, "NVS override: led_brightness=%u", led_bright_val);
|
||||
}
|
||||
|
||||
if (nvs_get_u16(handle, "swarm_ingest", &cfg->swarm_ingest_sec) != ESP_OK) {
|
||||
cfg->swarm_ingest_sec = 5;
|
||||
}
|
||||
|
||||
@@ -62,8 +62,21 @@ typedef struct {
|
||||
char zone_name[16]; /**< Zone name for this node (e.g. "lobby"). */
|
||||
uint16_t swarm_heartbeat_sec; /**< Heartbeat interval (seconds, default 30). */
|
||||
uint16_t swarm_ingest_sec; /**< Vector ingest interval (seconds, default 5). */
|
||||
|
||||
/* Onboard WS2812 "viz" LED. The default is a 40 Hz square-wave gamma
|
||||
* stimulus at full LUT brightness, which is fine on a bench and hostile
|
||||
* in a bedroom -- hence runtime selectable rather than Kconfig-only. */
|
||||
uint8_t led_mode; /**< 0=off, 1=steady colour, 2=40Hz flicker. */
|
||||
uint8_t led_brightness; /**< Percent, 0-100, scales the colormap. */
|
||||
} nvs_config_t;
|
||||
|
||||
#define LED_MODE_OFF 0
|
||||
#define LED_MODE_STEADY 1
|
||||
#define LED_MODE_FLICKER 2
|
||||
|
||||
/** Live configuration, loaded once at boot by app_main(). */
|
||||
extern nvs_config_t g_nvs_config;
|
||||
|
||||
/**
|
||||
* Load configuration from NVS, falling back to Kconfig defaults.
|
||||
*
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
*/
|
||||
|
||||
#include "ota_update.h"
|
||||
#include "config_api.h"
|
||||
|
||||
#include <string.h>
|
||||
#include "esp_log.h"
|
||||
@@ -81,6 +82,11 @@ static bool ota_check_auth(httpd_req_t *req)
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
bool ota_auth_check(httpd_req_t *req)
|
||||
{
|
||||
return ota_check_auth(req);
|
||||
}
|
||||
|
||||
/**
|
||||
* GET /ota/status — return firmware version and partition info.
|
||||
*/
|
||||
@@ -265,7 +271,7 @@ static esp_err_t ota_start_server(httpd_handle_t *out_handle)
|
||||
* httpd", RTC_SW_CPU_RST), so no OTA had ever completed on this firmware.
|
||||
* See CONFIG_OTA_HTTPD_STACK_SIZE for the sizing. */
|
||||
config.stack_size = CONFIG_OTA_HTTPD_STACK_SIZE;
|
||||
config.max_uri_handlers = 12; /* Extra slots for WASM endpoints (ADR-040). */
|
||||
config.max_uri_handlers = 14; /* WASM endpoints (ADR-040) + /config. */
|
||||
/* Increase receive timeout for large uploads. */
|
||||
config.recv_wait_timeout = 30;
|
||||
|
||||
@@ -294,6 +300,12 @@ static esp_err_t ota_start_server(httpd_handle_t *out_handle)
|
||||
};
|
||||
httpd_register_uri_handler(server, &upload_uri);
|
||||
|
||||
/* Remote configuration shares this server so it inherits the OTA PSK,
|
||||
* the port, and the same fail-closed auth path (config_api.c). */
|
||||
if (config_api_register(server) != ESP_OK) {
|
||||
ESP_LOGW(TAG, "remote config endpoints unavailable");
|
||||
}
|
||||
|
||||
ESP_LOGI(TAG, "OTA HTTP server started on port %d", OTA_PORT);
|
||||
ESP_LOGI(TAG, " GET /ota/status — firmware version info");
|
||||
ESP_LOGI(TAG, " POST /ota — upload new firmware binary");
|
||||
|
||||
@@ -9,7 +9,10 @@
|
||||
#ifndef OTA_UPDATE_H
|
||||
#define OTA_UPDATE_H
|
||||
|
||||
#include <stdbool.h>
|
||||
|
||||
#include "esp_err.h"
|
||||
#include "esp_http_server.h" /* httpd_req_t, for ota_auth_check() */
|
||||
|
||||
/**
|
||||
* Initialize the OTA update HTTP server.
|
||||
@@ -30,6 +33,16 @@ esp_err_t ota_update_init(void);
|
||||
*/
|
||||
esp_err_t ota_update_init_ex(void **out_server);
|
||||
|
||||
/**
|
||||
* Validate the Authorization: Bearer <psk> header against the provisioned OTA
|
||||
* PSK, in constant time. Fails closed when no PSK is provisioned.
|
||||
*
|
||||
* Shared with config_api.c so remote configuration is gated by exactly the
|
||||
* same secret and the same comparison as firmware upload -- a second
|
||||
* implementation would be a second place for the check to rot.
|
||||
*/
|
||||
bool ota_auth_check(httpd_req_t *req);
|
||||
|
||||
/**
|
||||
* Log the httpd task's stack high-water mark (bytes never used). Call from an
|
||||
* HTTP handler on the OTA server after its deepest work, so the configured
|
||||
|
||||