Compare commits

...
Author SHA1 Message Date
ruv bf715c9cf0 fix(provision): avoid backslash in f-string expression (SyntaxError on Python <3.12)
Co-Authored-By: claude-flow <ruv@ruv.net>
2026-10-17 10:51:08 -04:00
ruv bf46d113f3 fix(firmware): analyse first 128 entries of STBC CSI frames instead of dropping them (#2286)
Reporter MEASURED on ESP32-S3: vitals/feature packets 2 -> 59 per 60 s.
Not reproducible on C6 (EDGE_MAX_SUBCARRIERS=256); C6 build verified only.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-10-17 09:16:41 -04:00
ruv fde7514128 Merge branch 'pr-2267' into integrate/release-20261017 2026-10-17 08:58:35 -04:00
ruv 1e3ffc1bce Merge branch 'pr-2278' into integrate/release-20261017 2026-10-17 08:58:35 -04:00
ruv 001f36853a Merge branch 'pr-2280' into integrate/release-20261017 2026-10-17 08:58:34 -04:00
ruv add14700f6 Merge branch 'pr-2281' into integrate/release-20261017 2026-10-17 08:58:34 -04:00
ruv 8e446ce5ca chore: drop contributor-branch-only workflow from #2269 (UI fix kept)
Co-Authored-By: claude-flow <ruv@ruv.net>
2026-10-17 08:58:33 -04:00
ruv 12fdb007f6 Merge branch 'pr-2269' into integrate/release-20261017 2026-10-17 08:58:20 -04:00
ruv 802c92f929 Merge branch 'pr-2261' into integrate/release-20261017 2026-10-17 08:58:20 -04:00
ruv e2dac179e6 Merge branch 'pr-2263' into integrate/release-20261017 2026-10-17 08:58:19 -04:00
ruv a9f6ec96b5 Merge branch 'pr-2262' into integrate/release-20261017 2026-10-17 08:58:19 -04:00
ruv 03ac12dfe4 Merge branch 'pr-2283' into integrate/release-20261017 2026-10-17 08:58:18 -04:00
github-actions[bot] 9b8fe5c3a1 chore: update vendor submodules to latest upstream 2026-10-17 12:18:34 +00:00
Brian Richterandclaude-flow ff284f77fb fix(dashboard): exclude Playwright specs from Vitest
npm test (vitest run) collected tests/a11y.spec.ts, a Playwright spec,
and failed. Exclude tests/** from Vitest and pass when no unit tests
exist; Playwright specs still run via npm run test:e2e / test:a11y.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-10-16 17:45:38 -07:00
Dan Alec d45a04381d fix(ci): make the container image pipelines version-safe and fork-safe
nvsim-server -> ghcr.io was failing for three separate reasons, each one
masked by the previous. The Dockerfile carried two literals that had drifted
from their source of truth, and both workflows hard-coded the `ruvnet`
registry namespace that only the upstream can write to.

nvsim-server/Dockerfile

  1. Stub workspace version. The Dockerfile builds a throwaway workspace
     containing only nvsim and nvsim-server. That stub hard-coded
     version = "0.3.0" (PR #440); the real workspace is now 0.3.1 (PR
     #1439) and nvsim-server requires nvsim = { path = "../nvsim",
     version = "0.3.1" }. The build died with "failed to select a version
     for the requirement nvsim". The stub now extracts the version from
     the real Cargo.toml and fails loudly if it cannot read one.

  2. Builder toolchain. FROM was pinned to rust:1.81 while
     rust-toolchain.toml declares channel = "1.89", so async-compression
     0.4.42 and compression-codecs 0.4.38 (both MSRV 1.83, reached through
     tower-http's compression-gzip feature) aborted the release build with
     "rustc 1.81.0 is not supported". The builder is now 1.89.

     Rather than re-pinning another literal that will drift again, the
     declared channel travels into the build context and is compared
     against the effective rustc before compiling. Bumping one without the
     other now fails the build loudly instead of resurfacing as an MSRV
     error three steps later. It is copied under a separate name on
     purpose: letting rustup resolve it would silently download a
     toolchain and hide the drift.

nvsim-server-docker.yml

  The image name was hard-coded to ghcr.io/ruvnet/nvsim-server, so the push
  failed with `denied: The requested installation does not exist` outside
  the upstream namespace. It now derives from github.repository_owner,
  which resolves to ruvnet for the upstream and to the fork owner for
  every fork, so the workflow keeps working unchanged here.

  The smoke test also pulled :sha-<short> but ran :latest. `latest` is
  emitted with `enable={{is_default_branch}}`, so a tag build never had
  it, and the `|| latest` fallback masked a failed push by testing a stale
  image. It now pulls and runs the sha tag, which type=sha emits on every
  event.

sensing-server-docker.yml

  Same namespace problem: the image was fixed to ruvnet/wifi-densepose on
  both Docker Hub and ghcr.io, which a fork cannot push to. Publishing
  under github.repository_owner fixes that.

  docker login was also unconditional, so a repository without
  DOCKERHUB_USERNAME/DOCKERHUB_TOKEN died at "Must provide --username with
  --password-stdin" before building anything. ghcr.io needs no secret, so
  it stays the always-on target and Docker Hub is added only when both
  credentials are present.

Verified: cargo check -p nvsim-server --release under rustc 1.89.0
compiles nvsim v0.3.1, nvsim-server v0.3.1 and async-compression 0.4.42;
the resulting image builds, pushes and its /api/health reports
{"nvsim_version":"0.3.1"}. CI on this branch runs the full pipeline
green.
2026-10-16 16:06:56 -03:00
Dan Alec 8e846a9a36 fix: security floors, dashboard a11y violations, README visual assets
Three independent fixes that each resolve a red job on main.

Security floors (v2/Cargo.lock, v2/Cargo.toml)
  cargo audit reported 10 advisories across three transitive crates:
  quinn-proto 0.11.15, rustls 0.23.45 and wasmtime 36.0.16. Bump to
  0.11.18 / 0.23.46 / 36.0.18. All three arrive through
  midstreamer-quic = 0.3, so only the lockfile enforces them; v2/Cargo.toml
  now records the floors so a plain `cargo update` cannot walk them back
  without explanation.

Dashboard accessibility
  18 axe failures (3 browsers x 6 views): 58 role=switch toggles with no
  accessible name, two unlabelled range inputs, low-contrast timestamp /
  chip / badge text, and two scrollable regions with no keyboard access.

  The welcome modal also left the accessibility tree only by fading to
  opacity 0. A closed state at opacity 0 still gets scanned, and axe
  composes the semi-transparent text over the darkened backdrop, which
  produced a false color-contrast failure (ratio 1.55 reported where the
  real ratio was ~17:1). It now leaves via visibility, and
  connectedCallback exposes a `ready` attribute once its async storage read
  settles so the smoke test stops sampling the modal mid-transition.

README visual asset validation
  The workflow regenerates the 20 SVGs and requires byte-identical output.
  scripts/render-readme-visuals.py stripped unused CSS rules but left the
  trailing newline behind, so 14 removed rules collapsed into 16 blank
  lines. The producer now consumes the orphan newline and the assets are
  regenerated (94,576 -> 94,027 bytes).

Verified locally: cargo audit 0.22.2 exits 0, cargo check --workspace
--no-default-features --all-targets is clean, the README renderer is
idempotent with verify passing, and the axe suite is 18/18 across chromium,
firefox and webkit on six consecutive runs.
2026-10-16 16:06:38 -03:00
Joe 6dfd19ea28 Merge contrib/remote-config into contrib/link-reassoc 2026-10-16 11:34:00 -04:00
Joe 852f6fc2e9 Merge upstream main into contrib/remote-config 2026-10-16 11:30:57 -04:00
JoeandClaude Opus 5.5 13d62b98c0 feat(firmware): POST /link/reassoc -- rejoin the AP when the uplink rate collapses
An ESP32-C6 node whose uplink PHY rate drops to 1-2 Mbps stays there for
24-425 min without help, and that one slow node fills the channel for every
node on it. A fresh association restarts rate control and restores the rate.

POST /link/reassoc (same PSK auth as /config) replies, then drops the link
200 ms later; the existing STA_DISCONNECTED path reconnects with its usual
backoff. A 60 s floor returns 429 to a runaway caller. GET /config reports
reassoc_count and reassoc_last_s_ago. Detection stays with the caller.

Measured on a 9-node C6 fleet, 10-09..10-16: 15 of 15 requests that reached
the node restored its rate within 3 minutes.

Stacked on #1827 (remote configuration).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-16 09:34:45 -04:00
Kolomiets94 07af33a070 ci: validate desktop React UI accessibility change 2026-10-14 10:23:26 -07:00
Kolomiets94 50395d69d6 fix(ui): make interactive node cards keyboard accessible 2026-10-14 10:18:57 -07:00
rUv 285aea8f2d feat(sensing): add W1700K advisory RF diagnostics
Source tree verified against ruOS tested commit fa6b1bdf92e7959b8500d42e2a5c620e207e81b0. Synthetic pipeline validation only; hardware validation pending.
2026-10-14 04:13:50 -07:00
VG31OP 39e2e6ab40 test(scripts): add unit test suite and configurable manifest/root support for fix-marker regression checker 2026-10-13 19:21:09 +05:30
VG31OP 9d7db3290a fix(cli): execute jest javascript entrypoint directly and add config unit tests 2026-10-13 18:25:25 +05:30
VG31OP ddbe5645e9 fix(firmware): sanitize Windows reserved device names in state paths
On Windows, files whose basenames match DOS reserved device names (CON, PRN, AUX, NUL, COM1-9, LPT1-9) cannot be created, causing provision.py to fail with FileNotFoundError whenever Windows COM ports (e.g. COM7) were used. This also caused 16 test failures in the esp32-csi-node test suite on Windows.

- Prefix reserved device names with '_' in _state_path_for.
- Add legacy unprefixed path fallback in load_state and load_board_state for backward compatibility.
- Add regression tests in test_provision_state.py for reserved device prefixing.
- Add harness/homecore/** text=auto eol=lf in .gitattributes to maintain byte-for-byte provenance and prevent manifest.sha256 mismatch on Windows checkouts.

Validation: All 73 esp32-csi-node tests pass on Windows, and all 120 harness/ruview security tests, package manifest, and brain verification pass.
2026-10-13 17:57:38 +05:30
rUv a1ca023ee8 Animate anatomical pose with opposing gait and perspective depth 2026-10-12 21:04:27 -07:00
rUv 2b3138d9d0 Replace README stick figure with shaded anatomical SVG human 2026-10-12 20:58:45 -07:00
rUv 1c678a71fa Improve README spatial visuals with distinct sensing motion and typography 2026-10-12 20:52:23 -07:00
rUv 6699a2fc44 Match README spatial SVGs to RuView header palette and add RF wave motion 2026-10-12 20:33:57 -07:00
rUv 964377765a Improve README SVG spatial motion and typography (#2257) 2026-10-12 20:28:12 -07:00
rUv 5cfbb8d98d docs: restore original GIF at top of README (#2252) 2026-10-12 14:30:42 -07:00
Joe 7633794d41 Merge upstream main into contrib/remote-config (led_strip 3.0.x pin)
# Conflicts:
#	firmware/esp32-csi-node/main/idf_component.yml
2026-10-10 17:09:33 -04:00
JoeandClaude Opus 5.5 01c16ccab3 Merge origin/main into contrib/remote-config
Resolved conflicts keeping both sides:
- README.md: upstream's provision.py password-file paragraph stays under
  Provisioning Script; the Remote Configuration section follows it.
- ota_update.c: upstream's httpd stack_size (CONFIG_OTA_HTTPD_STACK_SIZE)
  and its comment kept; max_uri_handlers stays at 14 for /config.
- ota_update.h: ota_auth_check() (config_api) and upstream's
  ota_update_log_httpd_stack() both declared.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-09 12:10:20 -04:00
Joeandclaude-flow da77161fcc Merge origin/main: pick up dbaa3c7f's firmware size-gate raise to 1200 KiB
Unblocks the "Verify binary size budget" step of Build firmware
(esp32s3 / 8mb) -- this branch's head already carries the image growth
from 130afaba but was still checked against the old 1152 KiB limit.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-09-19 00:54:25 -04:00
Joe 95804695fe Merge remote-tracking branch 'origin/main' into contrib/remote-config
# Conflicts:
#	firmware/esp32-csi-node/main/main.c
2026-09-13 22:02:23 -04:00
Joeandclaude-flow 2ab3300fd9 fix(firmware): source cJSON from the component manager on ESP-IDF 6
cJSON ships as the bundled `json` component through 5.x and moved out to the
component manager in v6, where requiring it fails outright:

    Failed to resolve component 'json' required by component 'main': unknown name.

Requiring it unconditionally kept the 5.4 lane green while breaking the 6.0.2
lane -- one toolchain is not the build matrix. `json` is now required only when
IDF_VERSION_MAJOR < 6, and `espressif/cjson` is declared in idf_component.yml
under an `idf_version >=6.0` rule so the two never coexist in one build.

Verified building on esp32c6/16MB with ESP-IDF 5.4.

Co-Authored-By: claude-flow <ruv@ruv.net>
Claude-Session: https://claude.ai/code/session_01PVWMiHQifoYXL7uL3bphrZ
2026-09-10 14:13:46 -04:00
Joe f06c644b36 Merge remote-tracking branch 'origin/main' into contrib/remote-config
# Conflicts:
#	firmware/esp32-csi-node/main/CMakeLists.txt
2026-09-10 09:52:16 -04:00
Joeandclaude-flow cd251fafa8 feat(firmware): change node configuration over the network
Changing any node setting meant a USB cable and physical access. On a fleet
mounted on walls and ceilings that is the difference between a five-second
change and taking a board down.

Adds an authenticated HTTP config endpoint on each node. Settings are typed and
range-checked at the boundary, persisted to NVS, and applied without a reflash.
Includes LED mode and brightness: the status LED is bright enough to be
unwelcome in a bedroom, and that is a configuration question, not a reflash.

TRIAL AND REVERT. A setting that breaks connectivity would otherwise brick a
node remotely -- change the WiFi password and the node is simply gone. So a
change that could sever the uplink is applied on trial: the old values are
banked, the node reboots, and the change is confirmed only once the node
reassociates and gets an address. If it does not within the deadline, the node
restores the banked values and comes back on the old settings. Verified on
hardware with a deliberately wrong password: recovered on its own in 60.5 s.

The reply to a trial push is sent before the reboot rather than after it, so
the caller learns the trial was accepted instead of seeing a dropped
connection and having to guess.

Mutating requests require a pre-shared key checked in constant time, and the
endpoint FAILS CLOSED when no key is provisioned. The key is read from a file
path given at build time with no default, so no personal path is baked into a
published tree.

Co-Authored-By: claude-flow <ruv@ruv.net>
2026-09-04 16:17:13 -04:00
126 changed files with 4350 additions and 291 deletions

No files matched your search

+1
View File
@@ -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
+15 -5
View File
@@ -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
+44
View File
@@ -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 }}'
+41 -14
View File
@@ -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 \
+29 -31
View File
@@ -1,22 +1,7 @@
# π RuView
[![RuView: spaces become signals, with an animated conceptual RF field](assets/readme/hero.svg)](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>
[![RuView: spaces become signals, with an animated conceptual RF field](assets/readme/hero-spatial-v6.svg)](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.
[![Explore RuView sensing, models and automation](assets/readme/header-features.svg)](docs/user-guide.md)
[![Explore RuView sensing, models and automation](assets/readme/header-features-spatial-v4.svg)](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
[![RuView visual guide: header-evidence](assets/readme/header-evidence.svg)](docs/benchmarks/pose-estimation-cog.md)
[![RuView visual guide: header-evidence](assets/readme/header-evidence-spatial-v4.svg)](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
[![RuView visual guide: header-edge](assets/readme/header-edge.svg)](firmware/esp32-csi-node/README.md)
[![RuView visual guide: header-edge](assets/readme/header-edge-spatial-v4.svg)](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
[![RuView visual guide: overview](assets/readme/overview.svg)](docs/user-guide.md)
[![RuView visual guide: overview](assets/readme/overview-spatial-v4.svg)](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
[![RuView visual guide: header-learning](assets/readme/header-learning.svg)](docs/user-guide.md)
[![RuView visual guide: header-learning](assets/readme/header-learning-spatial-v4.svg)](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
[![RuView visual guide: header-docs](assets/readme/header-docs.svg)](docs/user-guide.md)
[![RuView visual guide: header-docs](assets/readme/header-docs-spatial-v4.svg)](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).
[![Cognitum One: Ambient Intelligence at the edge of the physical world](assets/readme/cognitum.svg)](https://cognitum.one)
[![Cognitum One: Ambient Intelligence at the edge of the physical world](assets/readme/cognitum-spatial-v4.svg)](https://cognitum.one)
## 📄 License
+21
View File
@@ -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

+22
View File
@@ -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

+19 -1
View File
@@ -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

+19 -1
View File
@@ -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

+19 -1
View File
@@ -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

+21
View File
@@ -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

+15
View File
@@ -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

+19 -1
View File
@@ -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

+21
View File
@@ -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

+31
View File
@@ -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

+31
View File
@@ -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

+27
View File
@@ -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

+19 -1
View File
@@ -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 &amp; 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 &amp; 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 &amp; 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 &amp; 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

+19 -1
View File
@@ -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 &amp; 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 &amp; 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 &amp; 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 &amp; 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 &amp; 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 &amp; 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 &amp; 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 &amp; 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

+19 -1
View File
@@ -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 &amp; 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 &amp; 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 &amp; 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 &amp; 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

+21
View File
@@ -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

+22
View File
@@ -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

+19 -1
View File
@@ -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

+21
View File
@@ -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

+22
View File
@@ -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

+19 -1
View File
@@ -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

+19 -1
View File
@@ -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

+19 -1
View File
@@ -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

+19 -1
View File
@@ -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

+21
View File
@@ -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

+31
View File
@@ -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

+31
View File
@@ -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

+35
View File
@@ -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

+19 -1
View File
@@ -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

+22 -22
View File
@@ -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,
+21
View File
@@ -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

+15
View File
@@ -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

+19 -1
View File
@@ -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

+21
View File
@@ -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

+15
View File
@@ -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

+19 -1
View File
@@ -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

+21
View File
@@ -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

+15
View File
@@ -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

+19 -1
View File
@@ -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

+21
View File
@@ -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

+15
View File
@@ -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

+19 -1
View File
@@ -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

+21
View File
@@ -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

+15
View File
@@ -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

+19 -1
View File
@@ -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

+21
View File
@@ -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

+15
View File
@@ -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

+19 -1
View File
@@ -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

+21
View File
@@ -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

+28
View File
@@ -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

+19 -1
View File
@@ -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

+3 -3
View File
@@ -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>
+1 -1
View File
@@ -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;
+3 -3
View File
@@ -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)
+1 -1
View File
@@ -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()
+18 -2
View File
@@ -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>
+25 -6
View File
@@ -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') {
+6
View File
@@ -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,
+40
View File
@@ -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.
+34 -2
View File
@@ -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.
+45
View File
@@ -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.
+48
View File
@@ -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;}
};
+29
View File
@@ -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});
+12
View File
@@ -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>
+46
View File
@@ -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);
+23
View File
@@ -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});
});
+1
View File
@@ -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}}
+87
View File
@@ -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
+205
View File
@@ -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())
+10 -1
View File
@@ -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")
+626
View File
@@ -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);
}
+45
View File
@@ -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"
+35 -2
View File
@@ -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
+15
View File
@@ -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;
}
+13
View File
@@ -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.
*
+13 -1
View File
@@ -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");
+13
View File
@@ -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
Loaded 100 of 126 files, more files were not shown because too many files have changed in this diff. Show more