видео-промо Simbiot Harness на hyperframes: 50 с, 7 сцен + музыка MusicGen

Лестница тактов, ES-спина с ed25519-цепочкой, куб политик 4×4×4,
каналы (TG, MCP, RAG, decision), цель AUM, установка одной командой.
Исходник — video/simbiot-promo (npx hyperframes render), результат — out/simbiot-harness-promo.mp4.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Simbiot Harness 2026-10-17 11:13:10 +00:00
parent 8bcb4b6238
commit 5471395ac1
24 changed files with 732 additions and 0 deletions

Binary file not shown.

3
video/simbiot-promo/.gitignore vendored Normal file
View file

@ -0,0 +1,3 @@
.hyperframes/
assets/bgm/*.log
bgm_status.json

View file

@ -0,0 +1,109 @@
# HyperFrames Composition Project
## Skills — USE THESE FIRST
**Always invoke the relevant skill before writing or modifying compositions.** Skills encode framework-specific patterns (e.g., `window.__timelines` registration, `data-*` attribute semantics, shader-compatible CSS rules) that are NOT in generic web docs. Skipping them produces broken compositions.
**Doing anything with HyperFrames?** Start at `/hyperframes` — it tells you what HyperFrames can do and which skill or workflow handles your intent (make a video, TTS / BGM, prep footage, author / animate, render, install blocks), confirms your brief up front (the intent layer), and routes every "make me a…" request (a video, a deck, a composition port) to the right workflow. Read it first, especially when there's no project context to orient you. The workflows it routes to:
- `/product-launch-video` — any **website** URL or brief / script → a product launch / SaaS / promo video, or a site tour / showcase featuring the site's own captured visuals.
- `/faceless-explainer` — arbitrary text (topic / article / notes), **no URL, no website capture** → 60-90s faceless explainer.
- `/embedded-captions` — an existing talking-head video (MP4) → the same footage with captions / subtitles added (rail + embed, or pure-cinematic embed); the footage itself is untouched.
- `/talking-head-recut` — an existing talking-head / interview / podcast video (MP4) → the same footage **packaged with designed graphic overlays** (kinetic titles, lower-thirds, data callouts, pull-quotes, side panels, pip) synced to the transcript; the clip plays unchanged underneath. (Plain captions/subtitles → `/embedded-captions`.)
- `/pr-to-video` — a GitHub PR (URL / `owner/repo#N` / "this PR") → 30-90s code-change explainer (changelog / feature reveal / fix / refactor).
- `/motion-graphics` — a short (typically under 10s) design-led **motion graphic**, motion-is-the-message, no narration: kinetic type, a stat / number count-up, a chart, a logo sting, a lower-third / overlay, or an animated tweet / headline / captured-page highlight; rendered to MP4 or a transparent overlay. Longer / narrated / custom → `/general-video`.
- `/music-to-video` — a **music track** (audio file, video to pull audio from, or one generated from a mood brief) → beat-synced video (lyric / slideshow / kinetic promo). Music drives pacing; user-supplied images / videos are cut onto the same beat grid.
- `/slideshow` — a **presentation / pitch deck / interactive deck** — discrete slides, fragment reveals, branching, hotspot navigation, presenter mode. Output is a navigable deck, not a rendered video.
- `/general-video` — fallback for any other video (title card, longer brand / sizzle reel, multi-scene montage, static loop, custom composition) and the home of **companion mode** — co-create with the full HyperFrames toolbox; the original hyperframes authoring flow, any length.
**Porting an existing composition?** `/remotion-to-hyperframes` translates a Remotion (React) composition into HyperFrames HTML — a source migration, separate from the creation workflows above.
The domain skills (`/hyperframes-core`, `/hyperframes-animation`, `/hyperframes-keyframes`, `/hyperframes-creative`, `/hyperframes-cli`, `/media-use`, `/hyperframes-audio`, `/hyperframes-registry`, `/figma`) and the full capability map live inside `/hyperframes` — it is the single source of truth for which skill handles which intent.
**Changing how real footage or images look or reveal?** Load `/media-use` and read its `references/media-treatments.md` before editing, even when the request only says dark, flat, boring, retro, private, or “make the reveal cooler.” It governs how footage is treated, never whether media may be used. Use canonical media treatments and seek-safe motion; do not improvise equivalent CSS/SVG filters or overlays.
> **Tailwind v4 projects** (`hyperframes init --tailwind`): see `/hyperframes-core` → `references/tailwind.md`.
> **Using a HyperFrames plugin?** Load skills from that installed bundle and follow
> its `hyperframes/references/plugin-installation.md` execution rules. Update via
> the plugin manager, not the standalone commands below.
>
> **Standalone skill missing or stale?** Run `npx hyperframes skills update <name>` to install/refresh
> the specific skill you need (the `/hyperframes` router does this automatically before
> entering a workflow), or bare `npx hyperframes skills update` to refresh the core set plus
> everything already installed — neither pulls the full set. Restart the agent session so
> newly installed skills load.
## Commands
```bash
npm run dev # human-operated foreground preview (blocks until stopped)
npx hyperframes preview --background # agent-safe persistent Studio preview
npx hyperframes preview --status # verify the persistent preview is listening
npx hyperframes preview --stop # stop it when review is finished
npm run check # lint + runtime + layout + motion + contrast (one command)
npm run render # render to MP4
npm run publish # publish and get a shareable link
npx hyperframes lint --verbose # include info-level findings
npx hyperframes lint --json # machine-readable output for CI
npx hyperframes docs <topic> # reference docs in terminal
```
> **Agents must use `npx hyperframes preview --background` for Studio handoff.** Do not rely
> on a shell/tool `run_in_background` wrapper around `npm run dev`: that foreground process
> remains owned by the invoking session and can disappear while the browser stays open,
> leaving refreshes at `ERR_CONNECTION_TIMED_OUT`. Verify with `preview --status`, keep it
> alive through review, and stop it explicitly with `preview --stop` afterward.
> **Back from the desktop app.** Once this project was opened in the HyperFrames desktop app, run `npx hyperframes catch-up` before your next change here: it lists what the person asked Framey in the app and which files changed since.
> **Pinned CLI version.** These scripts pin an exact `hyperframes@X.Y.Z` so this project re-renders identically over time. Weeks later that pin lags fixes shipped since. To move up: `npx hyperframes@latest upgrade --project . --check` (shows the delta), then `npx hyperframes@latest upgrade --project .` to rewrite the pins. Always unpinned — the pinned script re-runs the old version against itself.
## Documentation
**For quick reference**, use the local CLI docs command (no network required):
```bash
npx hyperframes docs <topic>
```
Topics: `data-attributes`, `gsap`, `compositions`, `rendering`, `examples`, `troubleshooting`
**For full documentation**, discover pages via the machine-readable index — do NOT guess URLs:
```
https://hyperframes.heygen.com/llms.txt
```
## Project Structure
- `index.html` — main composition (root timeline)
- `compositions/` — sub-compositions referenced via `data-composition-src`
- `meta.json` — project metadata (id, name)
- `transcript.json` — whisper word-level transcript (if generated)
## Linting — ALWAYS RUN AFTER CHANGES
After creating or editing any `.html` composition, **always** run the full check before considering the task complete:
```bash
npm run check
```
Fix all errors before presenting the result. Warnings should be reviewed before rendering.
## Key Rules
1. Every timed element needs `data-start` and a duration. `data-start` is what marks it as timed; `data-track-index` is an optional Studio display lane the render never reads
2. Give timed visual elements `class="clip"`. The framework keys visibility off `data-start`, not the class, but the shared `.clip` CSS is what gives a scene its full-frame box, and `lint` warns without it
3. Register one paused root timeline per composition on `window.__timelines`:
```js
window.__timelines = window.__timelines || {};
window.__timelines["composition-id"] = gsap.timeline({ paused: true });
```
Scene timelines manually added to this root must not be paused. A paused
child does not advance when the root is seeked. The runtime activates
registered composition siblings, not arbitrary nested scene timelines.
4. A video with sound keeps it on the `<video>` (`data-has-audio="true"`, no `muted`). Use a separate `<audio>` for music, voiceover, replacement audio, J/L cuts, or audio detached in Studio. Silent footage and b-roll: `muted`.
5. Sub-compositions use `data-composition-src="compositions/file.html"` to reference other HTML files
6. Only deterministic logic — no `Date.now()`, no `Math.random()`, no network fetches

View file

@ -0,0 +1,109 @@
# HyperFrames Composition Project
## Skills — USE THESE FIRST
**Always invoke the relevant skill before writing or modifying compositions.** Skills encode framework-specific patterns (e.g., `window.__timelines` registration, `data-*` attribute semantics, shader-compatible CSS rules) that are NOT in generic web docs. Skipping them produces broken compositions.
**Doing anything with HyperFrames?** Start at `/hyperframes` — it tells you what HyperFrames can do and which skill or workflow handles your intent (make a video, TTS / BGM, prep footage, author / animate, render, install blocks), confirms your brief up front (the intent layer), and routes every "make me a…" request (a video, a deck, a composition port) to the right workflow. Read it first, especially when there's no project context to orient you. The workflows it routes to:
- `/product-launch-video` — any **website** URL or brief / script → a product launch / SaaS / promo video, or a site tour / showcase featuring the site's own captured visuals.
- `/faceless-explainer` — arbitrary text (topic / article / notes), **no URL, no website capture** → 60-90s faceless explainer.
- `/embedded-captions` — an existing talking-head video (MP4) → the same footage with captions / subtitles added (rail + embed, or pure-cinematic embed); the footage itself is untouched.
- `/talking-head-recut` — an existing talking-head / interview / podcast video (MP4) → the same footage **packaged with designed graphic overlays** (kinetic titles, lower-thirds, data callouts, pull-quotes, side panels, pip) synced to the transcript; the clip plays unchanged underneath. (Plain captions/subtitles → `/embedded-captions`.)
- `/pr-to-video` — a GitHub PR (URL / `owner/repo#N` / "this PR") → 30-90s code-change explainer (changelog / feature reveal / fix / refactor).
- `/motion-graphics` — a short (typically under 10s) design-led **motion graphic**, motion-is-the-message, no narration: kinetic type, a stat / number count-up, a chart, a logo sting, a lower-third / overlay, or an animated tweet / headline / captured-page highlight; rendered to MP4 or a transparent overlay. Longer / narrated / custom → `/general-video`.
- `/music-to-video` — a **music track** (audio file, video to pull audio from, or one generated from a mood brief) → beat-synced video (lyric / slideshow / kinetic promo). Music drives pacing; user-supplied images / videos are cut onto the same beat grid.
- `/slideshow` — a **presentation / pitch deck / interactive deck** — discrete slides, fragment reveals, branching, hotspot navigation, presenter mode. Output is a navigable deck, not a rendered video.
- `/general-video` — fallback for any other video (title card, longer brand / sizzle reel, multi-scene montage, static loop, custom composition) and the home of **companion mode** — co-create with the full HyperFrames toolbox; the original hyperframes authoring flow, any length.
**Porting an existing composition?** `/remotion-to-hyperframes` translates a Remotion (React) composition into HyperFrames HTML — a source migration, separate from the creation workflows above.
The domain skills (`/hyperframes-core`, `/hyperframes-animation`, `/hyperframes-keyframes`, `/hyperframes-creative`, `/hyperframes-cli`, `/media-use`, `/hyperframes-audio`, `/hyperframes-registry`, `/figma`) and the full capability map live inside `/hyperframes` — it is the single source of truth for which skill handles which intent.
**Changing how real footage or images look or reveal?** Load `/media-use` and read its `references/media-treatments.md` before editing, even when the request only says dark, flat, boring, retro, private, or “make the reveal cooler.” It governs how footage is treated, never whether media may be used. Use canonical media treatments and seek-safe motion; do not improvise equivalent CSS/SVG filters or overlays.
> **Tailwind v4 projects** (`hyperframes init --tailwind`): see `/hyperframes-core` → `references/tailwind.md`.
> **Using a HyperFrames plugin?** Load skills from that installed bundle and follow
> its `hyperframes/references/plugin-installation.md` execution rules. Update via
> the plugin manager, not the standalone commands below.
>
> **Standalone skill missing or stale?** Run `npx hyperframes skills update <name>` to install/refresh
> the specific skill you need (the `/hyperframes` router does this automatically before
> entering a workflow), or bare `npx hyperframes skills update` to refresh the core set plus
> everything already installed — neither pulls the full set. Restart the agent session so
> newly installed skills load.
## Commands
```bash
npm run dev # human-operated foreground preview (blocks until stopped)
npx hyperframes preview --background # agent-safe persistent Studio preview
npx hyperframes preview --status # verify the persistent preview is listening
npx hyperframes preview --stop # stop it when review is finished
npm run check # lint + runtime + layout + motion + contrast (one command)
npm run render # render to MP4
npm run publish # publish and get a shareable link
npx hyperframes lint --verbose # include info-level findings
npx hyperframes lint --json # machine-readable output for CI
npx hyperframes docs <topic> # reference docs in terminal
```
> **Agents must use `npx hyperframes preview --background` for Studio handoff.** Do not rely
> on a shell/tool `run_in_background` wrapper around `npm run dev`: that foreground process
> remains owned by the invoking session and can disappear while the browser stays open,
> leaving refreshes at `ERR_CONNECTION_TIMED_OUT`. Verify with `preview --status`, keep it
> alive through review, and stop it explicitly with `preview --stop` afterward.
> **Back from the desktop app.** Once this project was opened in the HyperFrames desktop app, run `npx hyperframes catch-up` before your next change here: it lists what the person asked Framey in the app and which files changed since.
> **Pinned CLI version.** These scripts pin an exact `hyperframes@X.Y.Z` so this project re-renders identically over time. Weeks later that pin lags fixes shipped since. To move up: `npx hyperframes@latest upgrade --project . --check` (shows the delta), then `npx hyperframes@latest upgrade --project .` to rewrite the pins. Always unpinned — the pinned script re-runs the old version against itself.
## Documentation
**For quick reference**, use the local CLI docs command (no network required):
```bash
npx hyperframes docs <topic>
```
Topics: `data-attributes`, `gsap`, `compositions`, `rendering`, `examples`, `troubleshooting`
**For full documentation**, discover pages via the machine-readable index — do NOT guess URLs:
```
https://hyperframes.heygen.com/llms.txt
```
## Project Structure
- `index.html` — main composition (root timeline)
- `compositions/` — sub-compositions referenced via `data-composition-src`
- `meta.json` — project metadata (id, name)
- `transcript.json` — whisper word-level transcript (if generated)
## Linting — ALWAYS RUN AFTER CHANGES
After creating or editing any `.html` composition, **always** run the full check before considering the task complete:
```bash
npm run check
```
Fix all errors before presenting the result. Warnings should be reviewed before rendering.
## Key Rules
1. Every timed element needs `data-start` and a duration. `data-start` is what marks it as timed; `data-track-index` is an optional Studio display lane the render never reads
2. Give timed visual elements `class="clip"`. The framework keys visibility off `data-start`, not the class, but the shared `.clip` CSS is what gives a scene its full-frame box, and `lint` warns without it
3. Register one paused root timeline per composition on `window.__timelines`:
```js
window.__timelines = window.__timelines || {};
window.__timelines["composition-id"] = gsap.timeline({ paused: true });
```
Scene timelines manually added to this root must not be paused. A paused
child does not advance when the root is seeked. The runtime activates
registered composition siblings, not arbitrary nested scene timelines.
4. A video with sound keeps it on the `<video>` (`data-has-audio="true"`, no `muted`). Use a separate `<audio>` for music, voiceover, replacement audio, J/L cuts, or audio detached in Studio. Silent footage and b-roll: `muted`.
5. Sub-compositions use `data-composition-src="compositions/file.html"` to reference other HTML files
6. Only deterministic logic — no `Date.now()`, no `Math.random()`, no network fetches

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,21 @@
{
"tts_provider": null,
"voice_id": null,
"bgm": {
"path": "assets/bgm/track.wav",
"volume": 0.9,
"mode": "detached-seed-loop",
"duration_s": null
},
"bgm_pending": true,
"bgm_provider": "musicgen",
"bgm_pid": 829156,
"bgm_log": "/root/simbiot-harness/video/simbiot-promo/assets/bgm/bgm-1792234854170.log",
"bgm_mode": "detached-seed-loop",
"bgm_target_duration_s": 30,
"bgm_seed_duration_s": 28,
"bgm_loop_count": 2,
"voices": [],
"sfx": [],
"total_duration_s": 0
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

11
video/simbiot-promo/gsap.min.js vendored Normal file

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,12 @@
{
"$schema": "https://hyperframes.heygen.com/schema/hyperframes.json",
"registry": "https://raw.githubusercontent.com/heygen-com/hyperframes/main/registry",
"paths": {
"blocks": "compositions",
"components": "compositions/components",
"assets": "assets"
},
"media": {
"autoProxy": true
}
}

View file

@ -0,0 +1,451 @@
<!doctype html>
<html lang="ru">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=1920, height=1080" />
<title>Simbiot Harness — промо</title>
<script src="./gsap.min.js"></script>
<style>
@font-face { font-family: "Unbounded"; font-weight: 700; src: url("fonts/unbounded-700-latin.woff2") format("woff2"); unicode-range: U+0000-00FF, U+2000-206F, U+2190-21FF, U+2200-22FF; }
@font-face { font-family: "Unbounded"; font-weight: 700; src: url("fonts/unbounded-700-cyrillic.woff2") format("woff2"); unicode-range: U+0400-04FF; }
@font-face { font-family: "Unbounded"; font-weight: 400; src: url("fonts/unbounded-400-latin.woff2") format("woff2"); unicode-range: U+0000-00FF, U+2000-206F, U+2190-21FF, U+2200-22FF; }
@font-face { font-family: "Unbounded"; font-weight: 400; src: url("fonts/unbounded-400-cyrillic.woff2") format("woff2"); unicode-range: U+0400-04FF; }
@font-face { font-family: "Inter"; font-weight: 400; src: url("fonts/inter-400-latin.woff2") format("woff2"); unicode-range: U+0000-00FF, U+2000-206F, U+2190-21FF, U+2200-22FF; }
@font-face { font-family: "Inter"; font-weight: 400; src: url("fonts/inter-400-cyrillic.woff2") format("woff2"); unicode-range: U+0400-04FF; }
@font-face { font-family: "Inter"; font-weight: 600; src: url("fonts/inter-600-latin.woff2") format("woff2"); unicode-range: U+0000-00FF, U+2000-206F, U+2190-21FF, U+2200-22FF; }
@font-face { font-family: "Inter"; font-weight: 600; src: url("fonts/inter-600-cyrillic.woff2") format("woff2"); unicode-range: U+0400-04FF; }
@font-face { font-family: "JetBrains Mono"; font-weight: 400; src: url("fonts/jetbrains-mono-400-latin.woff2") format("woff2"); unicode-range: U+0000-00FF, U+2000-206F, U+2190-21FF, U+2200-22FF; }
@font-face { font-family: "JetBrains Mono"; font-weight: 400; src: url("fonts/jetbrains-mono-400-cyrillic.woff2") format("woff2"); unicode-range: U+0400-04FF; }
@font-face { font-family: "JetBrains Mono"; font-weight: 700; src: url("fonts/jetbrains-mono-700-latin.woff2") format("woff2"); unicode-range: U+0000-00FF, U+2000-206F, U+2190-21FF, U+2200-22FF; }
@font-face { font-family: "JetBrains Mono"; font-weight: 700; src: url("fonts/jetbrains-mono-700-cyrillic.woff2") format("woff2"); unicode-range: U+0400-04FF; }
:root {
--bg: #06080c;
--ink: #eef2f6;
--dim: #a7b0bd;
--mint: #5cffb0;
--violet: #8b7cff;
--amber: #ffb84d;
--rose: #ff6b8b;
--line: rgba(255, 255, 255, 0.08);
}
* { margin: 0; padding: 0; box-sizing: border-box; }
html, body { width: 1920px; height: 1080px; overflow: hidden; background: var(--bg); }
body { color: var(--ink); font-family: "Inter", sans-serif; }
#root { position: relative; width: 100%; height: 100%; overflow: hidden; }
.clip { position: absolute; inset: 0; }
.mono { font-family: "JetBrains Mono", monospace; }
.display { font-family: "Unbounded", sans-serif; }
/* ---------- фон ---------- */
#bg { background: radial-gradient(1200px 800px at 50% 40%, #0d1420 0%, var(--bg) 70%); }
#bg-grid {
position: absolute; inset: -120px;
background-image: linear-gradient(var(--line) 1px, transparent 1px), linear-gradient(90deg, var(--line) 1px, transparent 1px);
background-size: 80px 80px;
mask-image: radial-gradient(ellipse at center, #000 30%, transparent 75%);
}
#bg-glow-a, #bg-glow-b { position: absolute; width: 900px; height: 900px; border-radius: 50%; filter: blur(140px); opacity: 0.35; }
#bg-glow-a { background: var(--mint); left: -300px; top: -350px; opacity: 0.12; }
#bg-glow-b { background: var(--violet); right: -300px; bottom: -400px; opacity: 0.16; }
/* ---------- HUD ---------- */
#hud { pointer-events: none; }
.hud-row { position: absolute; left: 64px; right: 64px; display: flex; justify-content: space-between; align-items: center; font-size: 18px; letter-spacing: 0.12em; color: var(--dim); text-transform: uppercase; }
#hud-top { top: 44px; }
#hud-bot { bottom: 44px; }
.hud-dot { display: inline-block; width: 10px; height: 10px; border-radius: 50%; background: var(--mint); margin-right: 14px; box-shadow: 0 0 14px var(--mint); }
.hud-lv { display: flex; gap: 28px; }
.hud-lv span { display: block; }
.hud-lv b { color: var(--mint); font-weight: 400; }
/* ---------- общие ---------- */
.scene { display: flex; flex-direction: column; align-items: center; justify-content: center; padding: 140px 160px; }
.kicker { font-size: 22px; letter-spacing: 0.32em; color: var(--mint); text-transform: uppercase; margin-bottom: 28px; }
.h2 { font-family: "Unbounded", sans-serif; font-weight: 700; font-size: 76px; letter-spacing: -0.02em; line-height: 1.05; text-align: center; }
.sub { font-size: 30px; color: var(--dim); margin-top: 22px; text-align: center; }
.accent { color: var(--mint); }
/* ---------- S1 интро ---------- */
#s1-ecg { position: absolute; left: 0; top: 0; width: 1920px; height: 1080px; }
#s1-center { position: absolute; inset: 0; display: flex; flex-direction: column; align-items: center; justify-content: center; }
#s1-title { display: flex; gap: 0.32em; font-family: "Unbounded", sans-serif; font-weight: 700; font-size: 150px; letter-spacing: -0.03em; }
#s1-title .w { display: flex; overflow: hidden; padding: 0 4px 12px; }
#s1-title .ch { display: block; }
#s1-title .w2 .ch { color: var(--mint); }
#s1-tag { font-size: 40px; margin-top: 18px; color: var(--ink); }
#s1-stack { margin-top: 40px; font-size: 22px; color: var(--dim); letter-spacing: 0.14em; display: flex; gap: 22px; }
#s1-stack span { display: block; }
/* ---------- S2 лестница ---------- */
#s2-rows { margin-top: 64px; width: 1500px; display: flex; flex-direction: column; gap: 22px; }
.lvl { display: grid; grid-template-columns: 150px 150px 1fr 260px; align-items: center; gap: 30px; height: 96px; padding: 0 34px; border: 1px solid var(--line); border-radius: 20px; background: rgba(255, 255, 255, 0.025); }
.lvl-name { font-family: "Unbounded", sans-serif; font-weight: 700; font-size: 38px; }
.lvl-int { font-size: 26px; color: var(--dim); }
.lvl-bar { position: relative; height: 16px; border-radius: 8px; background: rgba(255, 255, 255, 0.06); }
.lvl-fill { position: absolute; left: 0; top: 0; height: 16px; width: 100%; border-radius: 8px; background: linear-gradient(90deg, var(--mint), var(--violet)); transform-origin: left center; }
.lvl-ctx { font-size: 26px; text-align: right; color: var(--ink); }
.lvl-ctx small { color: var(--dim); font-size: 20px; margin-left: 8px; }
.pulse { position: absolute; left: 0; width: 22px; height: 22px; border-radius: 50%; top: -3px; background: var(--mint); box-shadow: 0 0 24px var(--mint); }
#s2-foot { margin-top: 46px; font-size: 30px; color: var(--dim); display: flex; align-items: center; gap: 18px; }
#s2-foot b { color: var(--ink); font-weight: 600; }
.arrow-up { display: block; width: 40px; height: 40px; }
/* ---------- S3 ES-спина ---------- */
#s3-track-wrap { position: relative; width: 1920px; height: 340px; margin-top: 70px; overflow: hidden; }
#s3-track { position: absolute; left: 0; top: 30px; display: flex; gap: 0; align-items: center; }
.ev { position: relative; width: 340px; height: 230px; flex: 0 0 340px; margin-right: 70px; padding: 26px 28px; border-radius: 22px; border: 1px solid rgba(92, 255, 176, 0.25); background: linear-gradient(160deg, rgba(92, 255, 176, 0.08), rgba(139, 124, 255, 0.05)); }
.ev-seq { font-size: 18px; color: var(--dim); }
.ev-name { font-family: "JetBrains Mono", monospace; font-weight: 700; font-size: 29px; margin-top: 14px; color: var(--ink); }
.ev-sig { font-size: 17px; color: var(--mint); margin-top: 26px; line-height: 1.6; }
.ev-sig span { display: block; color: var(--dim); }
.ev-link { position: absolute; right: -70px; top: 108px; width: 70px; height: 4px; background: linear-gradient(90deg, var(--mint), var(--violet)); }
#s3-verify { margin-top: 30px; font-size: 30px; padding: 18px 34px; border-radius: 14px; background: rgba(0, 0, 0, 0.5); border: 1px solid var(--line); }
#s3-verify .ok { color: var(--mint); }
/* ---------- S4 куб ---------- */
#s4 { display: flex; align-items: center; padding: 0 140px 0 180px; }
#s4-text { width: 760px; }
#s4-text .h2 { text-align: left; font-size: 70px; }
#s4-axes { margin-top: 46px; display: flex; flex-direction: column; gap: 22px; }
.axis { display: flex; align-items: baseline; gap: 22px; font-size: 28px; }
.axis .ax-k { font-family: "Unbounded", sans-serif; font-weight: 700; font-size: 32px; color: var(--mint); width: 90px; display: block; }
.axis .ax-v { color: var(--ink); display: block; }
.axis .ax-v small { display: block; color: var(--dim); font-size: 21px; margin-top: 4px; }
#s4-punch { margin-top: 52px; font-size: 34px; font-weight: 600; }
#s4-stage { flex: 1; height: 900px; position: relative; perspective: 2200px; display: flex; align-items: center; justify-content: center; }
#s4-cube { position: relative; width: 0; height: 0; transform-style: preserve-3d; }
.cell { position: absolute; width: 96px; height: 96px; left: -48px; top: -48px; border-radius: 12px; border: 1px solid rgba(255, 255, 255, 0.35); }
#s4-label { position: absolute; bottom: 70px; left: 0; right: 0; text-align: center; font-size: 24px; color: var(--dim); letter-spacing: 0.08em; }
#s4-label b { color: var(--mint); font-weight: 400; }
/* ---------- S5 органы чувств ---------- */
#s5-stage { position: absolute; left: 0; top: 0; width: 1920px; height: 1080px; }
#s5-lines { position: absolute; left: 0; top: 0; width: 1920px; height: 1080px; }
#s5-core { position: absolute; left: 810px; top: 390px; width: 300px; height: 300px; border-radius: 50%; display: flex; flex-direction: column; align-items: center; justify-content: center; background: radial-gradient(circle, rgba(92, 255, 176, 0.25), rgba(92, 255, 176, 0.03) 70%); border: 2px solid rgba(92, 255, 176, 0.5); }
#s5-core .display { font-weight: 700; font-size: 40px; }
#s5-core .mono { font-size: 18px; color: var(--dim); margin-top: 8px; }
#s5-ring { position: absolute; left: 760px; top: 340px; width: 400px; height: 400px; border-radius: 50%; border: 1px dashed rgba(92, 255, 176, 0.35); }
.node { position: absolute; width: 380px; padding: 24px 28px; border-radius: 20px; background: rgba(12, 18, 28, 0.92); border: 1px solid var(--line); }
.node-t { font-family: "Unbounded", sans-serif; font-weight: 700; font-size: 27px; }
.node-d { font-size: 21px; color: var(--dim); margin-top: 8px; line-height: 1.35; }
.node-i { font-size: 18px; color: var(--mint); margin-bottom: 8px; letter-spacing: 0.1em; }
#s5-title { position: absolute; top: 118px; left: 0; right: 0; text-align: center; }
#s5-title .h2 { font-size: 58px; }
/* ---------- S6 AUM ---------- */
#s6 { display: flex; align-items: center; padding: 0 160px; gap: 80px; }
#s6-left { width: 780px; flex: 0 0 780px; }
#s6-left .h2 { text-align: left; font-size: 72px; }
#s6-num { font-family: "Unbounded", sans-serif; font-weight: 700; font-size: 210px; color: var(--mint); line-height: 1; margin-top: 30px; display: flex; align-items: baseline; }
#s6-num-cap { font-size: 30px; color: var(--ink); margin-top: 6px; }
#s6-q { margin-top: 36px; font-size: 32px; line-height: 1.4; color: var(--ink); border-left: 4px solid var(--mint); padding-left: 26px; }
#s6-q span { color: var(--dim); display: block; font-size: 22px; margin-top: 10px; }
#s6-chart { width: 800px; height: 546px; flex: 0 0 800px; }
/* ---------- S7 аутро ---------- */
#s7-logo { font-family: "Unbounded", sans-serif; font-weight: 700; font-size: 128px; letter-spacing: -0.03em; }
#s7-logo span { color: var(--mint); }
#s7-term { margin-top: 56px; width: 1320px; border-radius: 18px; overflow: hidden; border: 1px solid var(--line); background: #0b0f16; }
#s7-term-bar { height: 46px; display: flex; align-items: center; gap: 10px; padding: 0 20px; background: rgba(255, 255, 255, 0.04); }
#s7-term-bar i { display: block; width: 14px; height: 14px; border-radius: 50%; }
#s7-cmd { padding: 34px 38px; font-size: 34px; display: flex; flex-wrap: wrap; }
#s7-cmd .p { color: var(--mint); margin-right: 18px; }
#s7-cmd .c { display: block; white-space: pre; opacity: 0; }
#s7-caret { display: block; width: 18px; height: 40px; background: var(--mint); margin-left: 4px; }
#s7-foot { margin-top: 48px; font-size: 30px; color: var(--dim); letter-spacing: 0.08em; }
#s7-foot b { color: var(--ink); font-weight: 600; }
</style>
</head>
<body>
<div id="root" data-composition-id="main" data-start="0" data-duration="50" data-width="1920" data-height="1080">
<!-- фон на весь ролик -->
<div id="bg" class="clip" data-start="0" data-duration="50" data-track-index="0">
<div id="bg-grid"></div>
<div id="bg-glow-a"></div>
<div id="bg-glow-b"></div>
</div>
<!-- S1 — сердцебиение и имя -->
<section id="s1" class="clip" data-start="0" data-duration="5.2" data-track-index="1">
<div id="s1-in" style="position:absolute;inset:0">
<svg id="s1-ecg" viewBox="0 0 1920 1080">
<path id="s1-ecg-path" d="M0 540 L620 540 L660 540 L690 470 L720 610 L760 300 L800 760 L840 540 L900 540 L1000 540 L1030 500 L1060 580 L1100 540 L1920 540" fill="none" stroke="#5cffb0" stroke-width="4" stroke-linejoin="round" />
</svg>
<div id="s1-center">
<div id="s1-title">
<div class="w w1"><span class="ch">S</span><span class="ch">I</span><span class="ch">M</span><span class="ch">B</span><span class="ch">I</span><span class="ch">O</span><span class="ch">T</span></div>
<div class="w w2"><span class="ch">H</span><span class="ch">A</span><span class="ch">R</span><span class="ch">N</span><span class="ch">E</span><span class="ch">S</span><span class="ch">S</span></div>
</div>
<div id="s1-tag">Агент, который живёт тактами — как человек</div>
<div id="s1-stack" class="mono">
<span>CQRS</span><span>·</span><span>EVENT SOURCING</span><span>·</span><span>STRANDS</span><span>·</span><span>9ROUTER</span>
</div>
</div>
</div>
</section>
<!-- S2 — лестница времени -->
<section id="s2" class="clip scene" data-start="5.2" data-duration="8" data-track-index="1">
<div id="s2-in" style="display:flex;flex-direction:column;align-items:center">
<div class="kicker mono" id="s2-k">01 · Лестница времени</div>
<div class="h2" id="s2-h" style="font-size:60px">Секунда рефлексов. <span class="accent">День размышлений.</span></div>
<div id="s2-rows">
<div class="lvl" id="lv-sec"><div class="lvl-name">sec</div><div class="lvl-int mono">1 с</div><div class="lvl-bar"><div class="lvl-fill" style="width:1%"></div><div class="pulse" id="p-sec"></div></div><div class="lvl-ctx mono">без LLM<small>рефлекс</small></div></div>
<div class="lvl" id="lv-min"><div class="lvl-name">min</div><div class="lvl-int mono">1 мин</div><div class="lvl-bar"><div class="lvl-fill" style="width:25.6%"></div><div class="pulse" id="p-min"></div></div><div class="lvl-ctx mono">256K<small>триаж</small></div></div>
<div class="lvl" id="lv-hour"><div class="lvl-name">hour</div><div class="lvl-int mono">1 ч</div><div class="lvl-bar"><div class="lvl-fill" style="width:51.2%"></div><div class="pulse" id="p-hour"></div></div><div class="lvl-ctx mono">512K<small>дайджест</small></div></div>
<div class="lvl" id="lv-day"><div class="lvl-name">day</div><div class="lvl-int mono">1 д</div><div class="lvl-bar"><div class="lvl-fill" style="width:100%"></div><div class="pulse" id="p-day"></div></div><div class="lvl-ctx mono">1M<small>манифест</small></div></div>
</div>
<div id="s2-foot">
<svg class="arrow-up" viewBox="0 0 40 40"><path d="M20 34 L20 6 M8 18 L20 6 L32 18" stroke="#5cffb0" stroke-width="4" fill="none" stroke-linecap="round" /></svg>
<div>Тик идемпотентен. Задача не прерывается. <b>Наверх идут только дайджесты.</b></div>
</div>
</div>
</section>
<!-- S3 — событийная спина + криптоаудит -->
<section id="s3" class="clip scene" data-start="13.2" data-duration="8.4" data-track-index="1">
<div id="s3-in" style="display:flex;flex-direction:column;align-items:center">
<div class="kicker mono" id="s3-k">02 · Event Sourcing</div>
<div class="h2" id="s3-h">Состояние — <span class="accent">производное от событий</span></div>
<div class="sub" id="s3-sub">Append-only. Каждое событие подписано ed25519 и сцеплено с предыдущим.</div>
<div id="s3-track-wrap" data-layout-allow-overflow>
<div id="s3-track" data-layout-allow-overflow>
<div class="ev"><div class="ev-seq mono">#40211 · tg</div><div class="ev-name">MessageReceived</div><div class="ev-sig mono">sig 7c1e…a90f<span>prev 3b02…e1d4</span></div><div class="ev-link"></div></div>
<div class="ev"><div class="ev-seq mono">#40212 · min</div><div class="ev-name">CycleTicked</div><div class="ev-sig mono">sig 0d9a…41bc<span>prev 7c1e…a90f</span></div><div class="ev-link"></div></div>
<div class="ev"><div class="ev-seq mono">#40213 · task</div><div class="ev-name">TaskSubmitted</div><div class="ev-sig mono">sig e5f7…02aa<span>prev 0d9a…41bc</span></div><div class="ev-link"></div></div>
<div class="ev"><div class="ev-seq mono">#40214 · task</div><div class="ev-name">TaskClaimed</div><div class="ev-sig mono">sig 91c4…7d3e<span>prev e5f7…02aa</span></div><div class="ev-link"></div></div>
<div class="ev"><div class="ev-seq mono">#40215 · task</div><div class="ev-name">TaskCompleted</div><div class="ev-sig mono">sig 2ab8…c615<span>prev 91c4…7d3e</span></div><div class="ev-link"></div></div>
<div class="ev"><div class="ev-seq mono">#40216 · tg</div><div class="ev-name">DeliverySent</div><div class="ev-sig mono">sig 6f03…9b27<span>prev 2ab8…c615</span></div><div class="ev-link"></div></div>
<div class="ev"><div class="ev-seq mono">#40217 · hour</div><div class="ev-name">DigestWritten</div><div class="ev-sig mono">sig c44d…18f0<span>prev 6f03…9b27</span></div><div class="ev-link"></div></div>
<div class="ev"><div class="ev-seq mono">#40218 · day</div><div class="ev-name">AumMeasured</div><div class="ev-sig mono">sig 8e2b…d771<span>prev c44d…18f0</span></div></div>
</div>
</div>
<div id="s3-verify" class="mono">$ cli.py verify <span class="ok">→ цепочка цела ✓ exit 0</span></div>
</div>
</section>
<!-- S4 — куб 4×4×4 -->
<section id="s4" class="clip" data-start="21.6" data-duration="9" data-track-index="1">
<div id="s4-text">
<div class="kicker mono" id="s4-k">03 · Куб политик</div>
<div class="h2" id="s4-h">Кому, когда и&nbsp;насколько глубоко</div>
<div id="s4-axes">
<div class="axis" id="ax1"><span class="ax-k">×4</span><span class="ax-v">круга доверия<small>владелец · правление · свои · остальные</small></span></div>
<div class="axis" id="ax2"><span class="ax-k">×4</span><span class="ax-v">такта<small>sec · min · hour · day</small></span></div>
<div class="axis" id="ax3"><span class="ax-k">×4</span><span class="ax-v">глубины памяти<small>D0 реплика · D1 разговор · D2 эпизоды · D3 архив</small></span></div>
</div>
<div id="s4-punch">Одна таблица политик — <span class="accent">не 64 программы.</span></div>
</div>
<div id="s4-stage">
<div id="s4-cube"></div>
<div id="s4-label" class="mono">круг тика = <b>самый недоверенный вход</b></div>
</div>
</section>
<!-- S5 — органы чувств -->
<section id="s5" class="clip" data-start="30.6" data-duration="7" data-track-index="1">
<div id="s5-stage">
<div id="s5-title"><div class="kicker mono" id="s5-k">04 · Руки и уши</div><div class="h2" id="s5-h">Служит, а не ждёт указки</div></div>
<svg id="s5-lines" viewBox="0 0 1920 1080">
<path class="s5l" d="M960 540 L520 400" stroke="#5cffb0" stroke-opacity="0.45" stroke-width="2" fill="none" />
<path class="s5l" d="M960 540 L1400 400" stroke="#5cffb0" stroke-opacity="0.45" stroke-width="2" fill="none" />
<path class="s5l" d="M960 540 L460 640" stroke="#8b7cff" stroke-opacity="0.45" stroke-width="2" fill="none" />
<path class="s5l" d="M960 540 L1460 640" stroke="#8b7cff" stroke-opacity="0.45" stroke-width="2" fill="none" />
<path class="s5l" d="M960 540 L700 860" stroke="#5cffb0" stroke-opacity="0.45" stroke-width="2" fill="none" />
<path class="s5l" d="M960 540 L1220 860" stroke="#5cffb0" stroke-opacity="0.45" stroke-width="2" fill="none" />
</svg>
<div id="s5-ring"></div>
<div id="s5-core"><div class="display">SIMBIOT</div><div class="mono">ядро тика</div></div>
<div class="node" id="n1" style="left:150px;top:300px"><div class="node-i mono">TELEGRAM</div><div class="node-t">@juno42bot</div><div class="node-d">батч-триаж входящих за один тик</div></div>
<div class="node" id="n2" style="left:1390px;top:300px"><div class="node-i mono">ПОРТАЛ · MCP</div><div class="node-t">Звонки из чата</div><div class="node-d">слоты → jitsi → запись и транскрипт</div></div>
<div class="node" id="n3" style="left:90px;top:560px"><div class="node-i mono">RAG</div><div class="node-t">База знаний</div><div class="node-d">12.5K чанков кооператива</div></div>
<div class="node" id="n4" style="left:1450px;top:560px"><div class="node-i mono">DECISION</div><div class="node-t">clef:27b</div><div class="node-d">выбор без генерации, 0.6 с</div></div>
<div class="node" id="n5" style="left:330px;top:820px"><div class="node-i mono">ЯДРО КОДИ</div><div class="node-t">Учится сам</div><div class="node-d">ученик сменяет чемпиона по log-loss</div></div>
<div class="node" id="n6" style="left:1210px;top:820px"><div class="node-i mono">ДАШБОРД</div><div class="node-t">Всё видно</div><div class="node-d">задачи, лестница, эскалации live</div></div>
</div>
</section>
<!-- S6 — AUM -->
<section id="s6" class="clip" data-start="37.6" data-duration="6.4" data-track-index="1">
<div id="s6-left">
<div class="kicker mono" id="s6-k">05 · Одна цель</div>
<div class="h2" id="s6-h">Долгосрочная AUM кооператива</div>
<div id="s6-num"><span id="s6-val">+0%</span></div><div id="s6-num-cap">к AUM в горизонте год+</div>
<div id="s6-q">«Добавит ли это ≥1% к AUM за год+?»<span>Фильтр каждой задачи на каждом уровне.</span></div>
</div>
<svg id="s6-chart" viewBox="0 0 820 560">
<defs>
<linearGradient id="s6-grad" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#5cffb0" stop-opacity="0.35" /><stop offset="1" stop-color="#5cffb0" stop-opacity="0" /></linearGradient>
</defs>
<g stroke="rgba(255,255,255,0.08)" stroke-width="1">
<line x1="0" y1="140" x2="820" y2="140" /><line x1="0" y1="280" x2="820" y2="280" /><line x1="0" y1="420" x2="820" y2="420" /><line x1="0" y1="559" x2="820" y2="559" />
</g>
<path id="s6-area" d="M0 500 L80 488 L160 492 L240 462 L320 450 L400 404 L480 380 L560 318 L640 270 L720 196 L820 90 L820 560 L0 560 Z" fill="url(#s6-grad)" />
<path id="s6-line" d="M0 500 L80 488 L160 492 L240 462 L320 450 L400 404 L480 380 L560 318 L640 270 L720 196 L820 90" fill="none" stroke="#5cffb0" stroke-width="5" stroke-linejoin="round" stroke-linecap="round" />
<circle id="s6-dot" cx="820" cy="90" r="12" fill="#5cffb0" />
</svg>
</section>
<!-- S7 — аутро -->
<section id="s7" class="clip scene" data-start="44" data-duration="6" data-track-index="1">
<div id="s7-logo">SIMBIOT <span>HARNESS</span></div>
<div id="s7-term">
<div id="s7-term-bar"><i style="background:#ff5f57"></i><i style="background:#febc2e"></i><i style="background:#28c840"></i></div>
<div id="s7-cmd" class="mono"><span class="p">$</span></div>
</div>
<div id="s7-foot">Кооператив 42 · <b>cil.by</b></div>
</section>
<audio id="bgm" src="assets/bgm/bed.wav" data-start="0" data-duration="50" data-track-index="3" data-volume="0.85"></audio>
<!-- HUD поверх всего -->
<div id="hud" class="clip" data-start="0" data-duration="50" data-track-index="2">
<div class="hud-row mono" id="hud-top"><div><span class="hud-dot" id="hud-dot"></span>SIMBIOT · LIVE</div><div id="hud-seq">EVENT #40211</div></div>
<div class="hud-row mono" id="hud-bot"><div>КООПЕРАТИВ 42</div><div class="hud-lv"><span><b>sec</b></span><span>min</span><span>hour</span><span>day</span></div></div>
</div>
</div>
<script>
// ---- куб 4×4×4: x = круг доверия, y = такт, z = глубина памяти
const cube = document.getElementById("s4-cube");
const circleColors = ["#5cffb0", "#8b7cff", "#ffb84d", "#ff6b8b"];
const STEP = 124;
for (let x = 0; x < 4; x++)
for (let y = 0; y < 4; y++)
for (let z = 0; z < 4; z++) {
const c = document.createElement("div");
c.className = "cell c" + x;
c.dataset.x = x; c.dataset.y = y; c.dataset.z = z;
const a = 0.16 + z * 0.1;
c.style.background = circleColors[x] + Math.round(a * 255).toString(16).padStart(2, "0");
c.style.borderColor = circleColors[x];
cube.appendChild(c);
}
// ---- набор команды по символам (seek-safe: только opacity)
const cmd = document.getElementById("s7-cmd");
const text = "curl -fsSL https://raw.cil.by/simbiot/install.sh | bash";
for (const ch of text) {
const s = document.createElement("span");
s.className = "c";
s.textContent = ch;
cmd.appendChild(s);
}
const caret = document.createElement("span");
caret.id = "s7-caret";
cmd.appendChild(caret);
const tl = gsap.timeline({ paused: true });
// ---------- фон: медленный дрейф ----------
tl.fromTo("#bg-grid", { x: 0, y: 0 }, { x: 80, y: 80, duration: 50, ease: "none" }, 0);
tl.fromTo("#bg-glow-a", { x: 0, y: 0 }, { x: 500, y: 260, duration: 50, ease: "sine.inOut" }, 0);
tl.fromTo("#bg-glow-b", { x: 0, y: 0 }, { x: -520, y: -200, duration: 50, ease: "sine.inOut" }, 0);
// ---------- HUD ----------
tl.fromTo("#hud-top, #hud-bot", { opacity: 0 }, { opacity: 1, duration: 1, ease: "power2.out" }, 2.4);
tl.fromTo("#hud-dot", { scale: 1 }, { scale: 0.4, duration: 0.5, yoyo: true, repeat: 47, ease: "sine.inOut" }, 2.4);
const seq = { n: 40211 };
const seqEl = document.getElementById("hud-seq");
tl.fromTo(seq, { n: 40211 }, { n: 40987, duration: 47, ease: "none", onUpdate: () => { seqEl.textContent = "EVENT #" + Math.round(seq.n); } }, 2.4);
// ---------- S1 ----------
const ecg = document.getElementById("s1-ecg-path");
const ecgLen = ecg.getTotalLength();
tl.fromTo(ecg, { strokeDasharray: ecgLen, strokeDashoffset: ecgLen }, { strokeDashoffset: 0, duration: 1.5, ease: "power2.inOut" }, 0.1);
tl.to("#s1-ecg", { opacity: 0.12, duration: 0.8, ease: "power2.out" }, 1.4);
tl.fromTo("#s1-title .ch", { yPercent: 110 }, { yPercent: 0, duration: 0.7, stagger: 0.04, ease: "power4.out" }, 1.3);
tl.fromTo("#s1-tag", { opacity: 0, y: 24 }, { opacity: 1, y: 0, duration: 0.7, ease: "power3.out" }, 2.1);
tl.fromTo("#s1-stack span", { opacity: 0, y: 12 }, { opacity: 1, y: 0, duration: 0.4, stagger: 0.06, ease: "power2.out" }, 2.6);
tl.to("#s1-in", { opacity: 0, scale: 1.04, duration: 0.5, ease: "power2.in" }, 4.7);
// ---------- S2 ----------
const S2 = 5.2;
tl.fromTo("#s2-k", { opacity: 0, y: 16 }, { opacity: 1, y: 0, duration: 0.5, ease: "power3.out" }, S2 + 0.1);
tl.fromTo("#s2-h", { opacity: 0, y: 30 }, { opacity: 1, y: 0, duration: 0.7, ease: "power3.out" }, S2 + 0.2);
tl.fromTo(".lvl", { opacity: 0, x: -60 }, { opacity: 1, x: 0, duration: 0.6, stagger: 0.14, ease: "power3.out" }, S2 + 0.7);
tl.fromTo(".lvl-fill", { scaleX: 0 }, { scaleX: 1, duration: 1.1, stagger: 0.14, ease: "power3.inOut" }, S2 + 1.0);
// пульсы бегут по полосам с разной частотой — разный ритм уровней
const pulseDefs = [["#p-sec", 0.35], ["#p-min", 0.9], ["#p-hour", 1.8], ["#p-day", 3.6]];
const runFrom = S2 + 1.6, runFor = 6.0;
for (const [sel, period] of pulseDefs) {
const reps = Math.max(0, Math.floor(runFor / period) - 1);
const travel = document.querySelector(sel).parentElement.offsetWidth - 22;
tl.fromTo(sel, { x: 0, opacity: 1 }, { x: travel, opacity: 0.2, duration: period, repeat: reps, ease: "none" }, runFrom);
}
tl.fromTo("#s2-foot", { opacity: 0, y: 20 }, { opacity: 1, y: 0, duration: 0.6, ease: "power3.out" }, S2 + 2.6);
tl.fromTo(".arrow-up", { y: 8 }, { y: -6, duration: 0.6, yoyo: true, repeat: 6, ease: "sine.inOut" }, S2 + 2.8);
tl.to("#s2-in", { opacity: 0, y: -40, duration: 0.5, ease: "power2.in" }, S2 + 7.5);
// ---------- S3 ----------
const S3 = 13.2;
tl.fromTo("#s3-k", { opacity: 0, y: 16 }, { opacity: 1, y: 0, duration: 0.5, ease: "power3.out" }, S3 + 0.1);
tl.fromTo("#s3-h", { opacity: 0, y: 30 }, { opacity: 1, y: 0, duration: 0.7, ease: "power3.out" }, S3 + 0.2);
tl.fromTo("#s3-sub", { opacity: 0 }, { opacity: 1, duration: 0.6 }, S3 + 0.7);
tl.fromTo("#s3-track", { x: 1920 }, { x: -1520, duration: 7.4, ease: "power1.inOut" }, S3 + 0.6);
tl.fromTo(".ev", { opacity: 0.25, scale: 0.92 }, { opacity: 1, scale: 1, duration: 0.5, stagger: 0.55, ease: "power2.out" }, S3 + 1.2);
tl.fromTo("#s3-verify", { opacity: 0, y: 20 }, { opacity: 1, y: 0, duration: 0.6, ease: "power3.out" }, S3 + 4.2);
tl.to("#s3-in", { opacity: 0, duration: 0.5, ease: "power2.in" }, S3 + 7.9);
// ---------- S4 ----------
const S4 = 21.6;
tl.fromTo("#s4-k", { opacity: 0, y: 16 }, { opacity: 1, y: 0, duration: 0.5, ease: "power3.out" }, S4 + 0.1);
tl.fromTo("#s4-h", { opacity: 0, y: 30 }, { opacity: 1, y: 0, duration: 0.7, ease: "power3.out" }, S4 + 0.2);
tl.fromTo(".axis", { opacity: 0, x: -40 }, { opacity: 1, x: 0, duration: 0.5, stagger: 0.35, ease: "power3.out" }, S4 + 1.6);
tl.fromTo("#s4-punch", { opacity: 0, y: 20 }, { opacity: 1, y: 0, duration: 0.6, ease: "power3.out" }, S4 + 3.4);
tl.fromTo("#s4-label", { opacity: 0 }, { opacity: 1, duration: 0.6 }, S4 + 5.2);
// клетки собираются из «взрыва» в куб
const cells = gsap.utils.toArray(".cell");
cells.forEach((c, i) => {
const x = (+c.dataset.x - 1.5) * STEP, y = (+c.dataset.y - 1.5) * STEP, z = (+c.dataset.z - 1.5) * STEP;
const k = 3.2;
tl.fromTo(c, { x: x * k, y: y * k, z: z * k, opacity: 0 }, { x, y, z, opacity: 1, duration: 1.3, ease: "power3.out" }, S4 + 0.4 + (i % 16) * 0.035);
});
tl.fromTo("#s4-cube", { rotationX: -30, rotationY: -50, rotationZ: 0 }, { rotationX: -18, rotationY: 38, rotationZ: 0, duration: 8.6, ease: "sine.inOut" }, S4 + 0.2);
// подсветка: срез круга «владелец», затем «остальные»
tl.to(".cell:not(.c0)", { opacity: 0.18, duration: 0.5 }, S4 + 4.2);
tl.to(".cell:not(.c0)", { opacity: 1, duration: 0.5 }, S4 + 5.6);
tl.to(".cell:not(.c3)", { opacity: 0.18, duration: 0.5 }, S4 + 6.1);
tl.to(".cell:not(.c3)", { opacity: 1, duration: 0.5 }, S4 + 7.6);
tl.to("#s4-text, #s4-stage", { opacity: 0, duration: 0.5, ease: "power2.in" }, S4 + 8.5);
// ---------- S5 ----------
const S5 = 30.6;
tl.fromTo("#s5-title", { opacity: 0, y: 24 }, { opacity: 1, y: 0, duration: 0.6, ease: "power3.out" }, S5 + 0.1);
tl.fromTo("#s5-core", { scale: 0.4, opacity: 0 }, { scale: 1, opacity: 1, duration: 0.8, ease: "back.out(1.6)" }, S5 + 0.3);
tl.fromTo("#s5-ring", { scale: 0.6, opacity: 0, rotation: 0 }, { scale: 1, opacity: 1, rotation: 90, duration: 6.4, ease: "power1.out" }, S5 + 0.4);
document.querySelectorAll(".s5l").forEach((p) => { const L = p.getTotalLength(); p.style.strokeDasharray = L; p.style.strokeDashoffset = L; });
tl.to(".s5l", { strokeDashoffset: 0, duration: 0.6, stagger: 0.18, ease: "power2.out" }, S5 + 0.9);
tl.fromTo(".node", { opacity: 0, scale: 0.85 }, { opacity: 1, scale: 1, duration: 0.55, stagger: 0.18, ease: "back.out(1.4)" }, S5 + 1.2);
tl.fromTo("#s5-core", { boxShadow: "0 0 0px rgba(92,255,176,0)" }, { boxShadow: "0 0 90px rgba(92,255,176,0.55)", duration: 0.8, yoyo: true, repeat: 4, ease: "sine.inOut" }, S5 + 1.4);
tl.to("#s5-stage", { opacity: 0, duration: 0.5, ease: "power2.in" }, S5 + 6.5);
// ---------- S6 ----------
const S6 = 37.6;
tl.fromTo("#s6-k", { opacity: 0, y: 16 }, { opacity: 1, y: 0, duration: 0.5, ease: "power3.out" }, S6 + 0.1);
tl.fromTo("#s6-h", { opacity: 0, y: 30 }, { opacity: 1, y: 0, duration: 0.7, ease: "power3.out" }, S6 + 0.2);
const line = document.getElementById("s6-line");
const lineLen = line.getTotalLength();
tl.fromTo(line, { strokeDasharray: lineLen, strokeDashoffset: lineLen }, { strokeDashoffset: 0, duration: 2.4, ease: "power2.inOut" }, S6 + 0.6);
tl.fromTo("#s6-area", { opacity: 0 }, { opacity: 1, duration: 1.6 }, S6 + 1.4);
tl.fromTo("#s6-dot", { scale: 0, transformOrigin: "50% 50%" }, { scale: 1, duration: 0.5, ease: "back.out(2)" }, S6 + 2.9);
tl.fromTo("#s6-num, #s6-num-cap", { opacity: 0 }, { opacity: 1, duration: 0.4 }, S6 + 0.8);
const val = { v: 0 };
const valEl = document.getElementById("s6-val");
tl.fromTo(val, { v: 0 }, { v: 1, duration: 2.2, ease: "power2.out", onUpdate: () => { valEl.textContent = "+" + val.v.toFixed(1).replace(".0", "") + "%"; } }, S6 + 0.8);
tl.fromTo("#s6-q", { opacity: 0, x: -30 }, { opacity: 1, x: 0, duration: 0.6, ease: "power3.out" }, S6 + 2.8);
tl.to("#s6-left, #s6-chart", { opacity: 0, duration: 0.5, ease: "power2.in" }, S6 + 5.9);
// ---------- S7 ----------
const S7 = 44;
tl.fromTo("#s7-logo", { opacity: 0, scale: 0.92 }, { opacity: 1, scale: 1, duration: 1, ease: "power3.out" }, S7 + 0.1);
tl.fromTo("#s7-term", { opacity: 0, y: 30 }, { opacity: 1, y: 0, duration: 0.6, ease: "power3.out" }, S7 + 0.7);
tl.to("#s7-cmd .c", { opacity: 1, duration: 0.01, stagger: 0.035 }, S7 + 1.2);
tl.fromTo("#s7-caret", { opacity: 1 }, { opacity: 0, duration: 0.4, yoyo: true, repeat: 10, ease: "steps(1)" }, S7 + 1.2);
tl.fromTo("#s7-foot", { opacity: 0 }, { opacity: 1, duration: 0.8 }, S7 + 3.4);
window.__timelines["main"] = tl;
</script>
</body>
</html>

View file

@ -0,0 +1,5 @@
{
"id": "simbiot-promo",
"name": "simbiot-promo",
"createdAt": "2026-10-17T10:55:57.466Z"
}

View file

@ -0,0 +1,11 @@
{
"name": "simbiot-promo",
"private": true,
"type": "module",
"scripts": {
"dev": "npx --yes hyperframes@0.8.168 preview",
"check": "npx --yes hyperframes@0.8.168 check",
"render": "npx --yes hyperframes@0.8.168 render",
"publish": "npx --yes hyperframes@0.8.168 publish"
}
}