diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index b55992e..1a094bb 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -2,6 +2,8 @@ name: Deploy to GitHub Pages on: push: branches: [main] + pull_request: + branches: [main] workflow_dispatch: permissions: contents: read @@ -15,36 +17,22 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v7 - - uses: actions/checkout@v7 - with: - repository: SagaSmithAI/Sagasmith-dnd - # Production inputs stay reproducible. Bump this revision only after - # the D&D workbench CI passes at the replacement commit. - ref: 25ee1ce4994f1950d6febd50145d364dbf621dbe - path: sagasmith-dnd - uses: actions/setup-node@v7 with: node-version: 22 cache: npm - cache-dependency-path: | - package-lock.json - sagasmith-dnd/package-lock.json + cache-dependency-path: package-lock.json - run: npm ci + - run: npm run check:content - run: npm run build - - run: npm ci - working-directory: sagasmith-dnd - - run: npm run build:ui - working-directory: sagasmith-dnd - env: - SAGASMITH_UI_BASE: /dnd-ui - SAGASMITH_UI_SITE: https://SagaSmithAI.github.io - PUBLIC_SAGASMITH_LIBRARY_URL: https://SagaSmithAI.github.io/content-library/index.json - - run: cp -R sagasmith-dnd/apps/ui/dist dist/dnd-ui + - run: npm run check:site - uses: actions/upload-pages-artifact@v5 + if: github.event_name != 'pull_request' with: path: dist/ deploy: needs: build + if: github.event_name != 'pull_request' runs-on: ubuntu-latest environment: name: github-pages diff --git a/AGENTS.md b/AGENTS.md index ff3954d..6960c1a 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -42,6 +42,10 @@ accurate historical references. when the public topology changes. - Keep `/start` commands synchronized with the current `SagaSmith-agent` Local Kit manifest, CLI, transport contract, and credential-free templates. +- Keep `/security`, `/privacy`, `/updates`, the sitemap, manifest, README, and + Pages workflow aligned with the public routes and data boundaries. +- GitHub Pages publishes this repository's static `dist/` only. Do not bundle a + domain Workbench or treat another repository as a website release input. ## Development @@ -55,5 +59,5 @@ npm run preview ``` Use the repository scripts as the source of truth. A topology change is not -complete until `src/pages/developers.astro` and the relevant README/profile/news -entry agree. +complete until `src/lib/site.ts`, `src/pages/developers.astro`, and the relevant +README/profile/news entry agree. diff --git a/README.md b/README.md index 73dc29b..e3a3ab3 100644 --- a/README.md +++ b/README.md @@ -1,112 +1,87 @@ # SagaSmithAI Website -[Live site](https://sagasmithai.github.io) · [Platform overview](https://github.com/SagaSmithAI/.github/blob/main/profile/README.md) · [SagaSmith Web](https://github.com/SagaSmithAI/SagaSmith-Web) · [Content catalog](https://github.com/SagaSmithAI/SagaSmith-dnd-content-library) +[Live site](https://sagasmithai.github.io) · [Platform overview](https://github.com/SagaSmithAI/.github/blob/main/profile/README.md) · [SagaSmith Web](https://github.com/SagaSmithAI/SagaSmith-Web) · [Local Agent Kit](https://github.com/SagaSmithAI/SagaSmith-agent) -The source for [sagasmithai.github.io](https://sagasmithai.github.io): the public home of the AI-native TTRPG platform. +The bilingual static organization site for SagaSmithAI, an AI-native TTRPG platform. It is a public product and developer entry surface, not a runtime dashboard, hosted campaign service, or replacement for component documentation. -## What the site communicates +## Information architecture -- fully autonomous game mastering as the primary player-facing experience; -- human-DM collaboration as a supported second hosting mode; -- persistent choices, actor-scoped knowledge, and clear table boundaries; -- honest D&D and CoC experience status; -- extended ruleset import as an explicitly experimental capability; -- checksum-validated unified core-rule, addon, module, and preset packages; -- the public-source hosted [SagaSmith Web](https://github.com/SagaSmithAI/SagaSmith-Web), - while keeping game-state authority in system MCPs; -- Local Agent Kit and Hosted Web as two deployments of the same authoritative MCP contract; -- the public, rights-aware - [Content Pack repository](https://github.com/SagaSmithAI/SagaSmith-dnd-content-library) - and checksum catalog, without treating repository visibility as a content license; -- a separate developer path for architecture, repositories, and ownership boundaries; -- short public-facing platform updates. +| Route | Purpose | +|---|---| +| `/` | Product thesis, Local/Hosted contract map, experience boundaries, maturity, and recent progress | +| `/start` | Current release-locked Local Kit install, MCP-host templates, transports, and SagaSmith Web development entry | +| `/developers` | Authoritative contract, ownership boundaries, and current repository topology | +| `/library` | Current Content Pack inventory and integrity/authority/rights boundaries | +| `/updates` | Dated public progress summaries with explicit evidence limits | +| `/security` | Private vulnerability-reporting routes and sensitive-data guidance | +| `/privacy` | Privacy notice for this static GitHub Pages site | +| `/404` | Bilingual not-found recovery page | -The site is bilingual (Chinese/English), static, and deliberately contains no runtime dashboard or user campaign data. +Chinese is the default language. The language control reveals complete English copy on the same static routes and stores only `sagasmith-language` in browser `localStorage`. ## Current repository map | Layer | Current repository | |---|---| -| Neutral runtime | [`sagasmith-core`](https://github.com/SagaSmithAI/sagasmith-core) | -| D&D Domain / MCP / Skills / UI | [`sagasmith-dnd`](https://github.com/SagaSmithAI/sagasmith-dnd) | -| CoC Domain / MCP / Skills / UI | [`sagasmith-coc`](https://github.com/SagaSmithAI/sagasmith-coc) | +| Agent host and Local Agent Kit | [`SagaSmith-agent`](https://github.com/SagaSmithAI/SagaSmith-agent) | +| Hosted browser product | [`SagaSmith-Web`](https://github.com/SagaSmithAI/SagaSmith-Web) | +| System-neutral runtime | [`Sagasmith-core`](https://github.com/SagaSmithAI/Sagasmith-core) | +| D&D Domain / MCP / Skills / UI | [`Sagasmith-dnd`](https://github.com/SagaSmithAI/Sagasmith-dnd) | +| CoC Domain / MCP / Skills / UI | [`Sagasmith-coc`](https://github.com/SagaSmithAI/Sagasmith-coc) | | Narrative Domain / MCP / Skills | [`sagasmith-narrative`](https://github.com/SagaSmithAI/sagasmith-narrative) | -| Agent host | [`SagaSmith-agent`](https://github.com/SagaSmithAI/SagaSmith-agent) | -| Hosted Web product | [SagaSmith Web (`SagaSmith-Web`)](https://github.com/SagaSmithAI/SagaSmith-Web) | -| Content catalog | [`SagaSmith-dnd-content-library`](https://github.com/SagaSmithAI/SagaSmith-dnd-content-library) | +| Rights-aware Content Pack catalog | [`SagaSmith-dnd-content-library`](https://github.com/SagaSmithAI/SagaSmith-dnd-content-library) | +| Organization profile and community policy | [`.github`](https://github.com/SagaSmithAI/.github) | -Former standalone MCP, Skills, UI, and generic Module Generator repositories -are archived history, do not accept new issues, and are not current documentation, -release inputs, or compatibility fallbacks. Their landing READMEs should point to the matching -vertical repository. +Former standalone MCP, Skills, UI, and generic Module Generator repositories are archived read-only history. They are not current documentation, release inputs, producers, consumers, or compatibility fallbacks. -## Verified integration baseline +## Product boundaries -The public status published on 2026-08-20 is backed by a current-source hosted -run: SagaSmith Web-signed principal context reached session-scoped Agent/MCP tools, -and the D&D and CoC reference campaigns completed concurrently without a -reported regression gap. The D&D path recorded a legal ending. Catalog-wide -discovery and exclusions remain machine-readable; the site must not describe -this reference run as complete playthrough coverage for every Pack or branch. +- SagaSmith Local Agent Kit and SagaSmith Web are two deployments of `sagasmith.authoritative-mcp/v1`. +- Transport, authentication, storage, and deployment may differ. Handlers, tool schemas, errors, authority, revisions, idempotency, and rule-write semantics may not. +- SagaSmith Web owns accounts, sessions, quota, collaboration, Forge, Module Studio, hosted orchestration, and cloud projections. Domain MCPs remain authoritative for game state. +- Agent and Skills may interpret, facilitate, and propose. Domain runtimes and MCP settle deterministic rules and authoritative writes. +- `sagasmith.content-package` v2 Packs do not carry campaign permissions, ActorKnowledge, progress, random streams, branches, or snapshots. +- Public repository or catalog visibility is not a content license. Every Pack, source, image, map, font, and derived asset retains its own rights requirements. +- Extended ruleset import is Experimental and does not imply arbitrary unadapted rulebooks can execute safely. -## Development +## Development and validation Requires Node.js 22.12+. ```bash -npm install -npm run dev +npm ci +npm run check:content npm run build -npm run preview +npm run check:site +npm run check:external ``` -The Pages workflow publishes `/dnd-ui` from `apps/ui` in the current -[`sagasmith-dnd`](https://github.com/SagaSmithAI/Sagasmith-dnd) vertical -repository. That external release input is pinned to a full commit SHA in -`.github/workflows/deploy.yml` and is advanced only after the replacement -revision passes the D&D workbench CI. +`npm run check` runs the deterministic content/topology checks, production build, and built-site metadata/internal-link/asset-budget checks. `check:external` performs the network-dependent external-link pass separately. + +The GitHub Pages workflow builds only this repository and publishes `dist/`. It has no cross-repository release input or bundled Workbench. Pull requests run the same deterministic checks; deployment occurs only from `main` or an explicit workflow dispatch. + +## Assets and visual source -Main files: +- `public/logo-wordmark.png`, `public/logo-mark.png`, and the original favicon path are first-party brand assets already committed to this Apache-2.0 website in PR #9. They remain covered by this repository's `LICENSE` and `NOTICE`. +- `public/og.png` is rendered at 1200×630 from the site-owned, reviewable `src/assets/og-source.svg`; it uses only inline vectors, text, and the documented site tokens. +- `public/favicon.svg` is a site-owned vector mark adapted from the existing website favicon and recolored to the current product palette. `public/icon-512.png` is rendered from the reviewable `src/assets/icon-source.svg` for exact manifest and Apple touch dimensions. +- The visual token names and interaction patterns are aligned with the current SagaSmith Web product language: void `#0c0d0d`, forge panel `#151716`, iron line `#2a2d2b`, vellum ink `#eee9dc`, forge gold `#d9ad5b`, and moss state `#718d6a`. +- SagaSmith Web is proprietary. No SagaSmith Web source file, texture, icon, private implementation, model, cache, or generated workflow is copied into this static repository. +- The site loads no remote font, analytics, image CDN, or third-party visual asset at runtime. + +## Main sources ```text -src/pages/index.astro DM/player homepage and autonomous-hosting story -src/pages/start.astro Executable Local Kit, MCP-client, and Hosted Web entry paths -src/pages/developers.astro Developer architecture and repository map -src/pages/library.astro Rights-aware public Content Pack catalog frame -src/layouts/SiteLayout.astro Shared metadata, navigation, language, and footer -src/styles/site.css Shared responsive visual system -src/lib/news.ts Local field-note loader -news/ Dated Markdown field notes -public/ Logo, favicons, and social preview image +src/layouts/SiteLayout.astro Shared metadata, bilingual navigation, accessibility, and footer +src/lib/site.ts Current repository, system-status, and Local profile data +src/lib/news.ts Local dated-news loader +src/pages/ Every public route, including privacy, security, updates, and 404 +src/styles/site.css Shared responsive visual system and reduced-motion handling +news/ Dated historical progress sources +scripts/ Content, built-site, and external-link checks +public/ First-party brand assets, manifest, sitemap, and robots policy ``` -## Content rules - -- Describe SagaSmithAI as an **AI-native TTRPG platform** with complete autonomous - hosting as its primary end-user experience. -- Keep DM and player outcomes ahead of implementation details on the homepage. -- Present human-DM collaboration as a supported mode, not the only mode. -- Use the D&D-style fantasy d20 reference path for primary visual storytelling - while CoC remains less mature; keep CoC in clearly labeled system-status areas. -- Present D&D as the end-to-end reference path and label CoC/UI maturity honestly. -- Always label extended ruleset import as experimental; never imply arbitrary - rulebooks or unadapted third-party systems can be imported and run reliably. -- Keep domain state out of the website; link to MCP/Agent setup instead. -- Describe SagaSmith Web as the full hosted browser product, with its control plane as one backend - responsibility, never as the owner of D&D, CoC, or Narrative campaign state. -- Keep the Local Agent Kit independent from SagaSmith Web and require both deployments to preserve - the same MCP schemas, capabilities, errors, authority, revision, and idempotency behavior. -- Do not claim bundled access to commercial rulebooks or modules. -- Treat repository visibility and content licensing as separate facts. Every - public catalog entry still requires its own license and distribution rights. -- Distinguish unified content packages from campaign saves: no permissions, - ActorKnowledge, progress, random stream, branches, or Snapshots migrate with them. -- Describe rule-package imports as validated inactive storage; branch activation - remains a separate Owner/DM operation and never implies source authority. -- Update the organization Profile and platform README when the platform map changes. -- Tie verification claims to dated, machine-readable regression evidence and keep - unexecuted modules or paths explicit. - ## License -Apache-2.0 +Apache-2.0. See [`NOTICE`](NOTICE). Repository visibility and this website license do not grant rights to separately licensed Content Packs or SagaSmith Web source. diff --git a/news/2026-07-15-coc-mcp-vertical.md b/news/2026-07-15-coc-mcp-vertical.md index 5b1e488..f2dd9c8 100644 --- a/news/2026-07-15-coc-mcp-vertical.md +++ b/news/2026-07-15-coc-mcp-vertical.md @@ -7,6 +7,6 @@ summary: CoC 7e 已能保存调查进度、角色各自掌握的线索与战役 summaryEn: CoC 7e can now preserve investigation progress, character-specific clues, and campaign state while the experience remains in testing. --- -`SagaSmith-coc-mcp` 让 CoC Agent 不再依赖 CLI 拼接或复制 D&D 工具表。真实 stdio 客户端测试验证了目录级发现、按 session 加载和 `exposure_call` fallback;多人测试验证了 PC 知识互相隔离,并在重启后保留战役、模组、Snapshot 与知识状态。 +当时的 `SagaSmith-coc-mcp`(现已归档,能力已收敛到 `Sagasmith-coc` vertical)让 CoC Agent 不再依赖 CLI 拼接或复制 D&D 工具表。真实 stdio 客户端测试验证了目录级发现、按 session 加载和 `exposure_call` fallback;多人测试验证了 PC 知识互相隔离,并在重启后保留战役、模组、Snapshot 与知识状态。 -`SagaSmith-coc-mcp` removes the need for CoC agents to compose CLI commands or copy the D&D tool catalogue. A real stdio client test covers discovery, per-session loading, and the `exposure_call` fallback, while the multiplayer vertical test preserves campaign, scenario, snapshot, and actor-knowledge state across restart. +The then-current `SagaSmith-coc-mcp` repository—now archived with its capabilities consolidated into the `Sagasmith-coc` vertical—removed the need for CoC agents to compose CLI commands or copy the D&D tool catalogue. A real stdio client test covered discovery, per-session loading, and the `exposure_call` fallback, while the multiplayer vertical test preserved campaign, scenario, snapshot, and actor-knowledge state across restart. diff --git a/news/2026-08-18-public-service-and-content-catalog.md b/news/2026-08-18-public-service-and-content-catalog.md index 41dae28..06c8be0 100644 --- a/news/2026-08-18-public-service-and-content-catalog.md +++ b/news/2026-08-18-public-service-and-content-catalog.md @@ -7,7 +7,7 @@ summary: SagaSmith Web、D&D/CoC 多系统房间与当前 46 个 Content Pack summaryEn: SagaSmith Web, hosted D&D/CoC rooms, and the checksum catalog for 46 current Content Packs are now publicly visible with explicit state and content-rights boundaries. --- -`SagaSmith-service` 仓库中的 SagaSmith Web 现在公开展示托管账户、配额、战役房间、主持身份、Agent +当时名为 `SagaSmith-service`、现为 `SagaSmith-Web` 的 SagaSmith Web 仓库公开展示托管账户、配额、战役房间、主持身份、Agent 调度、浏览器前端与多系统 MCP 编排。SagaSmith Web 不拥有 D&D 或 CoC 的权威游戏状态; 每一次领域操作仍交给对应 MCP 重新做权限、revision、阶段和角色范围校验。 diff --git a/news/2026-08-19-domain-monorepos.md b/news/2026-08-19-domain-monorepos.md index 37e1bee..8cedb66 100644 --- a/news/2026-08-19-domain-monorepos.md +++ b/news/2026-08-19-domain-monorepos.md @@ -16,6 +16,6 @@ Skills、领域 UI(如有)和对应的 Pack/项目创作流程,同时继 审计,但不再接收当前 Issue、发布或集成,也不会被 Agent、SagaSmith Web 或安装器作为 兼容回退。 -`sagasmith-core`、`SagaSmith-agent`、`SagaSmith-service`、内容目录、官网与组织文档 +`sagasmith-core`、`SagaSmith-agent`、`SagaSmith-Web`(当时名为 `SagaSmith-service`)、内容目录、官网与组织文档 继续独立。开发者页、活跃仓库 README 与 SagaSmith Web 组件锁只把当前 vertical 仓库作为发布和集成输入;归档仓库的入口文档应仅重定向到对应 vertical 仓库。 diff --git a/news/2026-08-20-current-runtime-regression.md b/news/2026-08-20-current-runtime-regression.md index f41b6f0..bbc0356 100644 --- a/news/2026-08-20-current-runtime-regression.md +++ b/news/2026-08-20-current-runtime-regression.md @@ -8,7 +8,7 @@ summaryEn: The long regression now rebuilds the hosted stack from the current Ag --- 长回归会先从当前 `SagaSmith-agent`、`sagasmith-core`、三个领域仓库与 -`SagaSmith-service` 源码重建并重建容器,再开始任何房间动作。SagaSmith Web 使用临时共享 +`SagaSmith-Web`(当时名为 `SagaSmith-service`)源码重建并重建容器,再开始任何房间动作。SagaSmith Web 使用临时共享 密钥签发带时效的 `sagasmith.auth-context/v1`,Agent 将 principal context 传给 会话作用域 MCP;领域服务仍在实际调用边界重新校验身份、角色、战役与 revision。 diff --git a/package.json b/package.json index 47e53ed..e202960 100644 --- a/package.json +++ b/package.json @@ -10,7 +10,11 @@ "dev": "astro dev", "build": "astro build", "preview": "astro preview", - "astro": "astro" + "astro": "astro", + "check:content": "node scripts/check-content.mjs", + "check:site": "node scripts/check-site.mjs", + "check:external": "node scripts/check-external-links.mjs", + "check": "npm run check:content && npm run build && npm run check:site" }, "dependencies": { "astro": "^7.2.8" diff --git a/public/favicon.svg b/public/favicon.svg index bf1f537..2f22e02 100644 --- a/public/favicon.svg +++ b/public/favicon.svg @@ -1,6 +1,6 @@ - - - - + + + + diff --git a/public/icon-512.png b/public/icon-512.png new file mode 100644 index 0000000..d08aa76 Binary files /dev/null and b/public/icon-512.png differ diff --git a/public/og.png b/public/og.png index 4185d28..73b5d2c 100644 Binary files a/public/og.png and b/public/og.png differ diff --git a/public/site.webmanifest b/public/site.webmanifest new file mode 100644 index 0000000..27124c2 --- /dev/null +++ b/public/site.webmanifest @@ -0,0 +1,25 @@ +{ + "name": "SagaSmithAI", + "short_name": "SagaSmith", + "description": "AI-native TTRPG platform with one authoritative MCP contract across local and hosted deployments.", + "lang": "zh-CN", + "start_url": "/", + "scope": "/", + "display": "standalone", + "background_color": "#0c0d0d", + "theme_color": "#0c0d0d", + "icons": [ + { + "src": "/favicon.svg", + "sizes": "any", + "type": "image/svg+xml", + "purpose": "any" + }, + { + "src": "/icon-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any" + } + ] +} diff --git a/public/sitemap.xml b/public/sitemap.xml index a50cae0..d6f48dc 100644 --- a/public/sitemap.xml +++ b/public/sitemap.xml @@ -1,7 +1,10 @@ - https://sagasmithai.github.io/ - https://sagasmithai.github.io/start - https://sagasmithai.github.io/developers - https://sagasmithai.github.io/library + https://sagasmithai.github.io/1.0 + https://sagasmithai.github.io/start0.9 + https://sagasmithai.github.io/developers0.9 + https://sagasmithai.github.io/library0.8 + https://sagasmithai.github.io/updates0.7 + https://sagasmithai.github.io/security0.6 + https://sagasmithai.github.io/privacy0.5 diff --git a/scripts/check-content.mjs b/scripts/check-content.mjs new file mode 100644 index 0000000..b7dd6e0 --- /dev/null +++ b/scripts/check-content.mjs @@ -0,0 +1,81 @@ +import { readFile, readdir } from 'node:fs/promises'; +import path from 'node:path'; + +const root = process.cwd(); +const failures = []; +const requiredPages = ['index', 'start', 'developers', 'library', 'updates', 'security', 'privacy', '404']; +const currentRepos = [ + 'SagaSmith-agent', + 'SagaSmith-Web', + 'Sagasmith-core', + 'Sagasmith-dnd', + 'Sagasmith-coc', + 'sagasmith-narrative', + 'SagaSmith-dnd-content-library', +]; +const legacyNames = [ + 'SagaSmith-service', + 'SagaSmith-dnd-mcp', + 'SagaSmith-dnd-skills', + 'sagasmith-dnd-ui', + 'SagaSmith-coc-mcp', + 'SagaSmith-coc-skills', + 'sagasmith-coc-ui', + 'SagaSmith-narrative-mcp', + 'SagaSmith-narrative-skills', + 'SagaSmith-module-gen-skills', + 'sagasmith-ui', +]; + +const read = (file) => readFile(path.join(root, file), 'utf8'); + +for (const page of requiredPages) { + const file = `src/pages/${page}.astro`; + try { + const source = await read(file); + if (!source.includes(' file.endsWith('.astro')).map((file) => `src/pages/${file}`), + 'src/layouts/SiteLayout.astro', + 'src/lib/site.ts', + 'README.md', + 'AGENTS.md', + '.github/workflows/deploy.yml', +]; +const authoritativeText = (await Promise.all(authoritativeFiles.map(read))).join('\n'); + +for (const legacy of legacyNames) { + if (authoritativeText.includes(legacy)) failures.push(`authoritative surfaces still name legacy repository: ${legacy}`); +} +for (const repo of currentRepos) { + if (!authoritativeText.includes(repo)) failures.push(`current repository missing from authoritative surfaces: ${repo}`); +} + +const workflow = await read('.github/workflows/deploy.yml'); +if (/repository:\s+SagaSmithAI\//.test(workflow)) failures.push('Pages workflow still checks out an external release input'); +if (workflow.includes('/dnd-ui') || workflow.includes('build:ui')) failures.push('Pages workflow still bundles a domain Workbench'); + +const manifest = JSON.parse(await read('public/site.webmanifest')); +if (manifest.start_url !== '/' || manifest.scope !== '/') failures.push('manifest start_url and scope must both be /'); +if (!manifest.icons?.some((icon) => icon.src === '/favicon.svg')) failures.push('manifest is missing the SVG icon'); + +const sitemap = await read('public/sitemap.xml'); +for (const page of requiredPages.filter((page) => page !== '404' && page !== 'index')) { + if (!sitemap.includes(`/${page}`)) failures.push(`sitemap missing /${page}`); +} + +if (failures.length) { + console.error(`Content checks failed (${failures.length}):`); + failures.forEach((failure) => console.error(`- ${failure}`)); + process.exit(1); +} + +console.log(`Content checks passed: ${requiredPages.length} bilingual routes, current topology, manifest, sitemap, and Pages source.`); diff --git a/scripts/check-external-links.mjs b/scripts/check-external-links.mjs new file mode 100644 index 0000000..e415760 --- /dev/null +++ b/scripts/check-external-links.mjs @@ -0,0 +1,71 @@ +import { readFile, readdir } from 'node:fs/promises'; +import path from 'node:path'; + +const dist = path.join(process.cwd(), 'dist'); + +async function walk(directory) { + const files = []; + for (const entry of await readdir(directory, { withFileTypes: true })) { + const full = path.join(directory, entry.name); + if (entry.isDirectory()) files.push(...await walk(full)); + else files.push(full); + } + return files; +} + +const htmlFiles = (await walk(dist)).filter((file) => file.endsWith('.html')); +const links = new Set(); +for (const file of htmlFiles) { + const html = await readFile(file, 'utf8'); + for (const match of html.matchAll(/\shref="(https?:\/\/[^"]+)"/g)) { + const url = new URL(match[1].replaceAll('&', '&')); + if (url.hostname === 'sagasmithai.github.io') continue; + links.add(url.toString()); + } +} + +const failures = []; +const warnings = []; +const queue = [...links]; + +async function check(url) { + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), 20_000); + try { + let response = await fetch(url, { + method: 'HEAD', + redirect: 'follow', + signal: controller.signal, + headers: { 'user-agent': 'SagaSmithAI-site-link-check/1.0' }, + }); + if (response.status === 405) { + response = await fetch(url, { + method: 'GET', + redirect: 'follow', + signal: controller.signal, + headers: { 'user-agent': 'SagaSmithAI-site-link-check/1.0', range: 'bytes=0-0' }, + }); + } + if (response.status === 404 || response.status === 410) failures.push(`${response.status} ${url}`); + else if (!response.ok && response.status !== 403 && response.status !== 429) failures.push(`${response.status} ${url}`); + else if (!response.ok) warnings.push(`${response.status} ${url}`); + } catch (error) { + failures.push(`${error.name}: ${url}`); + } finally { + clearTimeout(timeout); + } +} + +const workers = Array.from({ length: Math.min(4, queue.length) }, async () => { + while (queue.length) await check(queue.shift()); +}); +await Promise.all(workers); + +warnings.forEach((warning) => console.warn(`Warning: ${warning}`)); +if (failures.length) { + console.error(`External-link checks failed (${failures.length}):`); + failures.forEach((failure) => console.error(`- ${failure}`)); + process.exit(1); +} + +console.log(`External-link checks passed: ${links.size} unique URLs (${warnings.length} access-limited warnings).`); diff --git a/scripts/check-site.mjs b/scripts/check-site.mjs new file mode 100644 index 0000000..57b4845 --- /dev/null +++ b/scripts/check-site.mjs @@ -0,0 +1,92 @@ +import { readFile, readdir, stat } from 'node:fs/promises'; +import path from 'node:path'; + +const root = process.cwd(); +const dist = path.join(root, 'dist'); +const failures = []; + +async function walk(directory) { + const files = []; + for (const entry of await readdir(directory, { withFileTypes: true })) { + const full = path.join(directory, entry.name); + if (entry.isDirectory()) files.push(...await walk(full)); + else files.push(full); + } + return files; +} + +const files = await walk(dist); +const htmlFiles = files.filter((file) => file.endsWith('.html')); +const byPath = new Set(files.map((file) => path.relative(dist, file).replaceAll('\\', '/'))); +const pageAnchors = new Map(); + +for (const file of htmlFiles) { + const html = await readFile(file, 'utf8'); + const relative = path.relative(dist, file).replaceAll('\\', '/'); + const required = [ + /]+lang=/, + /[^<]+<\/title>/, + /<meta name="description" content="[^"]+"/, + /<link rel="canonical" href="https:\/\/sagasmithai\.github\.io/, + /<meta property="og:image"/, + /<link rel="manifest" href="\/site\.webmanifest"/, + /<link rel="icon"/, + /<h1[\s>]/, + /class="zh"/, + /class="en"/, + ]; + required.forEach((pattern) => { + if (!pattern.test(html)) failures.push(`${relative}: missing ${pattern}`); + }); + if ((html.match(/<h1[\s>]/g) || []).length !== 1) failures.push(`${relative}: expected exactly one h1`); + if (relative === 'index.html' && (html.match(/class="news-card"/g) || []).length !== 3) failures.push(`${relative}: expected three current update cards`); + if (relative === 'updates/index.html' && (html.match(/data-update/g) || []).length < 7) failures.push(`${relative}: expected the complete dated update list`); + pageAnchors.set(relative, new Set([...html.matchAll(/\sid="([^"]+)"/g)].map((match) => match[1]))); +} + +function pageFileFor(pathname) { + const clean = pathname.replace(/^\//, '').replace(/\/$/, ''); + if (!clean) return 'index.html'; + if (path.extname(clean)) return clean; + return `${clean}/index.html`; +} + +for (const file of htmlFiles) { + const html = await readFile(file, 'utf8'); + const relative = path.relative(dist, file).replaceAll('\\', '/'); + for (const match of html.matchAll(/\s(?:href|src)="([^"]+)"/g)) { + const value = match[1]; + if (/^(?:https?:|mailto:|tel:|data:|javascript:)/.test(value)) continue; + const [rawPath, hash] = value.split('#'); + const currentRoute = `/${relative.replace(/index\.html$/, '')}`; + const resolved = new URL(rawPath || currentRoute, `https://sagasmithai.github.io${currentRoute}`); + const target = pageFileFor(resolved.pathname); + if (!byPath.has(target)) { + failures.push(`${relative}: broken internal reference ${value} -> ${target}`); + continue; + } + if (hash && target.endsWith('.html') && !pageAnchors.get(target)?.has(hash)) { + failures.push(`${relative}: missing anchor ${value}`); + } + } +} + +const budgets = [ + ['HTML', htmlFiles, 300_000], + ['CSS', files.filter((file) => file.endsWith('.css')), 160_000], + ['IMAGE', files.filter((file) => /\.(?:png|webp|jpg|jpeg|svg)$/.test(file)), 750_000], +]; +for (const [label, group, max] of budgets) { + for (const file of group) { + const size = (await stat(file)).size; + if (size > max) failures.push(`${path.relative(dist, file)}: ${label} asset ${size} bytes exceeds ${max}`); + } +} + +if (failures.length) { + console.error(`Built-site checks failed (${failures.length}):`); + failures.forEach((failure) => console.error(`- ${failure}`)); + process.exit(1); +} + +console.log(`Built-site checks passed: ${htmlFiles.length} HTML routes, internal links/anchors, metadata, bilingual markup, and asset budgets.`); diff --git a/src/assets/icon-source.svg b/src/assets/icon-source.svg new file mode 100644 index 0000000..c91b655 --- /dev/null +++ b/src/assets/icon-source.svg @@ -0,0 +1,6 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="512" height="512" viewBox="0 0 128 128" role="img" aria-label="SagaSmithAI"> + <rect width="128" height="128" rx="24" fill="#0c0d0d"/> + <path d="M35 28h57M35 100h57" stroke="#718d6a" stroke-width="5" stroke-linecap="round"/> + <path d="M88 35c-8-7-17-10-27-10-16 0-27 8-27 20 0 14 13 18 29 22 14 4 25 7 25 19 0 11-10 18-25 18-12 0-23-4-32-13" fill="none" stroke="#eee9dc" stroke-width="10" stroke-linecap="round" stroke-linejoin="round"/> + <path d="m87 21-10 21h13L78 67" fill="none" stroke="#d9ad5b" stroke-width="7" stroke-linecap="round" stroke-linejoin="round"/> +</svg> diff --git a/src/assets/og-source.svg b/src/assets/og-source.svg new file mode 100644 index 0000000..8d816a1 --- /dev/null +++ b/src/assets/og-source.svg @@ -0,0 +1,78 @@ +<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="630" viewBox="0 0 1200 630" role="img" aria-labelledby="title desc"> + <title id="title">SagaSmithAI — Worlds remember. Rules settle. + Local Agent Kit and SagaSmith Web connect to one authoritative MCP contract for D&D, Call of Cthulhu, and Narrative. + + + + + + + + + + + + + + + + + + SagaSmith + AI + ACTIVE ALPHA + + + + AI-NATIVE TTRPG PLATFORM + Worlds remember. + Rules settle. + 让世界记得,让规则落槌。 + + + + + DEPLOYMENT MAP / v1 + CONTRACT LOCKED + + + + + LOCAL / YOU OPERATE + Local Agent Kit + stdio · localhost HTTP · local data + + + HOSTED / WEB PRODUCT + SagaSmith Web + browser · API/BFF · Agent worker + + + + + + ONE AUTHORITATIVE CONTRACT + sagasmith.authoritative-mcp/v1 + schemas · errors · authority · revisions + + + + + + DOMAIN + D&D + DOMAIN + CoC + DOMAIN + Narrative + + + + LOCAL + HOSTED + ONE DOMAIN TRUTH + + + + sagasmithai.github.io + LOCAL · HOSTED · AUTHORITATIVE + diff --git a/src/layouts/SiteLayout.astro b/src/layouts/SiteLayout.astro index ac6c8de..02a8b35 100644 --- a/src/layouts/SiteLayout.astro +++ b/src/layouts/SiteLayout.astro @@ -3,28 +3,51 @@ import '../styles/site.css'; interface Props { title: string; + titleEn: string; description: string; - page?: 'home' | 'start' | 'developers' | 'library'; + descriptionEn: string; + page?: 'home' | 'start' | 'developers' | 'library' | 'updates' | 'security' | 'privacy' | '404'; + noindex?: boolean; } const { title, + titleEn, description, + descriptionEn, page = 'home', + noindex = false, } = Astro.props; const canonical = new URL(Astro.url.pathname, Astro.site); const socialImage = new URL('/og.png', Astro.site); -const homePrefix = page === 'home' ? '' : '/'; -const organizationSchema = { - '@context': 'https://schema.org', - '@type': 'Organization', - name: 'SagaSmithAI', - url: Astro.site?.toString(), - logo: new URL('/logo-mark.png', Astro.site).toString(), - sameAs: ['https://github.com/SagaSmithAI'], - description, -}; +const siteName = 'SagaSmithAI'; +const fullTitle = `${title} · ${siteName}`; +const navigation = [ + { id: 'start', href: '/start', zh: '开始使用', en: 'Get started' }, + { id: 'developers', href: '/developers', zh: '开发者', en: 'Developers' }, + { id: 'library', href: '/library', zh: '内容库', en: 'Library' }, + { id: 'updates', href: '/updates', zh: '进展', en: 'Updates' }, +]; +const schema = [ + { + '@context': 'https://schema.org', + '@type': 'Organization', + name: siteName, + url: Astro.site?.toString(), + logo: new URL('/logo-mark.png', Astro.site).toString(), + sameAs: ['https://github.com/SagaSmithAI'], + description, + }, + { + '@context': 'https://schema.org', + '@type': 'WebSite', + name: siteName, + alternateName: 'SagaSmith', + url: Astro.site?.toString(), + inLanguage: ['zh-CN', 'en'], + }, +]; --- @@ -32,83 +55,114 @@ const organizationSchema = { - + + - + + + + - + + + - + - - + + - - {title} - + + + {fullTitle} + - - - + + +
- SagaSmith + + SagaSmith + AI -
-
+
- diff --git a/src/lib/news.ts b/src/lib/news.ts index e97d6fc..05bf0e2 100644 --- a/src/lib/news.ts +++ b/src/lib/news.ts @@ -21,10 +21,10 @@ export async function loadNews(limit = 5): Promise { const items: NewsItem[] = []; for (const file of files) { const raw = await fs.readFile(path.join(dir, file), 'utf-8'); - const match = raw.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/); + const match = raw.match(/^---\r?\n([\s\S]*?)\r?\n---\r?\n([\s\S]*)$/); if (!match) continue; const meta: Record = {}; - match[1].split('\n').forEach(line => { + match[1].split(/\r?\n/).forEach(line => { const sep = line.indexOf(': '); if (sep > 0) meta[line.slice(0, sep).trim()] = cleanMeta(line.slice(sep + 2)); }); diff --git a/src/lib/site.ts b/src/lib/site.ts new file mode 100644 index 0000000..1a4b104 --- /dev/null +++ b/src/lib/site.ts @@ -0,0 +1,96 @@ +export const repositories = [ + { + key: 'agent', + name: 'SagaSmith-agent', + url: 'https://github.com/SagaSmithAI/SagaSmith-agent', + roleZh: 'Agent host、Channels、MCP consumer、Local Agent Kit 与 Hosted worker 基础', + roleEn: 'Agent host, channels, MCP consumer, Local Agent Kit, and the shared Hosted worker base', + }, + { + key: 'web', + name: 'SagaSmith-Web', + url: 'https://github.com/SagaSmithAI/SagaSmith-Web', + roleZh: '浏览器/PWA、API/BFF、托管控制面、协作、Forge、Module Studio 与运维', + roleEn: 'Browser/PWA, API/BFF, hosted control plane, collaboration, Forge, Module Studio, and operations', + }, + { + key: 'core', + name: 'Sagasmith-core', + url: 'https://github.com/SagaSmithAI/Sagasmith-core', + roleZh: '系统无关持久化、文档导入、检索、分支、知识与事务语义', + roleEn: 'System-neutral persistence, document ingestion, retrieval, branches, knowledge, and transactions', + }, + { + key: 'dnd', + name: 'Sagasmith-dnd', + url: 'https://github.com/SagaSmithAI/Sagasmith-dnd', + roleZh: 'D&D 5e Domain、权威 MCP、Skills、Workbench 与模组生成', + roleEn: 'D&D 5e Domain, authoritative MCP, Skills, Workbench, and module authoring', + }, + { + key: 'coc', + name: 'Sagasmith-coc', + url: 'https://github.com/SagaSmithAI/Sagasmith-coc', + roleZh: 'CoC 7e Domain、权威 MCP、Skills、Workbench 与模组生成', + roleEn: 'CoC 7e Domain, authoritative MCP, Skills, Workbench, and module authoring', + }, + { + key: 'narrative', + name: 'sagasmith-narrative', + url: 'https://github.com/SagaSmithAI/sagasmith-narrative', + roleZh: '系统无关 Narrative Domain、权威 MCP、Skills 与项目生成', + roleEn: 'System-neutral Narrative Domain, authoritative MCP, Skills, and project authoring', + }, + { + key: 'library', + name: 'SagaSmith-dnd-content-library', + url: 'https://github.com/SagaSmithAI/SagaSmith-dnd-content-library', + roleZh: '逐包许可约束的 Pack、来源/资产 blob 与 checksum 索引', + roleEn: 'Rights-aware Packs, source/asset blobs, and checksum index', + }, + { + key: 'profile', + name: '.github', + url: 'https://github.com/SagaSmithAI/.github', + roleZh: '组织 Profile、贡献指南、行为准则与公共安全入口', + roleEn: 'Organization profile, contribution guide, code of conduct, and public security entry point', + }, +] as const; + +export const systemStatus = [ + { + name: 'D&D 5e', + status: 'Early Alpha', + tone: 'live', + zh: '端到端参考路径:规则、内容、场景、角色知识、战斗与 Workbench。', + en: 'End-to-end reference path across rules, content, scenes, actor knowledge, combat, and Workbench.', + }, + { + name: 'Call of Cthulhu 7e', + status: 'In testing', + tone: 'testing', + zh: '调查、检定、SAN、追逐、角色知识边界与参考战役路径。', + en: 'Investigation, checks, SAN, chases, actor-knowledge boundaries, and a reference campaign path.', + }, + { + name: 'Narrative', + status: 'Alpha', + tone: 'alpha', + zh: '系统无关长线叙事、权威 stdio MCP、Skills 与项目生成。', + en: 'System-neutral long-form play, authoritative stdio MCP, Skills, and project authoring.', + }, + { + name: 'Extended ruleset import', + status: 'Experimental', + tone: 'experimental', + zh: '保留来源的解析、质量报告与 Pack 校验;不等于任意规则书可直接运行。', + en: 'Source-preserving parsing, quality reports, and Pack validation—not arbitrary rulebook execution.', + }, +] as const; + +export const localProfiles = [ + ['dnd-only', 'D&D 5e'], + ['coc-only', 'Call of Cthulhu 7e'], + ['narrative-only', 'Narrative'], + ['multi-system', 'D&D + CoC + Narrative'], +] as const; diff --git a/src/pages/404.astro b/src/pages/404.astro new file mode 100644 index 0000000..a4aef7b --- /dev/null +++ b/src/pages/404.astro @@ -0,0 +1,23 @@ +--- +import SiteLayout from '../layouts/SiteLayout.astro'; +--- + +
+
+ +

这条时间线没有这个页面。This timeline has no such page.

+

链接可能已经移动,或者地址输入有误。The link may have moved, or the address may be mistyped.

+ +
+
+
diff --git a/src/pages/developers.astro b/src/pages/developers.astro index 80d8f85..b95500f 100644 --- a/src/pages/developers.astro +++ b/src/pages/developers.astro @@ -1,200 +1,121 @@ --- import SiteLayout from '../layouts/SiteLayout.astro'; - -const repoGroups = [ - { - label: 'HOST & WEB PRODUCT', - title: 'Host 与托管 Web 产品', - titleEn: 'Host and hosted Web product', - repos: [ - { name: 'SagaSmith-agent', status: 'ALPHA', desc: '身份、会话、多渠道与 MCP 编排', descEn: 'Identity, sessions, channels, and MCP orchestration', url: 'https://github.com/SagaSmithAI/SagaSmith-agent' }, - { name: 'SagaSmith-Web', status: 'PUBLIC SOURCE', desc: 'SagaSmith Web:浏览器前端、API/BFF、托管控制面、协作、Forge、Module Studio 与运维', descEn: 'SagaSmith Web: browser frontend, API/BFF, hosted control plane, collaboration, Forge, Module Studio, and operations', url: 'https://github.com/SagaSmithAI/SagaSmith-Web' }, - ], - }, - { - label: 'FOUNDATIONS & DOMAINS', - title: '共享基础与垂直领域', - titleEn: 'Shared foundation and vertical domains', - repos: [ - { name: 'sagasmith-core', status: 'ACTIVE', desc: '分支、知识、统一内容包、检索与持久化', descEn: 'Branches, knowledge, unified content packages, retrieval, and persistence', url: 'https://github.com/SagaSmithAI/sagasmith-core' }, - { name: 'sagasmith-dnd', status: 'VERTICAL', desc: 'D&D 5e Domain、MCP、Skills、Workbench 与模组创作', descEn: 'D&D 5e Domain, MCP, Skills, Workbench, and module authoring', url: 'https://github.com/SagaSmithAI/sagasmith-dnd' }, - { name: 'sagasmith-coc', status: 'VERTICAL', desc: 'CoC 7e Domain、MCP、Skills、Workbench 与模组创作', descEn: 'CoC 7e Domain, MCP, Skills, Workbench, and scenario authoring', url: 'https://github.com/SagaSmithAI/sagasmith-coc' }, - { name: 'sagasmith-narrative', status: 'VERTICAL', desc: '系统无关 Narrative Domain、MCP、Skills 与项目创作', descEn: 'System-neutral Narrative Domain, MCP, Skills, and project authoring', url: 'https://github.com/SagaSmithAI/sagasmith-narrative' }, - ], - }, - { - label: 'CONTENT & DOCUMENTATION', - title: '内容与公共文档', - titleEn: 'Content and public documentation', - repos: [ - { name: 'SagaSmith-dnd-content-library', status: 'PUBLIC CATALOG', desc: '校验和绑定的当前 Content Pack 索引与归档', descEn: 'Checksum-bound current Content Pack index and archives', url: 'https://github.com/SagaSmithAI/SagaSmith-dnd-content-library' }, - { name: 'SagaSmithAI.github.io', status: 'PUBLIC DOCS', desc: '双语产品、开发者与内容目录站点', descEn: 'Bilingual product, developer, and content-catalog site', url: 'https://github.com/SagaSmithAI/SagaSmithAI.github.io' }, - { name: '.github', status: 'ORG CONTRACT', desc: '组织总览、贡献与发布政策', descEn: 'Organization overview, contribution, and release policy', url: 'https://github.com/SagaSmithAI/.github' }, - ], - }, -]; +import { repositories } from '../lib/site'; --- -
-
-
-
PUBLIC DEVELOPMENT · ACTIVE ALPHA
-

为会记得的世界,
打造可靠的边界。
Build reliable boundaries
for worlds that remember.

-

SagaSmithAI 用同一套领域 MCP 权威契约连接本地 Agent Kit 与托管 Web,让每一层只拥有它真正应该拥有的状态。

-

SagaSmithAI connects the Local Agent Kit and hosted Web through one authoritative domain MCP contract, so every layer owns only the right state.

- -
- -
- -
-
-
- 01 / OWNERSHIP -

流程不冒充结算,检索不决定真相Procedure does not impersonate settlement

-

SagaSmith 的可靠性来自明确所有权,而不是把更多上下文塞进同一个 Agent。

-

SagaSmith derives reliability from explicit ownership, not from putting more context into one agent.

-
-
-
A

SKILLS ≠ ENGINE

技能负责流程、提问与协作;确定性规则由领域引擎结算。

Skills own procedure and collaboration; domain engines settle deterministic rules.

-
B

SEARCH ≠ TRUTH

检索寻找候选证据;分支祖先与持久状态决定有效历史。

Retrieval finds candidate evidence; branch ancestry and persisted state determine valid history.

-
C

AGENT ≠ DATABASE

Agent Host 管理模型和会话,但不直接拥有规则与战役数据。

The Agent Host manages models and sessions without directly owning rules or campaign data.

-
D

CLIENT ≠ AUTHORITY

客户端只接收服务端已经按角色与范围过滤的投影。

Clients receive projections already filtered by actor and scope on the server.

-
-
-
- -
-
-
- 02 / TWO DEPLOYMENTS -

部署可以不同,权威契约不能分叉Deploy differently without forking authority

-

本地与 Hosted 只在 transport、authentication、storage 和 deployment 上不同;工具 schema、错误、规则写入、revision 与 idempotency 必须一致。

-

Local and hosted deployments may differ in transport, authentication, storage, and deployment; tool schemas, errors, rule writes, revisions, and idempotency must remain identical.

-
-
-
L

LOCAL AGENT KIT

Discord、QQ、Telegram Bot 可由 SagaSmith Agent 承载;Codex、Claude Code、OpenClaw 等 MCP client 也可通过 stdio 或 localhost HTTP 直连领域 MCP,使用 SQLite 与本地文件。

SagaSmith Agent can host Discord, QQ, and Telegram bots; Codex, Claude Code, OpenClaw, and other MCP clients can also connect directly over stdio or localhost HTTP with SQLite and local files.

-
W

SAGASMITH WEB

Browser/PWA 进入 API/BFF、托管控制面与 principal-scoped Agent worker,再调用同一领域 MCP;PostgreSQL、Redis 与对象存储只承担 Hosted 职责。

Browser/PWA traffic crosses the API/BFF, hosted control plane, and principal-scoped Agent worker before reaching the same domain MCP; PostgreSQL, Redis, and object storage remain hosted concerns.

-
-
-
+
+
+

开发者地图Developer map

+

一份契约,多条清晰边界。One contract. Clear ownership.

+

SagaSmith 不是一个把所有功能装进同一后端的仓库。Agent、Hosted Web、Core 与三个领域 vertical 各自拥有明确职责,通过同一 authoritative MCP contract 组合。SagaSmith is not a monolith that puts every feature into one backend. Agent, Hosted Web, Core, and three domain verticals own distinct responsibilities and compose through one authoritative MCP contract.

+
+ +
-
-
-
03 / ARCHITECTURE

每一层只拥有
它该拥有的状态
Each layer owns
only the right state

-

规则可以升级、系统可以扩展、Host 可以更换,而不需要复制领域逻辑或让客户端承担权限边界。

Rules can evolve, systems can expand, and hosts can change without copying domain logic or moving authorization into clients.

-
-
-
01HUMAN & AGENT CLIENTS

Players · DM · Bots · Generic Agents

浏览器、PWA、频道与通用 MCP 客户端Browser, PWA, channels, and generic MCP clients

-
local stdio/http · hosted https/session
-
-
02ALOCAL HOST

SagaSmith Agent

Bot、会话、模型、workspace 与多渠道Bots, sessions, models, workspace, and channels

-
02BHOSTED PRODUCT

SagaSmith Web

前端、API/BFF、控制面、协作、Forge 与 Agent workerFrontend, API/BFF, control plane, collaboration, Forge, and Agent workers

-
-
MCP · tools · resources · prompts
-
03CAPABILITY PLANE

Domain MCP Servers

发现、会话暴露、授权、幂等与审计Discovery, session exposure, authorization, idempotency, and audit

-
validated domain operations
-
-
04ARULE SYSTEMS

D&D 5e · CoC 7e · Next

规则包、角色 schema、战斗与解析 profileRule packs, character schemas, combat, and parser profiles

-
04BSHARED CORE

Campaign Runtime

分支、知识、导入、检索与持久化Branches, knowledge, imports, retrieval, and persistence

-
-
-
- -
-
-
- 04 / VERIFIED HOST PATH -

身份可验证,证据可复查Verifiable identity, reviewable evidence

-

2026-08-20 的当前源码回归从 SagaSmith Web 签名身份开始,经 Agent 的会话作用域动态工具进入 D&D 与 CoC MCP,并发完成两条参考战役且未记录回归缺口。D&D 路径记录了合法结局;未执行模组与互斥路径仍保留为机器可读 exclusion。

-

The 2026-08-20 current-source regression starts with SagaSmith Web-signed identity, crosses the Agent's session-scoped dynamic tools, and completes concurrent D&D and CoC reference campaigns without a reported regression gap. The D&D path records a legal ending; unexecuted modules and mutually exclusive paths remain machine-readable exclusions.

-
+
+
+
+

双发行物Two distributions

+

部署可以不同,领域真相不能分叉。Deployment may differ. Domain truth may not fork.

-
+

Local 与 Hosted 可以采用不同 transport、authentication、storage 和 deployment。它们必须保持相同 handlers、tool schemas、capability discovery、错误、权限、revision、idempotency 与规则写入语义。Local and Hosted may use different transport, authentication, storage, and deployment. They must preserve the same handlers, tool schemas, capability discovery, errors, authority, revisions, idempotency, and rule-write semantics.

+
+
+
+ LOCAL AGENT KIT +

Host → Domain MCP → Runtime → Core

+
    +
  • Host:SagaSmith Agent、Bot 或通用 MCP AgentHost: SagaSmith Agent, a bot, or a generic MCP agent
  • +
  • Transport:stdio 或仅 loopback 的 Streamable HTTPTransport: stdio or loopback-only Streamable HTTP
  • +
  • Storage:SQLite 与本地文件Storage: SQLite and local files
  • +
  • 不依赖 Web、PostgreSQL、Redis、对象存储、账户或 ForgeNo dependency on Web, PostgreSQL, Redis, object storage, accounts, or Forge
  • +
+
+
+ SAGASMITH WEB +

Browser → API/BFF → Agent worker → Domain MCP

+
    +
  • Web 拥有账户、session、配额、邀请、协作、Forge 与云端投影Web owns accounts, sessions, quota, invitations, collaboration, Forge, and cloud projections
  • +
  • principal-scoped worker 将身份交给动态 MCP 工具Principal-scoped workers pass identity to dynamic MCP tools
  • +
  • 领域服务在每次调用边界重新验证权限与 revisionDomain services revalidate authority and revision at every call boundary
  • +
  • Web 缓存投影,但不直接写开放领域数据库Web may cache projections but never writes open domain databases directly
  • +
+
+
+
-
-
-
- 05 / UNIFIED CONTENT -

分享内容,不复制运行时身份Share content without copying runtime identity

-

同一格式连接创作、分发与导入,同时把战役账本留在原来的权威边界。

-

One format connects authoring, distribution, and import while campaign ledgers stay with their authority.

-
-
-
A

ACTOR CARD

PC、NPC 与怪物共用完整角色卡;导入生成新 id,不复制 actor knowledge。

PCs, NPCs, and monsters share one complete card; import creates a new id and no ActorKnowledge.

-
B

ONE ARCHIVE

核心规则、Addon、模组和预设共用带来源、许可、checksum 与内容寻址资产的物理格式。

Core rules, addons, modules, and presets share one physical format with sources, licenses, checksums, and content-addressed assets.

-
C

MODULE PACK

Scene Atlas、原文、地图、审核内容、NPC/怪物/预设 PC、角色图与稳定关联可以自包含迁移。

Scene Atlas data, source documents, maps, reviews, NPCs, monsters, pregens, portraits, and stable bindings can migrate together.

-
D

PACK ≠ SAVE

权限、进度、世界状态、记忆、随机流、分支与 Snapshot 永不随内容包迁移。

Permissions, progress, world state, memory, random streams, branches, and Snapshots never ride with content packages.

-
-
-
+
+
+

权威所有权Authority ownership

+

每一次写入,都能回答“谁负责”。Every write has an accountable owner.

+
+
+
Client / UI输入与服务端授权投影Input and server-authorized projection
+
Agent / Skills意图、主持、工具选择与提案Intent, facilitation, tool selection, and proposals
+
Domain MCP权限、phase、revision、幂等与原子结算Authority, phase, revision, idempotency, and atomic settlement
+
Domain runtime系统规则、随机流与领域状态System rules, random streams, and domain state
+
SagaSmith Core持久化、事务、知识、分支、内容与检索Persistence, transactions, knowledge, branches, content, and retrieval
+
+
+
Agent ≠ Domain

Agent workspace memory 与主持人格不能替代战役 Snapshot、ActorKnowledge、权限或规则 receipt。Agent workspace memory and facilitation identity cannot replace campaign snapshots, ActorKnowledge, permissions, or rule receipts.

NO DIRECT DB WRITE
+
Skills ≠ Engine

Skills 描述主持、审核和创作步骤;会改变权威状态的机械结果必须由 runtime/MCP 验证。Skills describe facilitation, review, and authoring procedures; mechanical outcomes that change authoritative state must be validated by runtime and MCP.

PROPOSAL ONLY
+
Retrieval ≠ Truth

检索返回候选证据;来源、规则锁、分支祖先与持久状态决定什么当前有效。Retrieval returns candidate evidence; sources, rule locks, branch ancestry, and persistent state decide what is currently valid.

SOURCE-BOUND
+
+
-
-
-
- EXPERIMENTAL -

扩展规则集导入:
受控实验,而非通用承诺
Extended ruleset import:
a controlled experiment, not a universal promise

-
-
-

当前导入流程用于验证来源保留、解析、质量报告、索引与规则包校验。它不代表平台能够自动理解任意规则书,也不代表未经适配的第三方系统可以安全、完整地运行。

-

The current import flow validates provenance, parsing, quality reporting, indexing, and rule-pack checks. It does not imply automatic understanding of arbitrary rulebooks or safe, complete execution of unadapted third-party systems.

-
    -
  • 已定稿规则 Pack 可携带完整来源与稳定引用迁移;依赖使用跨实例 definition checksum,导入后已保存但仍未启用Finalized rule Packs migrate with complete sources and stable citations; dependencies use cross-instance definition checksums, and imported Packs are stored but remain inactive
  • -
  • 需要明确的系统插件与数据 schemaRequires an explicit system plugin and data schema
  • -
  • 需要规则验证、回归测试与人工审查Requires rule validation, regression tests, and human review
  • -
  • 不提供或授权商业规则书与模组内容Does not provide or license commercial rulebook or adventure content
  • -
-
+
+
+
+

当前仓库拓扑Current repository topology

+

这些是唯一当前入口。These are the only current entry points.

-
+
原独立 MCP、Skills、UI 与通用 Module Generator 仓库仅保留为 archived read-only history:不接收新 Issue、不参与发布,也不是 compatibility fallback。Former standalone MCP, Skills, UI, and generic Module Generator repositories are archived read-only history. They accept no new issues, supply no releases, and provide no compatibility fallback.
+
+
+ {repositories.map((repo) => ( +
+ {repo.key} + {repo.name} +

{repo.roleZh}{repo.roleEn}

+ CURRENT +
+ ))} +
+
-
-
-
06 / REPOSITORIES

按所有权进入,
而不是猜仓库
Enter by ownership,
not by guesswork

-

每个仓库独立版本与测试。状态标签表达当前定位,不隐含生产就绪。

Each repository versions and tests independently. Status labels describe current positioning without implying production readiness.

-
-
- {repoGroups.map((group, groupIndex) => ( -
-
0{groupIndex + 1}
{group.label}

{group.title}

{group.titleEn}

- -
- ))} -
-
+
+
+

公共契约Public contract

+

六个维度必须跨发行形态一致。Six dimensions stay consistent across distributions.

+
+
+
SCHEMA + CAPABILITY

动态发现,不维护固定工具全集Dynamic discovery, never a fixed tool superset

Host 使用服务端动态工具列表,并在 tools/list_changed 后刷新 schema;phase、role 与 exposure 继续在服务端收窄。Hosts use the server-owned dynamic tool list and refresh schemas after tools/list_changed; phase, role, and exposure narrow capabilities server-side.

+
ERROR + REVISION

失败可解释,写入可并发验证Failures explain themselves; writes remain concurrency-safe

客户端收到一致的错误结构与 revision 语义;旧 revision 不能通过 UI、Agent 文本或 transport 差异绕过。Clients receive consistent error structures and revision semantics; stale revisions cannot bypass checks through UI, Agent text, or transport differences.

+
IDEMPOTENCY + AUTHORITY

重试不会复制结算,身份不能由请求自选Retries do not duplicate settlement; requests do not choose identity

幂等键、principal context、actor binding 与 campaign role 均由可信边界注入并由 MCP 重新验证。Idempotency keys, principal context, actor bindings, and campaign roles are injected by trusted boundaries and revalidated by MCP.

+
PACK CONTRACT

统一内容包,独立权利与激活Unified content packages, separate rights and activation

sagasmith.content-package v2 是当前公开交换格式;每个 Pack 的许可、来源、导入权限与 campaign activation 都独立判断。sagasmith.content-package v2 is the current public exchange format; each Pack's license, provenance, import authority, and campaign activation are evaluated separately.

+
+
-
+
+
- OPEN PLATFORM -

从一条真实路径开始,
把边界做到可靠。
Start with one real path.
Make its boundaries reliable.

+

贡献入口Contribution entry

+

从拥有行为的仓库开始。Start in the repository that owns the behavior.

+

跨仓库或公共 contract 变更请先开 Issue。提交报告时附 commit/release、环境、复现命令与脱敏错误;漏洞、凭证、私有战役和受限制来源请走私密安全入口。Open an issue before cross-repository or public-contract changes. Include commit or release, environment, reproduction commands, and redacted errors. Route vulnerabilities, credentials, private campaigns, and restricted source material through private security reporting.

-
-
+ +
diff --git a/src/pages/index.astro b/src/pages/index.astro index a3026fe..9460e44 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -1,329 +1,208 @@ --- import SiteLayout from '../layouts/SiteLayout.astro'; import { loadNews } from '../lib/news'; +import { systemStatus } from '../lib/site'; const news = await loadNews(3); --- -
-
-
-
完整独立主持 · 也支持人类 DM 协作FULLY AUTONOMOUS HOSTING · HUMAN DM COLLABORATION
-

- 没有固定 DM,
也能完整开团。
- No regular DM?
Run the whole campaign.
-

-

SagaSmith 可以承担开场准备、场景推进、NPC 演绎、规则结算、战斗节奏和状态写回,独立完成从开团到收团的主持循环。世界会记得,而故事仍由玩家的选择推动。

-

SagaSmith can prepare the opening, advance scenes, portray NPCs, resolve rules, pace combat, and write back campaign state as a complete hosting loop. The world remembers; player choices still drive the story.

- -
- 完整主持循环COMPLETE HOSTING LOOP - 玩家推动故事PLAYER-DRIVEN STORY - 世界持续记忆PERSISTENT WORLD MEMORY -
+
+
+

AI 原生 TTRPG 平台AI-native TTRPG platform

+

+ 让世界记得,让规则落槌。 + Worlds remember. Rules settle. +

+

+ Agent 理解桌上的意图,领域运行时确定性结算,MCP 守住身份、权限、状态与时间线。你可以在自己的机器上运行完整 Local Agent Kit,也可以构建托管浏览器产品 SagaSmith Web。 + Agents understand intent at the table. Domain runtimes settle rules deterministically. MCP protects identity, authority, state, and timelines. Run the complete Local Agent Kit yourself, or build the hosted browser product, SagaSmith Web. +

+ - -
-
-
- 幻想 D20 战役 · 第 7 场 - FANTASY D20 CAMPAIGN · SESSION 07 - 游戏中IN PLAY -
- -
-
- 当前场景CURRENT SCENE -

灰烬钟楼下的地城The Dungeon Beneath the Ashen Bell

-

一支 5 级冒险小队刚在废弃祭坛旁发现断裂的银色徽记。A 5th-level party has found a broken silver sigil beside an abandoned altar.

-
- D20 -
- -
-
-
- V -
玩家视角PLAYER VIEWVARIS
- 已知 33 KNOWN -
-
    -
  • 徽记来自北门工坊The sigil came from the North Gate forge
  • -
  • 商人阿洛认得这个图案The merchant Arlo recognized the mark
  • -
  • 其余线索尚未发现More remains undiscovered
  • -
-
- -
-
- SS -
主持视角HOST VIEWSAGA HOST
- 主持专用HOST ONLY -
-
    -
  • 阿洛正在为钟楼主人传信Arlo carries messages for the bell's master
  • -
  • 放走他会改变第三场后的会面Sparing him changes a meeting three sessions later
  • -
  • 不会暴露给玩家视角Never exposed to the player view
  • -
-
-
- -
- 01放走阿洛SPARE ARLO - - 03旧承诺被兑现A PROMISE RETURNS -
-
+
    +
  • 本地优先可选Local-first option
  • +
  • 权威状态可验证Verifiable authority
  • +
  • Active AlphaActive Alpha
  • +
+
+ +
+
+ DEPLOYMENT MAP / v1 + CONTRACT LOCKED
-
- -
-
-
- 01 / 战役连续性CAMPAIGN CONTINUITY -

散场,不是世界的重置键The world does not reset when the table clears

-

一次看似微小的决定,可以穿过数场游戏,在正确的人面前、以正确的方式重新出现。

-

A small decision can travel across sessions and return to the right person in the right way.

-
- -
-
- 01 - 第一场 · 选择SESSION ONE · CHOICE -

队伍放走了可疑商人The party spared a suspicious merchant

-

DM 没有被迫选一条“正确剧情”。SagaSmith 只记录这件事,以及谁亲眼看见了它。

-

The DM is not forced into a “correct plot.” SagaSmith records what happened and who witnessed it.

-
- -
- 02 - 第三场 · 回应SESSION THREE · RESPONSE -

旧承诺在另一座城兑现The old promise returns in another city

-

人物关系和世界状态沿着真实战役继续,而不是依赖所有人记住几周前的笔记。

-

Relationships and world state carry forward without relying on everyone remembering weeks-old notes.

-
- -
- 03 - 此刻 · 秘密NOW · A SECRET -

只有一名角色知道原因Only one character knows why

-

玩家不会因为 AI 多说了一句而获得不该知道的事实,角色视角始终各不相同。

-

A stray AI sentence cannot reveal hidden truth. Every character keeps a distinct point of view.

-
-
- -
- -

SagaSmith 可以坐上主持席,但故事始终由玩家在桌上的选择向前推动。

-

SagaSmith can take the host's seat, while the players' choices keep driving the story forward.

-
-
-
- -
-
-
- 02 / 同一张桌子ONE TABLE -

同一个世界,
不同的视角
One world.
Different views.

-
-
-

DM 需要掌握全局,玩家需要相信自己的选择。SagaSmith 同时服务两者,但从不混淆他们的边界。

-

DMs need the whole picture. Players need to trust their choices. SagaSmith serves both without confusing their boundaries.

-
-
- -
-
-
- FOR THE DM - D -
-

你掌握世界,
不必独自记住一切
Own the world.
Do not carry it alone.

-
    -
  • 01
    接着上次继续Continue where you left off

    场景、NPC、约定和未完成事件保持连贯。

    Scenes, NPCs, promises, and unfinished events stay coherent.

  • -
  • 02
    秘密留在幕后Keep secrets backstage

    隐藏事实只在你决定的时候进入玩家视野。

    Hidden truths enter the player view only when you decide.

  • -
  • 03
    规则协助,不是夺权Rules support, not replacement

    确定的数值交给系统,目标、例外与叙事代价仍由你裁决。

    The system handles settled mechanics; targets, exceptions, and narrative cost remain yours.

  • -
  • 04
    安全探索另一种可能Explore another possibility

    从关键选择创建分支,而不覆盖原来的战役历史。

    Branch from a key choice without overwriting campaign history.

  • -
-
- -
-
- FOR THE PLAYER - P -
-

你的选择,
会被世界认真对待
Your choices
stay with the world.

-
    -
  • 01
    过去会回来The past returns

    曾经帮助、欺骗或放走的人,会在未来做出回应。

    People you helped, deceived, or spared can answer later.

  • -
  • 02
    角色有自己的认知Your character has a point of view

    你看到的是角色真正发现的内容,而不是幕后全知答案。

    You see what your character discovered, never an all-knowing backstage answer.

  • -
  • 03
    成长跨越每次聚会Growth carries across sessions

    人物状态、关系与经历不会在聊天记录里消失。

    Character state, relationships, and history do not vanish into a chat log.

  • -
  • 04
    结果可以理解Results make sense

    规则结果有依据;独立主持时的歧义与临时裁决会被明确说明。

    Rules have evidence; ambiguity and provisional rulings are made explicit in autonomous play.

  • -
-
-
- -
-
- PRIMARY EXPERIENCE / AUTONOMOUS HOSTING -

不只是协助记录,
而是完整独立主持
Not just campaign assistance.
A complete autonomous host.

-

SagaSmith 的主推体验是让没有固定 DM 的玩家也能完整开团;人类 DM 协作模式使用同一套可靠的战役基础。

-

SagaSmith's primary experience lets groups play a complete campaign without a regular DM. Human-led tables use the same reliable campaign foundation.

-
-
-
- 01 -
- 主推 · 完全独立主持PRIMARY · FULLY AUTONOMOUS HOSTING -

没有固定 DM,也能完整开团Run a complete game without a regular DM

-

SagaSmith 承担开场准备、场景推进、NPC 演绎、信息揭示、规则检定、战斗节奏与场后状态写回,形成从开团到收团的完整主持循环。

-

SagaSmith prepares the opening, advances scenes, portrays NPCs, reveals information, resolves rules, paces combat, and writes back session state as a complete hosting loop.

- 当前为活跃 Alpha,能力与界面仍在持续验证ACTIVE ALPHA · CAPABILITIES AND SURFACES ARE STILL BEING VALIDATED -
-
-
- 02 -
- 人类 DM 协作HUMAN DM COLLABORATION -

你决定世界如何回应You decide how the world answers

-

SagaSmith 协助整理连续性、角色知识、规则证据与现场状态;故事方向、例外和叙事代价仍由 DM 决定。

-

SagaSmith supports continuity, character knowledge, rule evidence, and live state while the DM owns direction, exceptions, and narrative cost.

-
-
+
+
+
+ LOCAL / YOU OPERATE +

Local Agent Kit

+

Bot、SagaSmith Agent 或通用 MCP Host;SQLite 与本地文件。Bot, SagaSmith Agent, or a generic MCP host; SQLite and local files.

+ stdio · localhost HTTP +
+
+ HOSTED / WEB PRODUCT +

SagaSmith Web

+

浏览器/PWA、API/BFF、协作、Forge 与 principal-scoped Agent worker。Browser/PWA, API/BFF, collaboration, Forge, and principal-scoped Agent workers.

+ account · session · worker +
-
-
- -
-
-
-
- 03 / 当前体验CURRENT EXPERIENCE -

从一条真实路径开始Start with one real path

-
-
-

SagaSmith 仍在早期阶段。我们宁愿清楚说明哪些已经可以体验、哪些正在测试,也不把未来计划写成现成功能。

-

SagaSmith is still early. We would rather show what can be tried, what is in testing, and what remains experimental than present a roadmap as a finished product.

+ +
+
+ ONE AUTHORITATIVE CONTRACT + sagasmith.authoritative-mcp/v1 +

同一 handlers、schemas、errors、authority、revision 与 idempotency。The same handlers, schemas, errors, authority, revisions, and idempotency.

+
+
+
DOMAIND&D
+
DOMAINCoC
+
DOMAINNarrative
-
- -
- - -
-
CALL OF CTHULHU 7E测试中IN TESTING
-
C7
-

调查、线索与逐渐逼近的真相Investigation, clues, and approaching truth

-

CoC 7e 已能验证调查场景、检定、理智、追逐、角色知识、权威结果展示与战役持久化;2026-08-20 已与 D&D 参考战役在同一最新托管栈中并发完成,完整体验仍处于 Alpha。

-

CoC 7e validates investigations, checks, sanity, chases, character knowledge, authoritative result presentation, and campaign persistence. On 2026-08-20 it completed concurrently with the D&D reference campaign on the current hosted stack; the complete experience remains Alpha.

-
    -
  • 调查场景与线索进度Investigation scenes and clue progress
  • -
  • 调查员各自掌握的信息Investigator-specific knowledge
  • -
  • 检定、理智与追逐流程Checks, sanity, and chases
  • -
-
- -
-
- -
-
- 04 / 明确边界CLEAR BOUNDARIES -

有些决定,始终属于桌上的人Some decisions always belong to the table

-

可靠的战役伙伴不只是能力更多,也必须知道什么时候不该越界。

-

A trustworthy campaign companion needs more than capability. It needs to know where to stop.

-
- -
-
DM

主持权与裁决方式由你选择You choose the hosting and ruling mode

人类主持时,DM 保留最终裁决;独立主持时,SagaSmith 按已激活 Pack 与当前战役状态推进,来源无冲突的语境歧义由 Agent 裁决。

With a human host, the DM keeps the final ruling. In autonomous mode, SagaSmith advances from activated Packs and campaign state, with the Agent settling contextual ambiguity when source evidence is not missing or conflicting.

-
PC

秘密不会靠“提醒 AI”保管Secrets do not rely on reminding the AI

玩家与角色只看到服务端允许的视角,幕后事实不会因为表达失误而混入回答。

Players and characters receive only the view allowed for them. Backstage truth cannot leak through a careless answer.

-
IP

分享格式不等于内容授权A sharing format is not a content license

每个统一内容包都保留独立许可与署名;规则书、角色或模组只能由获得相应授权的人导入与分享。

Every unified content package retains its own license and attribution. Books, actors, and adventures may be imported or shared only by authorized users.

-
LAB

实验不等于承诺Experiment is not a promise

扩展规则集导入、更多系统与部分客户端体验仍在实验或原型阶段,并会持续明确标注。

Extended ruleset import, additional systems, and parts of the client experience remain experimental or prototyped and will stay clearly labeled.

+

+ 允许不同May differ · transport / auth / storage / deployment + 不得分叉Must not fork · tools / rules / writes / revisions +

+
+
+ +
+
+
+

一次选择,不止一轮对话A choice outlives a chat turn

+

长期战役需要真正的连续性。Long campaigns need real continuity.

-
- -
-
-
-
- 05 / 最新进展LATEST PROGRESS -

锻造中的下一场游戏Forging the next session

-
-
-

我们公开记录哪些体验已经向前一步,也诚实保留尚未解决的边界。

-

We record what moved forward and keep the unresolved boundaries visible.

-
-
- -
- {news.map((item) => ( -
-
{item.tag}
-

{item.title}

-

{item.titleEn}

-

{item.summary}

-

{item.summaryEn}

-
- ))} -
- - +

SagaSmith 把主持表达与权威状态分开:模型可以提议、解释和扮演;只有领域 MCP 可以验证并写入战役账本。SagaSmith separates facilitation from authoritative state. Models may propose, explain, and portray; only the domain MCP validates and writes the campaign ledger.

+
+
+
+ 记住世界Remember the world +

战役 revision、分支、Snapshot、长期事实与检索来源都拥有明确身份,而不是被压进一段聊天摘要。Campaign revisions, branches, snapshots, durable facts, and retrieval sources keep explicit identities instead of disappearing into a chat summary.

+ STATE / TIMELINE +
+
+ 守住视角Protect perspective +

GM、玩家、PC 与 NPC 的知识边界独立。客户端只收到服务端按 principal、actor、campaign 与 phase 过滤后的投影。GM, player, PC, and NPC knowledge stays separate. Clients receive only server-filtered projections for the current principal, actor, campaign, and phase.

+ ACTOR KNOWLEDGE +
+
+ 让规则落槌Let rules settle +

Skills 指导主持流程,Agent 组织意图;确定性规则、随机流、权限与原子写入始终回到匹配的领域运行时。Skills guide facilitation and the Agent organizes intent; deterministic rules, random streams, authorization, and atomic writes return to the matching domain runtime.

+ MCP RECEIPT +
+
+
+ +
+
+

同一平台,两种发行形态One platform, two distributions

+

从你的桌面开始,或构建完整 Web 产品。Start on your machine, or build the full Web product.

+
+
+
+ LOCAL AGENT KIT +

你拥有运行环境。You own the runtime.

+

选择 D&D、CoC、Narrative 或 multi-system profile。使用 SagaSmith Agent 连接 Discord、QQ、Telegram,或让 Codex、Claude Code、OpenClaw 等 MCP Host 直接接入。Choose a D&D, CoC, Narrative, or multi-system profile. Use SagaSmith Agent for Discord, QQ, or Telegram, or connect an MCP-capable host such as Codex, Claude Code, or OpenClaw.

+
    +
  • 不依赖 SagaSmith Web、账户、配额或 ForgeNo dependency on SagaSmith Web, hosted accounts, quota, or Forge
  • +
  • stdio 或仅 loopback 的 Streamable HTTPstdio or loopback-only Streamable HTTP
  • +
  • 本地 SQLite、文件与用户自有 providerLocal SQLite, files, and your own model provider
  • +
+ +
+
+ SAGASMITH WEB +

托管浏览器产品。The hosted browser product.

+

SagaSmith Web 包含浏览器/PWA、API/BFF、托管控制面、协作房间、Forge、Module Studio、Agent 编排与运维;它不成为第二套游戏状态权威。SagaSmith Web includes the browser/PWA, API/BFF, hosted control plane, collaborative rooms, Forge, Module Studio, Agent orchestration, and operations. It never becomes a second authority for game state.

+
    +
  • 账户、session、配额、邀请与云端投影属于 WebAccounts, sessions, quota, invitations, and cloud projections belong to Web
  • +
  • 每次权威写入仍由领域 MCP 结算Every authoritative write is still settled by the domain MCP
  • +
  • 当前为公开可见源码的 Active Alpha,不是生产 SLAPublicly visible Active Alpha source, not a production SLA
  • +
+ +
+
+
+ +
+
+
+

成熟度透明Honest maturity

+

当前能验证什么,就只说什么。Claim only what current evidence proves.

-
- -
- -
+

Active Alpha 适合本地开发、集成验证与实团测试。参考战役通过不代表每个 Pack、剧情分支或生产部署都已覆盖。Active Alpha is ready for local development, integration validation, and real-table testing. Passing reference campaigns do not prove every Pack, story branch, or production deployment.

+
+
+ {systemStatus.map((system) => ( +
+ {system.name} +

{system.zh}{system.en}

+ {system.status} +
+ ))} +
+
+ +
+
+

边界就是产品Boundaries are product features

+

不把方便,误写成权威。Convenience never impersonates authority.

+
+
+
+ AGENT ≠ DATABASE +

Agent 不拥有战役账本The Agent does not own the campaign ledger

+

模型、会话、渠道与主持风格属于 Agent;规则、角色权限、战役状态与 receipt 属于领域 MCP。Models, sessions, channels, and facilitation style belong to the Agent; rules, actor authority, campaign state, and receipts belong to the domain MCP.

+
+
+ PACK ≠ SAVE +

内容包不是战役存档A content Pack is not a campaign save

+

Pack 不携带权限、ActorKnowledge、进度、随机流、分支或 Snapshot。导入与激活是两次独立、受权威控制的操作。Packs do not carry permissions, ActorKnowledge, progress, random streams, branches, or snapshots. Import and activation are separate authoritative operations.

+
+
+ PUBLIC ≠ LICENSED +

仓库公开不等于内容开放Public visibility is not a content license

+

商业规则书、模组、图片与衍生资产必须逐项核对许可、署名和分发权。公开索引只证明可发现,不授予使用权。Commercial rulebooks, modules, images, and derived assets require item-level license, attribution, and distribution checks. A public index proves discoverability—not permission.

+
+
+
+ +
+
- 下一场战役THE NEXT CAMPAIGN -

不必从遗忘中
重新开始。
Do not begin again
from forgetting.

-

SagaSmith 仍在早期阶段。关注当前进展,看看一个真正会记得的世界如何逐步成形。

-

SagaSmith is still early. Follow the work and watch a world that truly remembers take shape.

+

公开进展Public progress

+

变更留下时间与证据。Changes keep their dates and evidence.

- +
+ {news.map((item) => ( +
+
{item.tag}
+

{item.title}{item.titleEn}

+

{item.summary}{item.summaryEn}

+
+ ))} +
+
+ +
+
+
+

选择入口Choose an entry point

+

先决定谁来运行,再选择系统。Choose who operates it, then choose a system.

+

Local Agent Kit 是完整的本地发行物;SagaSmith Web 是托管产品代码库。两者不是主从关系,也不互作兼容回退。The Local Agent Kit is the complete local distribution; SagaSmith Web is the hosted product repository. Neither is a fallback for the other.

-
-
+ 开始使用Get started + +
diff --git a/src/pages/library.astro b/src/pages/library.astro index 3a82956..9fcdea5 100644 --- a/src/pages/library.astro +++ b/src/pages/library.astro @@ -2,46 +2,93 @@ import SiteLayout from '../layouts/SiteLayout.astro'; --- -
-
- PUBLIC REPOSITORY / CHECKSUM-BOUND / RIGHTS-AWARE -

SagaSmith 当前内容包目录SagaSmith Current Content Pack Catalog

-

仓库与机器可读索引现已公开,统一列出 D&D 5e 与 CoC 7e 的 core rules、Addon、模组和预设。公开可见不等于开放内容许可:每个 Pack、来源与资产仍保留自己的许可、署名和分发限制。请只下载、导入或再分发你有权使用的内容;安装与战役启用仍由 MCP 权限和规则锁控制。

-

The repository and machine-readable index are now public, covering current D&D 5e and CoC 7e core rules, addons, modules, and presets. Public visibility is not an open-content license: every Pack, source, and asset retains its own license, attribution, and distribution restrictions. Download, import, or redistribute only what you are authorized to use; MCP permissions and campaign locks still govern installation and activation.

-

2026-08-20,当前源码托管栈已并发完成 D&D 与 CoC 参考战役。回归 runner 会发现目录中的全部模组,并把未执行项与互斥路径保留为机器可读 exclusion;一次参考通过不等于 46 个 Pack 的完整通关认证。

-

On 2026-08-20 the current-source hosted stack completed the D&D and CoC reference campaigns concurrently. The regression runner discovers every catalog module and preserves unexecuted items and mutually exclusive paths as machine-readable exclusions; one reference pass is not complete playthrough certification for all 46 Packs.

-
- 查看公开仓库View public repository - 机器可读索引Machine-readable index +
+
+

内容库Content library

+

可校验,不等于可随意分发。Verifiable does not mean freely distributable.

+

当前目录用不可变 identity、版本、大小与 SHA-256 描述 Content Pack。仓库可见性只让索引可以被审计;每个 Pack、来源文档与嵌入资产仍保留各自许可、署名和分发限制。The current catalog describes Content Packs with immutable identities, versions, sizes, and SHA-256 checksums. Repository visibility makes the index auditable; every Pack, source document, and embedded asset retains its own license, attribution, and distribution restrictions.

+
+ +
+ +
+
+
+

当前机器索引Current machine index

+

46 个当前 Pack,逐项锁定。46 current Packs, locked item by item.

-
-
- CANONICAL CATALOG SOURCE -

目录以校验和索引为准The checksum index is the catalog authority

-

Content Library 仓库目前没有独立 GitHub Pages 部署。请通过公开仓库审阅 Pack、许可与验证证据,或直接读取 raw JSON 索引;本站不再嵌入不存在的 Pages UI。

-

The Content Library repository currently has no separate GitHub Pages deployment. Review Packs, rights metadata, and validation evidence in the public repository or consume the raw JSON index; this site no longer embeds a nonexistent Pages UI.

-
- 浏览目录源码Browse catalog source - 打开 JSONOpen JSON +

数量来自当前 content-library/index.json:D&D 43 个,CoC 3 个。索引存在不代表任何条目已经授予你下载、导入或再分发权。Counts come from the current content-library/index.json: 43 D&D Packs and 3 CoC Packs. Presence in the index does not grant permission to download, import, or redistribute an entry.

+
+
+
21D&D ADDONS
+
3CORE RULE PACKS
D&D 2 · CoC 1
+
20MODULES
D&D 18 · CoC 2
+
2D&D ACTOR PRESETS
+
+ +
+ +
+
+

统一内容边界Unified content boundary

+

四种 Pack,共用一套可验证骨架。Four Pack kinds share one verifiable envelope.

+
+
+
core_rules

核心规则 profile、来源、版本与确定性定义;安装不会自动赋予来源权利。Core-rule profiles, provenance, versions, and deterministic definitions; installation does not confer source rights.

RULE LOCK
+
addon

绑定依赖与版本的扩展规则;导入后仍需 Owner/DM 独立激活。Extension rules with locked dependencies and versions; Owner or DM activation remains a separate step after import.

DEPENDENCY LOCK
+
module

结构化模组、标准化来源、Scene Atlas、NPC、资产与审核记录;不携带战役进度。Structured modules, normalized sources, Scene Atlas, NPCs, assets, and review records; never campaign progress.

SOURCE-BOUND
+
preset

PC、NPC 与怪物共用 sagasmith.actor-card.v3;导入始终创建新的本地 identity。PCs, NPCs, and monsters share sagasmith.actor-card.v3; import always creates a new local identity.

NEW IDENTITY
+
+
+ +
+
+
+

边界检查Boundary checks

+

技术完整性、运行权限与内容权利是三件事。Technical integrity, runtime authority, and content rights are separate.

-
-
-
+
Extended ruleset import 仍是 Experimental。来源保留、解析成功和 checksum 通过,都不代表系统能够安全执行任意未适配规则书。Extended ruleset import remains Experimental. Preserved provenance, successful parsing, and valid checksums do not mean the system can safely execute an arbitrary unadapted rulebook.
+ +
+
01 / INTEGRITY

先验证归档Validate the archive first

校验 manifest、archive SHA-256、每个 blob、依赖闭包、system/kind 与可移植路径。旧 portable JSON、release manifest 与 .sagasmith-module 不属于当前公开协议。Validate manifest, archive SHA-256, every blob, dependency closure, system and kind, and portable paths. Legacy portable JSON, release manifests, and .sagasmith-module are not part of the current public protocol.

+
02 / AUTHORITY

导入不等于激活Import is not activation

领域 MCP 负责导入、导出、删除与激活。导入可先进入校验后的非活动存储;branch activation 仍需独立的 Owner/DM 操作。The domain MCP owns import, export, removal, and activation. Import may land in validated inactive storage; branch activation remains a separate Owner or DM operation.

+
03 / RIGHTS

逐包核对许可Verify rights per Pack

检查 license_evidence、来源授权、署名、图片/字体/地图许可与分发范围。商业原文、提取文本、chunks、embeddings 与 Drafts 应保持私密。Check license_evidence, source authorization, attribution, image, font, and map licenses, and distribution scope. Commercial source text, extracts, chunks, embeddings, and Drafts must remain private.

+
+ - +
+
+

不会随 Pack 迁移Never travels with a Pack

+

内容可以移动,战役权威留在原账本。Content may move. Campaign authority stays in its ledger.

+
+
+
IDENTITY

权限与角色绑定Permissions and actor bindings

来源数据库 id、principal、role grant 与 actor control 不进入可分享内容。Source database IDs, principals, role grants, and actor control never enter shareable content.

+
CONTINUITY

进度、分支与 SnapshotProgress, branches, and snapshots

战役时间线、revision、世界状态、随机流和 Snapshot DAG 仍属于原运行时。Campaign timelines, revisions, world state, random streams, and snapshot DAGs remain in the original runtime.

+
KNOWLEDGE

ActorKnowledge 与记忆ActorKnowledge and memory

PC/NPC 所知事实、隐藏 GM 内容、Agent session 与 workspace memory 不随 actor card 或模组导出。PC and NPC knowledge, hidden GM content, Agent sessions, and workspace memory do not export with actor cards or modules.

+
ACTIVATION

分支激活决定Branch activation decisions

安装一个 Pack 不会替用户选择 campaign,也不暗示来源具有权威;激活需要匹配领域的受权操作。Installing a Pack does not choose a campaign or make its source authoritative; activation requires an authorized domain operation.

+
+
+ +
+
+
+

先检查,再使用Inspect before use

+

公开目录是审计入口,不是许可商店。The public catalog is an audit surface, not a license store.

+

只下载、导入或再分发你已获授权的内容。发现缺失、矛盾或敏感的许可证据时,不要公开传播包内容;从目录仓库的贡献或安全入口报告。Download, import, or redistribute only content you are authorized to use. If license evidence is missing, conflicting, or sensitive, do not redistribute the Pack; report it through the catalog repository's contribution or security path.

+
+ 安全报告入口Security reporting +
+
+ diff --git a/src/pages/privacy.astro b/src/pages/privacy.astro new file mode 100644 index 0000000..0fb3201 --- /dev/null +++ b/src/pages/privacy.astro @@ -0,0 +1,39 @@ +--- +import SiteLayout from '../layouts/SiteLayout.astro'; +--- + +
+

隐私Privacy

+

这个站点不承载你的战役。This site does not host your campaign.

+

本说明只适用于 sagasmithai.github.io 组织静态站点,不是 SagaSmith Web、Local Agent Kit、Bot、领域 MCP 或第三方模型 provider 的隐私政策。This notice applies only to the static organization site at sagasmithai.github.io. It is not the privacy policy for SagaSmith Web, the Local Agent Kit, bots, domain MCPs, or third-party model providers.

+ +
+

本站处理什么What this site handles

+

本站是由 GitHub Pages 发布的静态文件。它不提供账户登录、不接收 campaign 数据、不嵌入分析脚本、不设置广告 cookie,也不把表单提交到 SagaSmithAI 服务。This site is a set of static files published by GitHub Pages. It provides no account login, receives no campaign data, embeds no analytics, sets no advertising cookies, and submits no forms to a SagaSmithAI service.

+

语言切换会在浏览器 localStorage 中保存一个值:sagasmith-languagezhen)。它只在你的设备上用于记住界面语言,可以通过清除站点数据删除。The language switch stores one value in browser localStorage: sagasmith-language (zh or en). It remains on your device solely to remember interface language and can be removed by clearing site data.

+
+ +
+

托管与外部链接Hosting and external links

+

GitHub Pages 可能按 GitHub 自身政策处理请求日志、IP 地址与安全遥测。本站链接到 GitHub 仓库、文档和安全报告页面;离开本站后,目标服务的政策适用。GitHub Pages may process request logs, IP addresses, and security telemetry under GitHub's own policies. This site links to GitHub repositories, documentation, and security reporting pages; the destination service's policy applies after you leave this site.

+

GitHub 通用隐私声明GitHub General Privacy Statement

+
+ +
+

产品数据不属于本站Product data is outside this site's scope

+

SagaSmith Web 拥有其账户、session、协作与运营数据边界;Local Agent Kit 的数据保存在用户选择的本地环境。使用这些产品前,请阅读相应仓库和部署中的当前隐私、安全与配置说明。SagaSmith Web owns its account, session, collaboration, and operations data boundaries. Local Agent Kit data remains in the local environment chosen by its operator. Read the current privacy, security, and configuration documentation for the relevant repository and deployment before use.

+
+ +
+

更新与联系Updates and contact

+

本说明随静态站点源码版本化。若发现说明与站点行为不一致,请在本站源码仓库提交普通 Issue;若不一致涉及敏感数据或漏洞,请使用安全页面的私密入口。This notice is versioned with the static site source. If it diverges from site behavior, open a regular issue in the site repository; if the mismatch involves sensitive data or a vulnerability, use the private path on the security page.

+

站点 IssueSite issues · 安全入口Security entry

+
+
+
diff --git a/src/pages/security.astro b/src/pages/security.astro new file mode 100644 index 0000000..e80cc4e --- /dev/null +++ b/src/pages/security.astro @@ -0,0 +1,39 @@ +--- +import SiteLayout from '../layouts/SiteLayout.astro'; +--- + +
+

安全Security

+

敏感问题,请私密报告。Report sensitive issues privately.

+

不要在公开 Issue、Pull Request、日志或截图中放入漏洞利用细节、token、cookie、provider 响应、私有战役、个人数据、商业来源材料或受限 Pack 内容。Do not put exploit details, tokens, cookies, provider responses, private campaigns, personal data, commercial source material, or restricted Pack content in public issues, pull requests, logs, or screenshots.

+ +
+

选择拥有边界的仓库Choose the repository that owns the boundary

+

如果问题属于 SagaSmith Web 的账户、session、API/BFF、托管 worker、Forge、Module Studio 或部署基础设施,请使用 Web 仓库的 GitHub Private Vulnerability Reporting。For SagaSmith Web accounts, sessions, API/BFF, hosted workers, Forge, Module Studio, or deployment infrastructure, use the Web repository's GitHub Private Vulnerability Reporting.

+

私密报告 SagaSmith Web 漏洞Privately report a SagaSmith Web vulnerability

+

如果问题属于 Agent、Core、D&D、CoC、Narrative 或内容目录,请先打开对应仓库的 SECURITY.md 或组织安全入口,并遵循其当前私密渠道。For Agent, Core, D&D, CoC, Narrative, or the content catalog, open the matching repository's current SECURITY.md or the organization security entry point and follow its private channel.

+

组织安全政策Organization security policy

+
+ +
+

请提供最少且可用的证据Provide minimal, usable evidence

+
    +
  • 受影响的仓库与 commit/release;Affected repository and commit or release;
  • +
  • 受影响的信任边界与最小复现;Affected trust boundary and minimal reproduction;
  • +
  • 操作系统、Python/Node 版本与脱敏错误;Operating system, Python and Node versions, and redacted errors;
  • +
  • 不会暴露其他人的战役、账户或来源内容的证明材料。Evidence that does not expose another person's campaign, account, or source content.
  • +
+
+ +
+

不要测试他人的生产数据Do not test against someone else's production data

+

未经明确授权,不要访问他人的 campaign、账户或部署,也不要扩大最小复现。找不到安全的私密渠道时,先通过组织安全政策中列出的联系入口说明“存在敏感问题”,不要公开细节。Without explicit authorization, do not access another person's campaign, account, or deployment, and do not expand a minimal reproduction. If no safe private channel is available, use the organization security policy's contact path to report that a sensitive issue exists without publishing details.

+
+
+
diff --git a/src/pages/start.astro b/src/pages/start.astro index b9cd3df..22dcb71 100644 --- a/src/pages/start.astro +++ b/src/pages/start.astro @@ -1,158 +1,184 @@ --- import SiteLayout from '../layouts/SiteLayout.astro'; - -const profiles = [ - { - name: 'D&D only', - command: 'uv run nanobot sagasmith install --source release --profile dnd-only', - zh: 'D&D 5e Domain、MCP、Skills 与 Workbench', - en: 'D&D 5e Domain, MCP, Skills, and Workbench', - }, - { - name: 'CoC only', - command: 'uv run nanobot sagasmith install --source release --profile coc-only', - zh: 'CoC 7e Domain、MCP、Skills 与 Workbench', - en: 'CoC 7e Domain, MCP, Skills, and Workbench', - }, - { - name: 'Narrative only', - command: 'uv run nanobot sagasmith install --source release --profile narrative-only', - zh: '系统无关长线叙事 Domain、MCP 与 Skills', - en: 'System-neutral long-form Narrative Domain, MCP, and Skills', - }, - { - name: 'Multi-system', - command: 'uv run nanobot sagasmith install --source release --profile multi-system', - zh: '一次安装三条当前领域链路', - en: 'Install all three current domain paths together', - }, -]; +import { localProfiles } from '../lib/site'; --- - -
-
-
- START / ONE CONTRACT -

选择入口,
不要复制规则。
Choose an entry.
Never duplicate authority.

-

本地 Agent、通用 MCP 客户端与 Hosted Web 都经过同一套领域 MCP。先选择你要运行在哪里,再选择 D&D、CoC 或 Narrative。

-

Local Agent, generic MCP clients, and Hosted Web all cross the same domain MCP contract. Choose where to run first, then select D&D, CoC, or Narrative.

- -
- -
+
+
+

开始使用Get started

+

先选择运行边界。Choose the operating boundary first.

+

想自己运行 Bot、桌面工具或 MCP 服务,选择 Local Agent Kit。想开发账户、多人房间、Forge 与浏览器产品,进入 SagaSmith Web。两条路径执行同一权威领域契约。Choose the Local Agent Kit to run bots, desktop tools, or MCP services yourself. Choose SagaSmith Web to develop accounts, multiplayer rooms, Forge, and the browser product. Both paths execute the same authoritative domain contract.

+
+ +
-
-
-
- 01 / CHOOSE A PATH -

三种入口,一个权威边界Three entries, one authority boundary

-

不要先把整个组织的仓库都克隆下来。按你真正要运行的产品选择入口。

-

Do not begin by cloning the whole organization. Enter through the product you actually want to run.

-
-
-
- 01 · LOCAL KIT -

自己运行 Bot 或本地 AgentRun your own bot or local Agent

-

SagaSmith-agent 安装所选领域、Skills、本地 SQLite 与可选 Workbench。适合 Discord、QQ、Telegram 和长期小团。

-

Install selected domains, Skills, local SQLite, and optional Workbenches from SagaSmith-agent. Best for Discord, QQ, Telegram, and persistent small groups.

- 查看安装步骤Open installation steps -
-
- 02 · BRING YOUR AGENT -

让现有 Agent 直接连接 MCPConnect an existing Agent directly

-

Codex、Claude Code、OpenClaw 等客户端可通过 stdio 或 localhost Streamable HTTP 使用同一领域能力,无需运行 SagaSmith Web。

-

Codex, Claude Code, OpenClaw, and other clients can use the same domain capabilities over stdio or localhost Streamable HTTP without SagaSmith Web.

- 查看客户端边界Review the client boundary -
-
- 03 · SAGASMITH WEB -

构建浏览器与多人托管产品Build the hosted browser product

-

前端、API/BFF、协作、Forge、Module Studio、Agent worker 与 operations 位于 SagaSmith-Web。当前是公开开发源码,不是公开运营承诺。

-

Frontend, API/BFF, collaboration, Forge, Module Studio, Agent workers, and operations live in SagaSmith-Web. It is public development source, not a production-service promise.

- 打开 Web 仓库Open the Web repository -
-
-
-
+
+
+
+ 01 / LOCAL AGENT KIT +

在自己的机器上运行完整栈Run the complete stack on your machine

+

适合本地 AI GM、Discord/QQ/Telegram Bot、Workbench,以及需要直接控制数据、模型 provider 和更新节奏的团队。For local AI GMs, Discord/QQ/Telegram bots, Workbenches, and teams that need direct control over data, model providers, and update cadence.

+ +
+
+ 02 / EXISTING MCP HOST +

让现有 Agent 直接接入Connect an existing Agent directly

+

Codex、Claude Code、OpenClaw 和其他 MCP Host 可以通过 auth bridge 连接选定领域 MCP;无凭证模板不包含 token、密钥、角色授权或 campaign authority。Codex, Claude Code, OpenClaw, and other MCP hosts can connect through the auth bridge. Credential-free templates contain no tokens, secrets, role grants, or campaign authority.

+ +
+
+ 03 / SAGASMITH WEB +

开发托管浏览器产品Develop the hosted browser product

+

适合账户/session、协作房间、托管 worker、Forge、Module Studio 与运营基础设施。当前仓库是 proprietary Active Alpha;公开可见不构成生产服务承诺。For accounts and sessions, collaborative rooms, hosted workers, Forge, Module Studio, and operations infrastructure. The repository is a proprietary Active Alpha; public visibility is not a production-service promise.

+ +
+
+ 04 / EXTEND A DOMAIN +

扩展 D&D、CoC 或 NarrativeExtend D&D, CoC, or Narrative

+

进入对应 vertical repository;Domain、MCP、Skills、UI(如有)与创作流程在同一 commit 下版本化。不要从已归档的拆分仓库开始。Start in the matching vertical repository, where Domain, MCP, Skills, UI where present, and authoring flow are versioned at one commit. Do not start from archived split repositories.

+ +
+
+
-
-
-
-
02 / LOCAL AGENT KIT

从一个发行 profile 开始Start from one release profile

-

默认安装需要 Python 3.11+、uv 与 Node.js 22.12+(含 npm);纯后端安装可显式添加 --skip-ui--source release 会按发行锁克隆并固定所选组件,不导入商业规则书,也不自动激活 Pack。

The default install requires Python 3.11+, uv, and Node.js 22.12+ with npm; a backend-only install may explicitly add --skip-ui. --source release clones and pins selected components from the release lock; it does not import commercial books or activate Packs.

-
-
-
01 · CLONESagaSmith-agent
-
git clone https://github.com/SagaSmithAI/SagaSmith-agent.git
+  
+
+
+

Local Agent Kit

+

从发行锁安装,不依赖旧仓库。Install from the release lock, with no legacy repository dependency.

+
+

--source release 会使用 Agent 仓库内的发行锁检出所选当前组件。安装器不会导入商业书籍、生成私有 Pack、配置 provider secret 或自动激活内容。--source release uses the Agent repository's release lock to fetch the selected current components. The installer does not import commercial books, create private Packs, configure provider secrets, or activate content.

+
+
+
+
+

克隆 Agent hostClone the Agent host

+

Local Agent Kit 的安装器、发行锁、模板和 doctor 都在当前 SagaSmith-agent 仓库。The Local Agent Kit installer, release lock, templates, and doctor live in the current SagaSmith-agent repository.

+
+
POWERSHELL / BASHCLONE
+
git clone https://github.com/SagaSmithAI/SagaSmith-agent.git
 cd SagaSmith-agent
+
-
- {profiles.map((profile) => ( -
-
{profile.name}PROFILE
-

{profile.zh}

{profile.en}

-
{profile.command}
-
- ))} +
+
+
+

选择正式 profileChoose a release profile

+

下面以 D&D-only 为例。可换成 CoC、Narrative 或 multi-system;已有同级源码工作区的开发者可按 Agent 指南选择 workspace source。The example uses D&D-only. Substitute CoC, Narrative, or multi-system; developers with current sibling source checkouts can follow the Agent guide for workspace source.

+
+
LOCAL KIT INSTALLERRELEASE SOURCE
+
uv run nanobot sagasmith install --source release --profile dnd-only
+
-
-
03 · CONFIGURE, VERIFY, STARTrepo-local state
-
uv run nanobot onboard --wizard --config config/config.json --workspace workspace
-uv run nanobot sagasmith install --verify-only
-uv run nanobot sagasmith doctor --json
-uv run nanobot sagasmith start
-

Windows PowerShell 可把路径分隔符写成 config\config.json。provider 未配置会作为 readiness warning 报告,不会伪装成安装损坏。On Windows PowerShell, config\config.json is also accepted. A missing provider is reported as a readiness warning, not as a broken installation.

+
+
+
+

配置本地 AgentConfigure the local Agent

+

向导写入 repo-local 配置与 workspace。provider secret 只应通过环境变量或本机私密配置提供,不要提交填充后的配置。The wizard writes repo-local configuration and workspace state. Provide model-provider secrets through environment variables or private local configuration, and never commit filled configuration.

+
+
ONBOARDREPO-LOCAL
+
uv run nanobot onboard --wizard --config config/config.json --workspace workspace
+
-
+
+
+

先诊断,再启动Diagnose before starting

+

doctor 分别报告 MCP/config、领域数据库、Skills、provider 与 transport。provider 未配置是 readiness 警告,不应被误报为安装损坏。Doctor reports MCP/config, domain databases, Skills, provider readiness, and transport separately. A missing provider is a readiness warning, not a broken installation.

+
+
VERIFY + STARTLOCAL
+
uv run nanobot sagasmith doctor --json
+uv run nanobot sagasmith start
+
+
+
+
+ +
+
+

安装 profileInstall profiles

+

只装需要的领域。Install only the domains you need.

+
+ + + + {localProfiles.map(([profile, label]) => ( + + + + + + ))} + +
Profile领域Domains当前仓库输入Current repository inputs
{profile}{label}{profile === 'multi-system' ? 'Agent + Core + D&D + CoC + Narrative' : `Agent + Core + ${label}`}
+
+ +
+
+
+

同一契约,三种本地 transportOne contract, three local transports

+

transport 改变进程形态,不改变规则语义。Transport changes process shape, not rule semantics.

-
+

每种模式都必须执行同一 handlers、schema、错误、revision、idempotency 与 authority 语义。HTTP 仅监听 loopback。Every mode executes the same handlers, schemas, errors, revisions, idempotency, and authority semantics. HTTP binds to loopback only.

+
+
+
stdio

一个本地客户端独占每个 MCP 进程;不开放网络监听。One local client owns each MCP process; no network listener.

+
streamable-http

多个本机客户端共享常驻领域进程;仅 loopback。Multiple local clients share long-lived domain processes on loopback only.

+
mixed

默认:D&D/CoC 使用 loopback HTTP,Narrative 使用 stdio。Default: D&D and CoC use loopback HTTP; Narrative uses stdio.

+
+
-
-
+
+
+
+

SagaSmith Web

+

托管产品不是 Local 的必需后端。The hosted product is not a required Local backend.

+
+

SagaSmith Web 独立拥有账户、session、配额、邀请、协作、Forge、Module Studio 与云端投影;它通过 principal-scoped worker 调用同一领域 MCP,不直接写领域数据库。SagaSmith Web independently owns accounts, sessions, quota, invitations, collaboration, Forge, Module Studio, and cloud projections. Principal-scoped workers call the same domain MCP; Web never writes domain databases directly.

+
+
+
- 03 / TRANSPORT -

stdio 独占,HTTP 共享stdio for one client, HTTP for shared local use

-

stdio 适合一个 Agent 独占一个 MCP 进程;streamable-http 适合多个本地 Bot 或客户端共享常驻服务。HTTP profile 只监听 loopback。非本机访问必须配置 bearer token 与显式 origin allowlist。

-

stdio fits one Agent owning one MCP process; streamable-http lets several local bots or clients share a resident service. HTTP profiles bind to loopback only. Remote access requires a bearer token and an explicit origin allowlist.

+

阅读产品与许可边界Read the product and license boundaries

+

SagaSmith Web 源码公开可见,但仓库使用 proprietary license。Local 的 Apache-2.0 组件不依赖 Web。SagaSmith Web source is publicly visible under a proprietary license. Apache-2.0 Local components do not depend on Web.

-
-
UNCHANGEDhandlers · schemas · errors · revision · idempotency · authority
-
LOCAL ONLYSQLite · local files · optional embeddings/OCR · your provider
-
NEVER REQUIREDSagaSmith Web · PostgreSQL · Redis · object storage · quota · Forge
+
+
+
+

在隔离开发环境启动Start in an isolated development environment

+

当前开发路径使用 Docker Compose 与同级当前源码 worktree;生产/release 形态使用仓库的不可变组件锁。不要把真实凭证、私有战役或商业来源材料放进测试数据。The current development path uses Docker Compose with current sibling source worktrees; production and release builds use the repository's immutable component lock. Keep real credentials, private campaigns, and commercial source material out of test data.

+
+
POWERSHELLWEB DEVELOPMENT
+
Copy-Item .env.example .env
+docker compose -f compose.yaml -f compose.workspace.yaml up --build
+
-
-
+ +
+ +
-
+
+
- ACTIVE ALPHA -

先跑通一条路径,
再扩展整张桌子。
Prove one path.
Then expand the table.

-

SagaSmith 适合本地开发、集成验证与实团测试。遇到问题,请附带仓库、commit、系统版本、命令与脱敏错误。

-

SagaSmith is ready for local development, integration validation, and real-table testing. Reports should include repository, commit, platform, command, and sanitized error.

-
-
- SagaSmith Agent - 查看架构与仓库地图Explore architecture and repositories +

仍然不确定?Still deciding?

+

从 Local Kit 的单一领域 profile 开始。Start with one Local Kit domain profile.

+

它是最小、可逆且不依赖托管基础设施的入口;需要浏览器协作、Forge 或运营能力时,再评估 SagaSmith Web。It is the smallest reversible entry with no hosted-infrastructure dependency. Evaluate SagaSmith Web when you need browser collaboration, Forge, or operations.

-
-
+ SagaSmith-agent ↗ + +
diff --git a/src/pages/updates.astro b/src/pages/updates.astro new file mode 100644 index 0000000..5f689d4 --- /dev/null +++ b/src/pages/updates.astro @@ -0,0 +1,54 @@ +--- +import SiteLayout from '../layouts/SiteLayout.astro'; +import { loadNews } from '../lib/news'; + +const news = await loadNews(50); +--- + +
+
+

公开进展Public updates

+

变更有日期,结论有边界。Dated changes. Bounded claims.

+

这里记录对外可验证的平台里程碑。历史条目保留当时事实;当前安装、发布与贡献入口始终以现行 vertical repositories、SagaSmith-agent、Sagasmith-core 与 SagaSmith-Web 为准。These notes record externally verifiable milestones. Historical entries preserve the facts of their date; current installation, release, and contribution paths always use the active vertical repositories, SagaSmith-agent, Sagasmith-core, and SagaSmith-Web.

+
+ +
+ +
+
+ {news.map((item) => ( +
+
+ {item.tag} + +
+
+ {item.title}{item.titleEn} +

{item.summary}{item.summaryEn}

+
+ 已发布Published +
+ ))} +
+
+ +
+
+
+

看当前状态Need current status?

+

以默认分支和当前仓库地图为准。Use default branches and the current repository map.

+

历史条目不是安装清单,也不重新激活已归档组件。当前 topology、owner 与 contract 见开发者页面。Historical notes are not installation manifests and do not reactivate archived components. See the developer page for current topology, ownership, and contracts.

+
+ 当前仓库地图Current repository map +
+
+
diff --git a/src/styles/site.css b/src/styles/site.css index fe56141..e7f7a86 100644 --- a/src/styles/site.css +++ b/src/styles/site.css @@ -1,20 +1,25 @@ :root { - --ink: #171a18; - --ink-2: #222722; - --ink-3: #303630; - --paper: #f3efe6; - --paper-2: #e7dfd0; - --paper-3: #d8cebd; - --orange: #e65025; - --orange-dark: #b93618; - --sage: #7f927a; - --sage-light: #a9b6a4; - --gold: #c7a968; - --line: rgba(23, 26, 24, .18); - --line-dark: rgba(255, 255, 255, .14); + --void: #0c0d0d; + --forge: #151716; + --forge-raised: #1b1e1c; + --iron: #2a2d2b; + --iron-soft: #343735; + --vellum: #eee9dc; + --vellum-dim: #c8c5ba; + --muted: #96998f; + --gold: #d9ad5b; + --gold-bright: #f2c76f; + --moss: #718d6a; + --moss-bright: #9fbd98; + --ember: #d97a58; + --danger: #dc8177; + --line: rgba(238, 233, 220, .13); + --line-strong: rgba(238, 233, 220, .24); + --display: "Iowan Old Style", "Noto Serif SC", "Songti SC", Georgia, serif; + --body: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", "Noto Sans SC", sans-serif; --mono: "Cascadia Mono", "SFMono-Regular", Consolas, monospace; - --serif: "Iowan Old Style", "Palatino Linotype", "Noto Serif SC", "Songti SC", Georgia, serif; - --sans: Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", "Noto Sans SC", sans-serif; + color-scheme: dark; + font-synthesis: none; } * { @@ -22,154 +27,196 @@ } html { + min-width: 320px; + background: var(--void); scroll-behavior: smooth; - background: var(--paper); } body { + min-height: 100vh; margin: 0; - color: var(--ink); + color: var(--vellum); background: - radial-gradient(circle at 18% 7%, rgba(255, 255, 255, .62), transparent 34rem), - var(--paper); - font-family: var(--sans); - line-height: 1.6; + radial-gradient(circle at 18% -5%, rgba(217, 173, 91, .13), transparent 31rem), + radial-gradient(circle at 86% 24%, rgba(113, 141, 106, .07), transparent 28rem), + var(--void); + font: 400 16px/1.7 var(--body); overflow-x: hidden; } ::selection { - color: white; - background: var(--orange); + color: #17140e; + background: var(--gold-bright); } -[data-lang="zh"] .en, -[data-lang="en"] .zh { - display: none !important; +img, +svg { + max-width: 100%; } -a { - color: inherit; +button, +input, +select, +textarea { + font: inherit; } -button, a { - -webkit-tap-highlight-color: transparent; + color: inherit; } a:focus-visible, -button:focus-visible { - outline: 3px solid var(--orange); +button:focus-visible, +summary:focus-visible { + outline: 3px solid var(--gold-bright); outline-offset: 4px; } +[data-lang="zh"] .en, +[data-lang="en"] .zh { + display: none !important; +} + +.ambient-grid { + position: fixed; + inset: 0; + z-index: -1; + pointer-events: none; + opacity: .22; + background-image: + linear-gradient(rgba(238, 233, 220, .028) 1px, transparent 1px), + linear-gradient(90deg, rgba(238, 233, 220, .028) 1px, transparent 1px); + background-size: 56px 56px; + mask-image: linear-gradient(to bottom, black, transparent 78%); +} + .skip-link { - padding: 10px 14px; position: fixed; - top: 8px; - left: 8px; - z-index: 200; - color: var(--paper); - background: var(--ink); - border: 1px solid var(--orange); + top: 10px; + left: 10px; + z-index: 300; + padding: 10px 14px; + color: var(--void); + background: var(--gold-bright); + border-radius: 6px; text-decoration: none; - font: 700 11px var(--mono); - transform: translateY(-150%); + font: 800 12px var(--mono); + transform: translateY(-160%); } .skip-link:focus { transform: none; } -.grain { - position: fixed; - inset: 0; - z-index: 100; - pointer-events: none; - opacity: .17; - mix-blend-mode: multiply; - background-image: - repeating-radial-gradient(circle at 17% 23%, rgba(23, 26, 24, .13) 0 1px, transparent 1px 4px), - repeating-radial-gradient(circle at 83% 71%, rgba(23, 26, 24, .09) 0 1px, transparent 1px 5px); - background-size: 47px 43px, 61px 59px; -} - .shell { - width: min(1240px, calc(100% - 48px)); + width: min(1180px, calc(100% - 48px)); margin-inline: auto; } .topbar { - width: min(1320px, calc(100% - 32px)); - height: 72px; + width: min(1260px, calc(100% - 32px)); + min-height: 68px; margin: 14px auto 0; - padding: 0 20px; - display: flex; + padding: 10px 14px 10px 18px; + display: grid; + grid-template-columns: 1fr auto 1fr; align-items: center; - justify-content: space-between; position: sticky; top: 14px; - z-index: 90; + z-index: 100; + background: rgba(12, 13, 13, .9); border: 1px solid var(--line); - background: rgba(243, 239, 230, .9); - box-shadow: 0 8px 32px rgba(23, 26, 24, .05); - backdrop-filter: blur(16px); + border-radius: 12px; + box-shadow: 0 16px 50px rgba(0, 0, 0, .32); + backdrop-filter: blur(18px); } .brand { + width: max-content; display: inline-flex; - align-items: center; + align-items: baseline; + gap: 3px; + color: var(--vellum); text-decoration: none; } -.brand-wordmark { - width: 142px; - height: 38px; - display: block; - object-fit: contain; - mix-blend-mode: multiply; +.brand img { + width: 34px; + height: 34px; + margin-right: 8px; + align-self: center; } -.topbar nav { - display: flex; - align-items: center; - gap: 30px; +.brand span { + font: 600 24px/1 var(--display); + letter-spacing: -.025em; } -.topbar nav a { - position: relative; - text-decoration: none; - font: 700 10px/1 var(--mono); - letter-spacing: .08em; - text-transform: uppercase; +.brand small { + color: var(--gold); + font: 800 9px/1 var(--mono); + letter-spacing: .14em; } -.topbar nav a::after { - content: ""; - width: 0; - height: 2px; - position: absolute; - left: 0; - bottom: -8px; - background: var(--orange); - transition: width .2s ease; +.desktop-nav { + display: flex; + align-items: center; + gap: 4px; } -.topbar nav a:hover::after { - width: 100%; +.desktop-nav a, +.github-link, +.language, +.mobile-menu summary { + min-height: 38px; + padding: 0 12px; + display: inline-flex; + align-items: center; + justify-content: center; + border: 1px solid transparent; + border-radius: 7px; + text-decoration: none; + font: 750 11px/1 var(--mono); + letter-spacing: .04em; } -.topbar nav a.active { - color: var(--orange-dark); +.desktop-nav a { + color: var(--muted); } -.topbar nav a.active::after { - width: 100%; +.desktop-nav a:hover, +.desktop-nav a[aria-current="page"] { + color: var(--gold-bright); + background: rgba(217, 173, 91, .08); + border-color: rgba(217, 173, 91, .2); } .header-actions { display: flex; + justify-content: flex-end; align-items: center; - gap: 8px; + gap: 6px; +} + +.github-link { + color: var(--vellum-dim); + border-color: var(--line); +} + +.github-link:hover, +.language:hover, +.mobile-menu summary:hover { + color: var(--gold-bright); + border-color: rgba(217, 173, 91, .4); +} + +.language { + min-width: 40px; + padding-inline: 8px; + color: var(--vellum); + background: var(--forge); + border-color: var(--line); + cursor: pointer; } .mobile-menu { @@ -178,16 +225,9 @@ button:focus-visible { } .mobile-menu summary { - min-width: 62px; - height: 34px; - padding: 0 11px; - display: grid; - place-items: center; - border: 1px solid var(--line); + border-color: var(--line); cursor: pointer; - font: 700 10px var(--mono); list-style: none; - text-transform: uppercase; } .mobile-menu summary::-webkit-details-marker { @@ -195,146 +235,115 @@ button:focus-visible { } .mobile-menu[open] summary { - color: var(--paper); - background: var(--ink); + color: var(--void); + background: var(--gold); } .mobile-menu nav { - width: min(280px, calc(100vw - 32px)); - padding: 10px; + width: min(300px, calc(100vw - 32px)); + padding: 8px; display: grid; position: absolute; - top: 44px; + top: 48px; right: 0; - gap: 0; - color: var(--paper); - background: var(--ink); - border: 1px solid var(--line-dark); - box-shadow: 0 20px 50px rgba(23, 26, 24, .24); + color: var(--vellum); + background: #101211; + border: 1px solid var(--line-strong); + border-radius: 10px; + box-shadow: 0 22px 70px #000b; } .mobile-menu nav a { padding: 13px 12px; - border-bottom: 1px solid var(--line-dark); + border-radius: 6px; text-decoration: none; - font: 700 11px var(--mono); -} - -.mobile-menu nav a:last-child { - border-bottom: 0; + font: 700 12px var(--mono); } -.language { - min-width: 38px; - height: 34px; - color: var(--ink); - border: 1px solid var(--line); - background: transparent; - cursor: pointer; - font: 700 10px var(--mono); +.mobile-menu nav a:hover, +.mobile-menu nav a[aria-current="page"] { + color: var(--gold-bright); + background: var(--forge-raised); } -.language:hover { - background: var(--paper-2); +main:focus { + outline: none; } .section { - padding-block: 116px; -} - -.js [data-reveal] { - opacity: 0; - transform: translateY(24px); - transition: opacity .65s ease, transform .65s ease; + padding-block: 112px; } -.js [data-reveal].visible { - opacity: 1; - transform: none; -} - -.eyebrow, -.section-index { - color: var(--orange-dark); - font: 700 10px/1.2 var(--mono); - letter-spacing: .15em; - text-transform: uppercase; +.section + .section { + border-top: 1px solid var(--line); } .eyebrow { + margin: 0 0 18px; display: flex; align-items: center; gap: 9px; - margin-bottom: 24px; + color: var(--gold); + font: 800 10px/1.3 var(--mono); + letter-spacing: .18em; + text-transform: uppercase; } -.pulse { +.eyebrow::before { + content: ""; width: 7px; height: 7px; - flex: 0 0 auto; - border-radius: 50%; - background: var(--orange); - box-shadow: 0 0 0 6px rgba(230, 80, 37, .11); + border: 1px solid currentColor; + transform: rotate(45deg); } -.hero { - min-height: 790px; - padding-block: 62px 88px; - display: grid; - grid-template-columns: .92fr 1.08fr; - align-items: center; - gap: 68px; - position: relative; +.section-head { + max-width: 800px; + margin-bottom: 52px; } -.hero::before, -.dev-hero::before { - content: ""; - width: 520px; - height: 520px; - position: absolute; - left: -355px; - top: 60px; - z-index: -1; - border: 1px solid var(--line); - border-radius: 50%; - box-shadow: - 0 0 0 52px rgba(23, 26, 24, .025), - 0 0 0 104px rgba(23, 26, 24, .016); +.section-head.split { + max-width: none; + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(280px, .56fr); + align-items: end; + gap: 56px; } -.hero h1, -.dev-hero h1 { +.section-head h2, +.page-hero h1, +.hero-copy h1, +.prose h1 { margin: 0; - font: 500 clamp(56px, 6.2vw, 92px)/.9 var(--serif); - letter-spacing: -.06em; + font-family: var(--display); + font-weight: 500; + letter-spacing: -.045em; } -.hero h1 em, -.dev-hero h1 em { - display: inline-block; - width: max-content; - max-width: 100%; - color: var(--orange); - font-size: .62em; - font-weight: 500; - white-space: nowrap; +.section-head h2 { + font-size: clamp(40px, 5vw, 68px); + line-height: .98; } -[data-lang="en"] .hero h1, -[data-lang="en"] .dev-hero h1 { - font-size: clamp(52px, 5.6vw, 82px); +.section-head p, +.page-hero .lede, +.hero-copy .lede { + margin: 22px 0 0; + color: var(--vellum-dim); + font-size: clamp(16px, 1.6vw, 19px); + line-height: 1.72; } -.lede { - max-width: 640px; - margin: 30px 0 0; - color: rgba(23, 26, 24, .72); - font-size: clamp(16px, 1.35vw, 19px); +.section-note { + padding: 18px 20px; + color: var(--muted); + background: var(--forge); + border-left: 2px solid var(--gold); + font-size: 14px; } -.hero-actions { - margin-top: 34px; +.button-row { + margin-top: 32px; display: flex; flex-wrap: wrap; gap: 10px; @@ -342,1863 +351,880 @@ button:focus-visible { .button { min-height: 48px; - padding: 0 20px; + padding: 0 18px; display: inline-flex; align-items: center; justify-content: center; - gap: 13px; - border: 1px solid var(--ink); + gap: 12px; + color: var(--vellum); + background: transparent; + border: 1px solid var(--line-strong); + border-radius: 8px; text-decoration: none; - font: 750 11px var(--mono); + font: 800 11px var(--mono); letter-spacing: .04em; - transition: transform .2s ease, background .2s ease, color .2s ease, border-color .2s ease; + transition: transform .18s ease, border-color .18s ease, color .18s ease, background .18s ease; +} + +.button:hover { + color: var(--gold-bright); + border-color: rgba(217, 173, 91, .6); + transform: translateY(-2px); } .button.primary { - color: white; - background: var(--orange-dark); - border-color: var(--orange-dark); + color: #17140f; + background: var(--gold); + border-color: var(--gold); } .button.primary:hover { - background: var(--ink); - border-color: var(--ink); - transform: translateY(-2px); + color: #090a09; + background: var(--gold-bright); + border-color: var(--gold-bright); +} + +.hero { + min-height: 780px; + padding-block: 88px 104px; + display: grid; + grid-template-columns: minmax(0, .82fr) minmax(520px, 1.18fr); + align-items: center; + gap: 70px; } -.button.secondary:hover { - color: white; - background: var(--ink); +.hero-copy h1 { + max-width: 8.8em; + font-size: clamp(58px, 7.2vw, 104px); + line-height: .88; } -.button.light { - color: var(--ink); - background: var(--paper); - border-color: var(--paper); +.hero-copy h1 em { + color: var(--gold); + font-style: normal; } -.button.light:hover { - color: white; - background: var(--orange-dark); - border-color: var(--orange-dark); +.hero-copy .lede { + max-width: 620px; } -.promise-row { +.hero-proof { margin-top: 34px; + padding: 0; display: flex; flex-wrap: wrap; - gap: 10px 24px; - color: rgba(23, 26, 24, .52); - font: 700 9px var(--mono); - letter-spacing: .08em; -} - -.promise-row > span { - display: inline-flex; - align-items: center; - gap: 8px; + gap: 9px 22px; + color: var(--muted); + list-style: none; + font: 750 10px var(--mono); } -.promise-row i { - width: 5px; - height: 5px; - background: var(--orange); - transform: rotate(45deg); +.hero-proof li::before { + content: "◆"; + margin-right: 8px; + color: var(--moss-bright); + font-size: 8px; } -.campaign-window { +.contract-map { min-width: 0; - padding: 15px; + padding: 14px; position: relative; - color: #e9e5da; - background: var(--ink); - border: 1px solid #343a35; - box-shadow: 22px 26px 0 var(--paper-2), 22px 26px 0 1px var(--line); - transform: rotate(.3deg); + background: linear-gradient(145deg, #171918, #101211); + border: 1px solid var(--line-strong); + border-radius: 16px; + box-shadow: 24px 26px 0 rgba(217, 173, 91, .055), 0 28px 90px rgba(0, 0, 0, .38); } -.window-head { - min-height: 34px; - padding: 0 3px 11px; - display: grid; - grid-template-columns: 1fr auto 1fr; +.contract-head { + min-height: 36px; + padding: 0 4px 11px; + display: flex; align-items: center; - border-bottom: 1px solid rgba(255, 255, 255, .12); - color: rgba(255, 255, 255, .48); - font: 700 8px var(--mono); - letter-spacing: .11em; + justify-content: space-between; + color: var(--muted); + border-bottom: 1px solid var(--line); + font: 700 9px var(--mono); + letter-spacing: .12em; } -.window-head > div { - display: flex; - gap: 5px; +.contract-head span:first-child::before { + content: "● ● ●"; + margin-right: 12px; + color: var(--iron-soft); + letter-spacing: .25em; } -.window-head i { - width: 6px; - height: 6px; - border-radius: 50%; - background: #59615b; +.contract-head b { + color: var(--moss-bright); + font-weight: 800; } -.window-head i:first-child { - background: var(--orange); +.contract-body { + padding: 24px 0 4px; + display: grid; + grid-template-columns: 1fr 62px 1.16fr; + align-items: stretch; + gap: 0; } -.window-head b { - justify-self: end; - color: var(--sage-light); - font-weight: 700; +.contract-lanes { + display: grid; + gap: 12px; } -.scene-banner { - min-height: 150px; - padding: 26px 26px 24px; - display: flex; - align-items: center; - justify-content: space-between; +.contract-lane { + min-height: 152px; + padding: 18px; position: relative; - overflow: hidden; - border-bottom: 1px solid rgba(255, 255, 255, .1); - background: - linear-gradient(110deg, rgba(230, 80, 37, .13), transparent 45%), - repeating-linear-gradient(90deg, transparent 0 39px, rgba(255, 255, 255, .025) 39px 40px); + background: #1c1f1d; + border: 1px solid var(--line); + border-radius: 11px; } -.scene-banner::after { - content: ""; - width: 170px; - height: 170px; - position: absolute; - right: -58px; - border: 1px solid rgba(255, 255, 255, .08); - border-radius: 50%; - box-shadow: 0 0 0 28px rgba(255, 255, 255, .018); +.contract-lane small, +.contract-core small, +.runtime-node small { + display: block; + color: var(--muted); + font: 800 8px/1.3 var(--mono); + letter-spacing: .13em; + text-transform: uppercase; } -.scene-banner small, -.perspective small { - color: var(--sage-light); - font: 700 8px var(--mono); - letter-spacing: .12em; +.contract-lane h2 { + margin: 9px 0 7px; + font: 500 24px/1.08 var(--display); } -.scene-banner h2 { - margin: 10px 0 3px; - font: 500 30px/1 var(--serif); +.contract-lane p { + margin: 0; + color: var(--vellum-dim); + font-size: 12px; + line-height: 1.55; } -.scene-banner p { - margin: 0; - color: rgba(255, 255, 255, .5); - font-size: 11px; +.lane-status { + margin-top: 15px; + display: inline-flex; + align-items: center; + gap: 7px; + color: var(--moss-bright); + font: 700 8px var(--mono); + letter-spacing: .07em; } -.scene-mark { - margin-right: 14px; - position: relative; - z-index: 1; - color: rgba(255, 255, 255, .11); - font: 500 58px/1 var(--serif); +.lane-status::before, +.status-dot { + content: ""; + width: 7px; + height: 7px; + flex: 0 0 auto; + border-radius: 50%; + background: var(--moss-bright); + box-shadow: 0 0 0 5px rgba(113, 141, 106, .12); } -.perspective-grid { - margin-top: 10px; +.contract-junction { display: grid; - grid-template-columns: 1fr 1fr; - gap: 10px; + place-items: center; + position: relative; } -.perspective { - min-width: 0; - padding: 17px; - border: 1px solid rgba(255, 255, 255, .11); - background: #1d211e; +.contract-junction::before, +.contract-junction::after { + content: ""; + position: absolute; + background: var(--gold); } -.dm-perspective { - background: #24201b; - border-color: rgba(230, 80, 37, .32); +.contract-junction::before { + width: 100%; + height: 1px; } -.perspective header { - padding-bottom: 13px; - display: grid; - grid-template-columns: 32px 1fr auto; - align-items: center; - gap: 9px; - border-bottom: 1px solid rgba(255, 255, 255, .09); +.contract-junction::after { + width: 1px; + height: calc(50% + 1px); +} + +.contract-junction i { + width: 16px; + height: 16px; + z-index: 1; + background: var(--void); + border: 2px solid var(--gold); + transform: rotate(45deg); } -.avatar { - width: 30px; - height: 30px; +.contract-stack { + min-width: 0; + padding-left: 0; display: grid; - place-items: center; - color: var(--orange); - background: #303630; - font: 800 9px var(--mono); + grid-template-rows: auto 1fr; + gap: 12px; } -.avatar.dm { - color: var(--gold); - background: #3b3327; +.contract-core { + padding: 18px; + color: #17140f; + background: var(--gold); + border-radius: 11px; } -.perspective header div { - display: grid; +.contract-core small { + color: rgba(23, 20, 15, .64); } -.perspective header strong { - margin-top: 2px; - font: 700 10px var(--mono); - letter-spacing: .08em; +.contract-core strong { + margin-top: 6px; + display: block; + font: 800 13px/1.35 var(--mono); + overflow-wrap: anywhere; } -.perspective header em { - color: rgba(255, 255, 255, .4); - font: normal 7px var(--mono); - letter-spacing: .06em; +.contract-core p { + margin: 9px 0 0; + font-size: 11px; + line-height: 1.5; } -.perspective ul { - margin: 12px 0 0; - padding: 0; - list-style: none; +.runtime-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 7px; } -.perspective li { - min-height: 36px; - padding: 8px 0; +.runtime-node { + min-width: 0; + padding: 13px 9px; display: grid; - grid-template-columns: 10px 1fr; - align-items: start; - gap: 8px; - border-bottom: 1px dotted rgba(255, 255, 255, .08); - color: rgba(255, 255, 255, .67); - font-size: 9px; + align-content: start; + gap: 7px; + color: var(--vellum-dim); + background: #111312; + border: 1px solid var(--line); + border-radius: 9px; + font: 700 10px/1.3 var(--mono); + text-align: center; } -.perspective li:last-child { - border: 0; +.runtime-node small { + color: var(--moss-bright); + font-size: 7px; } -.perspective li.muted { - color: rgba(255, 255, 255, .3); +.contract-foot { + margin: 14px 0 0; + padding: 12px 14px; + display: flex; + justify-content: space-between; + gap: 18px; + color: var(--muted); + background: rgba(255, 255, 255, .018); + border: 1px solid var(--line); + border-radius: 8px; + font: 700 8px/1.45 var(--mono); } -.fact-dot { - width: 5px; - height: 5px; - margin-top: 5px; - border: 1px solid #626c64; - border-radius: 50%; +.contract-foot strong { + color: var(--gold-bright); } -.fact-dot.found { - border-color: var(--sage); - background: var(--sage); +.ledger { + border-top: 1px solid var(--line-strong); } -.fact-dot.sealed { - border-color: var(--orange); - background: var(--orange); +.ledger-row { + min-height: 116px; + padding: 22px 0; + display: grid; + grid-template-columns: 170px minmax(0, 1fr) auto; + align-items: center; + gap: 30px; + border-bottom: 1px solid var(--line); } -.lock { - color: var(--orange); - font-size: 6px; +.ledger-row > strong { + font: 600 clamp(20px, 2.2vw, 29px)/1.1 var(--display); } -.choice-thread { - min-height: 64px; - margin-top: 10px; - padding: 13px 16px; - display: grid; - grid-template-columns: auto 1fr auto; - align-items: center; - border: 1px solid rgba(255, 255, 255, .1); - background: rgba(255, 255, 255, .025); +.ledger-row p { + margin: 0; + color: var(--vellum-dim); } -.choice-thread > i { - height: 1px; - background: linear-gradient(90deg, var(--orange), rgba(255, 255, 255, .15)); +.ledger-row code, +.repo-row code { + color: var(--gold-bright); + font: 700 11px var(--mono); } -.choice-node { +.tag, +.state { + width: max-content; + padding: 6px 9px; display: inline-flex; align-items: center; - gap: 8px; - color: rgba(255, 255, 255, .46); - font: 700 7px var(--mono); - letter-spacing: .08em; + gap: 7px; + color: var(--vellum-dim); + background: var(--forge); + border: 1px solid var(--line); + border-radius: 99px; + font: 750 9px var(--mono); + letter-spacing: .05em; + white-space: nowrap; } -.choice-node b { - width: 23px; - height: 23px; - display: grid; - place-items: center; - color: var(--orange); - border: 1px solid var(--orange); - border-radius: 50%; - font-size: 7px; -} - -.choice-node.current { - color: var(--sage-light); -} - -.choice-node.current b { - color: var(--ink); - background: var(--sage-light); - border-color: var(--sage-light); -} - -.window-seal { - width: 82px; - height: 82px; - position: absolute; - right: -27px; - bottom: -30px; - overflow: hidden; - border: 4px solid var(--paper); +.state::before { + content: ""; + width: 6px; + height: 6px; border-radius: 50%; - box-shadow: 0 0 0 1px var(--line); - transform: rotate(-8deg); -} - -.window-seal img { - width: 100%; - height: 100%; - object-fit: cover; + background: var(--moss-bright); } -.section-heading { - margin-bottom: 56px; +.state[data-tone="testing"]::before, +.state[data-tone="experimental"]::before { + background: var(--gold-bright); } -.section-heading h2 { - margin: 18px 0 18px; - font: 500 clamp(42px, 5.3vw, 69px)/.98 var(--serif); - letter-spacing: -.05em; -} - -.section-heading p { - max-width: 730px; - margin: 0; - color: rgba(23, 26, 24, .64); - font-size: 16px; -} - -.section-heading.centered { - max-width: 880px; - margin-inline: auto; - margin-bottom: 58px; - text-align: center; -} - -.section-heading.centered p { - margin-inline: auto; -} - -.split-heading { +.mode-grid, +.boundary-grid, +.choice-grid, +.feature-grid { display: grid; - grid-template-columns: 1.15fr .85fr; - align-items: end; - gap: 84px; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 16px; } -.split-heading > div:last-child { - padding-bottom: 8px; +.mode-card, +.boundary-card, +.choice-card, +.feature-card { + padding: clamp(24px, 4vw, 42px); + background: linear-gradient(145deg, var(--forge-raised), #111312); + border: 1px solid var(--line); + border-radius: 14px; } -.experience { - color: var(--paper); - background: var(--ink); +.mode-card.highlight { + border-color: rgba(217, 173, 91, .44); + box-shadow: inset 0 1px 0 rgba(242, 199, 111, .13); } -.experience .section-index, -.systems .section-index, -.developer-bridge .section-index, -.closing .section-index, -.dev-experiment .section-index, -.dev-closing .section-index { - color: var(--orange); +.mode-card h3, +.boundary-card h3, +.choice-card h2, +.feature-card h3 { + margin: 14px 0 14px; + font: 500 clamp(25px, 3vw, 38px)/1.05 var(--display); } -.experience .section-heading p, -.inverse p { - color: rgba(255, 255, 255, .56); +.mode-card p, +.boundary-card p, +.choice-card p, +.feature-card p { + margin: 0; + color: var(--vellum-dim); } -.story-track { - display: grid; - grid-template-columns: 1fr 42px 1fr 42px 1fr; - align-items: stretch; +.mode-list, +.compact-list, +.check-list { + margin: 22px 0 0; + padding: 0; + list-style: none; } -.story-track article { - min-height: 325px; - padding: 30px; +.mode-list li, +.compact-list li, +.check-list li { + padding: 9px 0 9px 20px; position: relative; - border: 1px solid rgba(255, 255, 255, .14); - background: #1d211e; -} - -.story-track article.story-focus { - border-color: rgba(230, 80, 37, .82); - background: #261f1a; - box-shadow: inset 0 4px var(--orange); + color: var(--vellum-dim); + border-bottom: 1px solid rgba(238, 233, 220, .07); } -.story-number { +.mode-list li::before, +.compact-list li::before, +.check-list li::before { + content: "◆"; position: absolute; - right: 24px; - top: 17px; - color: rgba(255, 255, 255, .08); - font: 500 58px/1 var(--serif); -} - -.story-track small { - color: var(--orange); - font: 700 9px var(--mono); - letter-spacing: .1em; -} - -.story-track h3 { - margin: 68px 0 18px; - font: 500 28px/1.08 var(--serif); -} - -.story-track p { - margin: 0; - color: rgba(255, 255, 255, .56); - font-size: 13px; -} - -.story-arrow { - display: grid; - place-items: center; - color: var(--orange); - font: 18px var(--mono); -} - -.table-promise { - max-width: 960px; - margin: 62px auto 0; - padding: 30px 34px; - display: grid; - grid-template-columns: 56px 1fr; - align-items: center; - border-block: 1px solid rgba(255, 255, 255, .12); -} - -.table-promise .quote-mark { - color: var(--orange); - font: 500 64px/.7 var(--serif); -} - -.table-promise p { - margin: 0; - color: rgba(255, 255, 255, .65); - font: 500 22px/1.4 var(--serif); + left: 1px; + top: 13px; + color: var(--moss-bright); + font-size: 7px; } -.table-promise strong { - color: white; - font-weight: 500; +.boundary-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); } -.roles { - padding-bottom: 130px; - display: flex; - flex-direction: column; +.boundary-card h3 { + font-size: 25px; } -.roles > .section-heading { - order: 0; +.boundary-card.warning { + border-color: rgba(217, 122, 88, .38); } -.role-grid { - order: 2; +.news-grid { display: grid; - grid-template-columns: 1fr 1fr; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 1px; + background: var(--line); border: 1px solid var(--line); } -.role-card { +.news-card { min-width: 0; - padding: 38px; -} - -.role-card + .role-card { - border-left: 1px solid var(--line); + padding: 28px; + background: var(--void); } -.role-card > header { +.news-meta { display: flex; - align-items: center; justify-content: space-between; -} - -.role-kicker { - color: var(--orange-dark); + gap: 14px; + color: var(--muted); font: 700 9px var(--mono); - letter-spacing: .12em; -} - -.role-glyph { - width: 48px; - height: 48px; - display: grid; - place-items: center; - border: 1px solid var(--line); - font: 500 27px var(--serif); + letter-spacing: .09em; } -.role-card h3 { - margin: 45px 0 38px; - font: 500 clamp(31px, 3vw, 43px)/1.02 var(--serif); - letter-spacing: -.04em; +.news-meta span:first-child { + color: var(--gold); } -.player-card { - background: var(--paper-2); +.news-card h3 { + margin: 24px 0 12px; + font: 500 25px/1.12 var(--display); } -.role-list { +.news-card p { margin: 0; - padding: 0; - list-style: none; + color: var(--vellum-dim); + font-size: 14px; } -.role-list li { - padding: 18px 0; +.page-hero { + min-height: 460px; + padding-block: 104px 82px; display: grid; - grid-template-columns: 36px 1fr; - gap: 14px; - border-top: 1px solid var(--line); -} - -.role-list li > b { - color: var(--orange-dark); - font: 700 9px var(--mono); -} - -.role-list strong { - display: block; - font: 650 14px var(--sans); -} - -.role-list p { - margin: 4px 0 0; - color: rgba(23, 26, 24, .58); - font-size: 12px; -} - -.hosting-modes { - order: 1; - margin: 0 0 92px; - scroll-margin-top: 110px; -} - -.hosting-modes > header { - max-width: 820px; - margin: 0 auto 44px; - text-align: center; -} - -.hosting-modes > header h2 { - margin: 16px 0; - font: 500 clamp(38px, 4.6vw, 61px)/1 var(--serif); - letter-spacing: -.045em; -} - -.hosting-modes > header p { - max-width: 650px; - margin: auto; - color: rgba(23, 26, 24, .6); + grid-template-columns: minmax(0, 1fr) minmax(260px, .5fr); + align-items: end; + gap: 70px; + border-bottom: 1px solid var(--line); } -.hosting-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 14px; +.page-hero h1 { + max-width: 10em; + font-size: clamp(56px, 7vw, 94px); + line-height: .9; } -.hosting-grid article { - min-height: 280px; - padding: 34px; - display: grid; - grid-template-columns: 58px 1fr; - gap: 22px; +.page-hero-aside { + padding: 22px; + background: var(--forge); border: 1px solid var(--line); - background: rgba(255, 255, 255, .24); -} - -.hosting-grid article.autonomous-host { - color: var(--paper); - background: var(--ink); - border-color: var(--ink); -} - -.hosting-number { - width: 48px; - height: 48px; - display: grid; - place-items: center; - color: var(--orange-dark); - border: 1px solid currentColor; - border-radius: 50%; - font: 700 9px var(--mono); -} - -.autonomous-host .hosting-number { - color: var(--orange); + border-radius: 12px; } -.hosting-grid small { - color: var(--orange-dark); - font: 700 9px var(--mono); - letter-spacing: .11em; -} - -.autonomous-host small { - color: var(--orange); -} - -.hosting-grid h3 { - margin: 16px 0 12px; - font: 500 29px/1.05 var(--serif); -} - -.hosting-grid p { - margin: 0; - color: rgba(23, 26, 24, .62); - font-size: 13px; -} - -.autonomous-host p { - color: rgba(255, 255, 255, .58); -} - -.alpha-note { - margin-top: 22px; - display: flex; - align-items: center; - gap: 8px; - color: var(--sage-light); - font: 700 8px/1.4 var(--mono); - letter-spacing: .05em; -} - -.alpha-note i { - width: 6px; - height: 6px; - flex: 0 0 auto; - border-radius: 50%; - background: var(--sage); - box-shadow: 0 0 0 5px rgba(127, 146, 122, .12); -} - -.systems { - color: var(--paper); - background: var(--ink); -} - -.system-grid { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 16px; +.page-hero-aside strong { + display: block; + color: var(--gold); + font: 800 10px var(--mono); + letter-spacing: .1em; } -.system-card { - min-height: 540px; - padding: 30px; - position: relative; - overflow: hidden; - border: 1px solid rgba(255, 255, 255, .15); - background: #1d211e; +.page-hero-aside p { + margin: 12px 0 0; + color: var(--vellum-dim); + font-size: 14px; } -.system-card.featured { - border-color: rgba(230, 80, 37, .72); - background: #251f1a; - box-shadow: inset 0 4px var(--orange); +.choice-grid { + align-items: stretch; } -.system-card > header { +.choice-card { display: flex; - align-items: center; - justify-content: space-between; - color: rgba(255, 255, 255, .42); - font: 700 8px var(--mono); - letter-spacing: .08em; + flex-direction: column; } -.system-card > header em { - display: flex; - align-items: center; - gap: 7px; - color: var(--sage-light); - font-style: normal; +.choice-card .button-row { + margin-top: auto; + padding-top: 26px; } -.status-dot { - width: 6px; - height: 6px; - border-radius: 50%; - background: var(--sage); - box-shadow: 0 0 0 5px rgba(127, 146, 122, .12); +.choice-label { + color: var(--gold); + font: 800 10px var(--mono); + letter-spacing: .13em; } -.status-dot.testing { - background: var(--gold); - box-shadow: 0 0 0 5px rgba(199, 169, 104, .12); +.steps { + counter-reset: steps; + border-top: 1px solid var(--line-strong); } -.system-emblem { - width: 96px; - height: 96px; - margin: 54px 0 34px; +.step { + counter-increment: steps; + padding: 32px 0; display: grid; - place-items: center; - position: relative; - color: var(--orange); - border: 1px solid rgba(230, 80, 37, .55); - border-radius: 50%; - font: 700 19px var(--serif); - transform: rotate(-4deg); -} - -.system-emblem::before, -.system-emblem::after { - content: ""; - position: absolute; - inset: 11px; - border: 1px solid rgba(230, 80, 37, .26); - transform: rotate(45deg); -} - -.system-emblem::after { - inset: 23px; - transform: rotate(0); + grid-template-columns: 76px minmax(0, 1fr); + gap: 28px; + border-bottom: 1px solid var(--line); } -.system-emblem.rune { +.step::before { + content: counter(steps, decimal-leading-zero); color: var(--gold); - border-color: rgba(199, 169, 104, .5); -} - -.system-emblem.rune::before, -.system-emblem.rune::after { - border-color: rgba(199, 169, 104, .22); + font: 800 12px var(--mono); } -.system-card h3 { - max-width: 500px; - margin: 0 0 16px; - font: 500 clamp(30px, 3.4vw, 43px)/1.03 var(--serif); - letter-spacing: -.035em; +.step h3 { + margin: 0 0 9px; + font: 500 27px/1.1 var(--display); } -.system-card > p { - max-width: 520px; +.step p { margin: 0; - color: rgba(255, 255, 255, .55); - font-size: 13px; -} - -.system-card ul { - margin: 34px 0 0; - padding: 0; - display: grid; - grid-template-columns: repeat(3, 1fr); - list-style: none; -} - -.system-card li { - min-height: 66px; - padding: 10px; - display: grid; - place-items: center; - border: 1px solid rgba(255, 255, 255, .09); - border-right: 0; - color: rgba(255, 255, 255, .46); - text-align: center; - font: 700 8px/1.4 var(--mono); -} - -.system-card li:last-child { - border-right: 1px solid rgba(255, 255, 255, .09); + color: var(--vellum-dim); } -.experiment-note { - margin-top: 18px; - padding: 24px 28px; - display: grid; - grid-template-columns: 150px 1fr auto; - align-items: center; - gap: 28px; - border: 1px solid rgba(255, 255, 255, .14); - border-left: 4px solid var(--orange); - background: rgba(255, 255, 255, .035); +.code-block { + margin: 20px 0 0; + overflow: auto; + color: #dfe5dc; + background: #090a09; + border: 1px solid var(--line); + border-radius: 10px; } -.lab-tag { - display: inline-flex; - align-items: center; - gap: 9px; - color: var(--orange); +.code-block header { + padding: 9px 13px; + display: flex; + justify-content: space-between; + gap: 12px; + color: var(--muted); + background: var(--forge); + border-bottom: 1px solid var(--line); font: 700 9px var(--mono); - letter-spacing: .1em; -} - -.lab-tag i { - width: 7px; - height: 7px; - border: 1px solid var(--orange); - transform: rotate(45deg); -} - -.experiment-note h3 { - margin: 0 0 5px; - font: 500 23px var(--serif); } -.experiment-note p { - max-width: 780px; +.code-block pre { margin: 0; - color: rgba(255, 255, 255, .52); - font-size: 12px; + padding: 18px; + overflow: auto; + font: 13px/1.7 var(--mono); } -.experiment-code { - color: rgba(255, 255, 255, .3); - font: 700 8px var(--mono); - letter-spacing: .08em; +.profile-table, +.repo-ledger { + width: 100%; + border-collapse: collapse; + border-top: 1px solid var(--line-strong); } -.boundaries { - padding-bottom: 128px; +.profile-table th, +.profile-table td { + padding: 16px 18px; + border-bottom: 1px solid var(--line); + text-align: left; } -.boundary-grid { - display: grid; - grid-template-columns: repeat(4, 1fr); - border: 1px solid var(--line); +.profile-table th { + color: var(--muted); + font: 800 9px var(--mono); + letter-spacing: .1em; + text-transform: uppercase; } -.boundary-grid article { - min-height: 290px; - padding: 28px; - border-right: 1px solid var(--line); +.profile-table td:first-child { + color: var(--gold-bright); + font: 700 12px var(--mono); } -.boundary-grid article:last-child { - border-right: 0; +.transport-grid { + display: grid; + grid-template-columns: repeat(3, minmax(0, 1fr)); + gap: 12px; } -.boundary-grid article > span { - width: 38px; - height: 38px; - display: grid; - place-items: center; - color: var(--orange-dark); +.transport { + padding: 22px; + background: var(--forge); border: 1px solid var(--line); - font: 700 8px var(--mono); + border-radius: 11px; } -.boundary-grid h3 { - margin: 56px 0 13px; - font: 500 24px/1.1 var(--serif); +.transport code { + color: var(--gold-bright); + font: 700 12px var(--mono); } -.boundary-grid p { - margin: 0; - color: rgba(23, 26, 24, .59); - font-size: 12px; +.transport p { + margin: 12px 0 0; + color: var(--vellum-dim); + font-size: 14px; } -.boundary-grid .boundary-experimental { - background: var(--paper-2); -} - -.boundary-grid .boundary-experimental > span { - color: var(--orange); - border-color: var(--orange); -} - -.progress { - background: var(--paper-2); - border-block: 1px solid var(--line); -} - -.news-grid { - display: grid; - grid-template-columns: repeat(3, 1fr); - border: 1px solid var(--line); -} - -.news-card { - min-height: 285px; - padding: 28px; - border-right: 1px solid var(--line); -} - -.news-card:last-child { - border-right: 0; -} - -.news-card header { - display: flex; - justify-content: space-between; - color: rgba(23, 26, 24, .4); - font: 700 8px var(--mono); - letter-spacing: .08em; -} - -.news-card header span { - color: var(--orange-dark); -} - -.news-card h3 { - margin: 54px 0 12px; - font: 500 24px/1.1 var(--serif); -} - -.news-card > p { - margin: 0; - color: rgba(23, 26, 24, .58); - font-size: 12px; -} - -.developer-bridge { - min-height: 310px; - margin-top: 80px; - padding: 42px; - display: grid; - grid-template-columns: 100px 1fr auto; - align-items: center; - gap: 34px; - color: var(--paper); - background: var(--ink); -} - -.bridge-mark { - width: 82px; - height: 82px; - display: grid; - place-items: center; - color: var(--orange); - border: 1px solid rgba(255, 255, 255, .16); - font: 500 22px var(--mono); - transform: rotate(-3deg); -} - -.developer-bridge h2 { - margin: 12px 0 10px; - font: 500 clamp(31px, 4vw, 49px)/1 var(--serif); - letter-spacing: -.04em; -} - -.developer-bridge p { - max-width: 680px; - margin: 0; - color: rgba(255, 255, 255, .55); - font-size: 13px; -} - -.bridge-actions { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 15px; -} - -.bridge-actions > a:last-child { - color: rgba(255, 255, 255, .55); - text-decoration: none; - font: 700 9px var(--mono); -} - -.closing, -.dev-closing { - min-height: 500px; - padding: 82px max(24px, calc((100vw - 1240px) / 2)); - display: grid; - grid-template-columns: 140px 1fr auto; - align-items: center; - gap: 46px; - position: relative; - overflow: hidden; - color: var(--paper); - background: var(--ink); -} - -.closing-mark { - width: 118px; - height: 118px; - display: grid; - place-items: center; - position: relative; - z-index: 1; - overflow: hidden; - color: var(--orange); - background: var(--paper); - border: 1px solid rgba(255, 255, 255, .18); - transform: rotate(-3deg); -} - -.closing-mark img { - width: 100%; - height: 100%; - object-fit: cover; -} - -.closing-rings { - width: 620px; - height: 620px; - position: absolute; - right: -250px; - border: 1px solid rgba(255, 255, 255, .07); - border-radius: 50%; - box-shadow: - 0 0 0 72px rgba(255, 255, 255, .022), - 0 0 0 144px rgba(255, 255, 255, .014); -} - -.closing h2, -.dev-closing h2 { - margin: 14px 0; - font: 500 clamp(43px, 5.4vw, 70px)/.98 var(--serif); - letter-spacing: -.045em; -} - -.closing p { - max-width: 640px; - margin: 0; - color: rgba(255, 255, 255, .55); -} - -.closing-actions { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 16px; - position: relative; - z-index: 1; -} - -.closing-actions > a:last-child { - color: rgba(255, 255, 255, .56); - text-decoration: none; - font: 700 9px var(--mono); -} - -.footer { - min-height: 126px; - display: flex; - align-items: center; - justify-content: space-between; - gap: 30px; -} - -.footer p { - color: rgba(23, 26, 24, .43); - text-align: center; - font: 700 8px/1.5 var(--mono); - letter-spacing: .08em; -} - -.footer-links { - display: flex; - flex-wrap: wrap; - gap: 18px; -} - -.footer-links a { - text-decoration: none; - font: 700 9px var(--mono); -} - -.footer-links a:hover { - color: var(--orange); -} - -/* Developer page */ - -.developer-main { - background: var(--paper); -} - -.dev-hero { - min-height: 720px; - padding-block: 70px 92px; - display: grid; - grid-template-columns: 1fr .82fr; - align-items: center; - gap: 80px; - position: relative; -} - -.dev-hero h1 { - font-size: clamp(50px, 5.6vw, 82px); -} - -.dev-status-card { - padding: 18px; - color: var(--paper); - background: var(--ink); - box-shadow: 20px 24px 0 var(--paper-2), 20px 24px 0 1px var(--line); -} - -.dev-status-card > header { - padding: 3px 3px 15px; - display: flex; - justify-content: space-between; - border-bottom: 1px solid rgba(255, 255, 255, .12); - color: rgba(255, 255, 255, .42); - font: 700 8px var(--mono); - letter-spacing: .1em; -} - -.dev-status-card > header em { - color: var(--orange); - font-style: normal; -} - -.dev-status-card > div { - min-height: 90px; - padding: 18px 10px; - display: grid; - grid-template-columns: 12px 1fr auto; - align-items: center; - gap: 12px; - border-bottom: 1px solid rgba(255, 255, 255, .09); -} - -.status-signal { - width: 7px; - height: 7px; - border-radius: 50%; -} - -.status-signal.live { - background: var(--sage); - box-shadow: 0 0 0 6px rgba(127, 146, 122, .12); -} - -.status-signal.testing { - background: var(--gold); - box-shadow: 0 0 0 6px rgba(199, 169, 104, .12); -} - -.status-signal.experimental { - background: var(--orange); - box-shadow: 0 0 0 6px rgba(230, 80, 37, .12); -} - -.dev-status-card p { - margin: 0; - display: grid; -} - -.dev-status-card p b { - font: 700 10px var(--mono); -} - -.dev-status-card p small { - margin-top: 4px; - color: rgba(255, 255, 255, .36); - font: 700 7px var(--mono); - letter-spacing: .06em; -} - -.dev-status-card > div > strong { - color: rgba(255, 255, 255, .42); - font: 700 8px var(--mono); -} - -.dev-status-card footer { - padding: 15px 8px 3px; - color: rgba(255, 255, 255, .34); - font: 700 7px/1.5 var(--mono); -} - -.dev-principles { - color: var(--paper); - background: var(--ink); -} - -.dev-principles .section-heading p { - color: rgba(255, 255, 255, .55); -} - -.dev-principle-grid { - display: grid; - grid-template-columns: repeat(4, 1fr); - border: 1px solid rgba(255, 255, 255, .14); -} - -.dev-principle-grid article { - min-height: 250px; - padding: 28px; - border-right: 1px solid rgba(255, 255, 255, .12); -} - -.dev-principle-grid article:last-child { - border-right: 0; -} - -.dev-principle-grid article > span { - color: var(--orange); - font: 500 44px var(--serif); -} - -.dev-principle-grid h3 { - margin: 38px 0 12px; - color: var(--sage-light); - font: 700 11px var(--mono); -} - -.dev-principle-grid p { - margin: 0; - color: rgba(255, 255, 255, .52); - font-size: 12px; -} - -.dev-architecture { - padding-bottom: 130px; -} - -.architecture-stack { - max-width: 980px; - margin: auto; -} - -.architecture-stack article { - min-height: 112px; - padding: 20px 26px 20px 92px; - display: grid; - align-content: center; - position: relative; - border: 1px solid var(--line); - background: rgba(255, 255, 255, .25); -} - -.architecture-stack article > span { - width: 66px; - position: absolute; - inset: 0 auto 0 0; - display: grid; - place-items: center; - color: var(--orange-dark); - border-right: 1px solid var(--line); - font: 700 9px var(--mono); -} - -.architecture-stack article small { - color: rgba(23, 26, 24, .43); - font: 700 8px var(--mono); - letter-spacing: .11em; -} - -.architecture-stack article h3 { - margin: 4px 0 1px; - font: 500 24px var(--serif); -} - -.architecture-stack article p { - margin: 0; - color: rgba(23, 26, 24, .55); - font-size: 11px; -} - -.architecture-stack article.stack-focus { - color: var(--paper); - background: var(--ink); - border-color: var(--ink); -} - -.architecture-stack article.stack-focus > span { - color: var(--orange); - border-color: rgba(255, 255, 255, .14); -} - -.architecture-stack article.stack-focus small, -.architecture-stack article.stack-focus p { - color: rgba(255, 255, 255, .48); -} - -.stack-connector { - height: 62px; - display: grid; - place-items: center; - position: relative; -} - -.stack-connector i { - width: 1px; - height: 100%; - background: var(--line); -} - -.stack-connector code { - position: absolute; - padding: 3px 9px; - color: rgba(23, 26, 24, .46); - background: var(--paper); - font: 8px var(--mono); -} - -.stack-pair { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 14px; -} - -.stack-pair article { - min-height: 126px; -} - -.dev-experiment { - color: var(--paper); - background: #211c18; -} - -.experiment-panel { - display: grid; - grid-template-columns: .9fr 1.1fr; - gap: 92px; -} - -.experiment-title h2 { - margin: 20px 0 0; - font: 500 clamp(37px, 4.5vw, 58px)/1 var(--serif); - letter-spacing: -.04em; -} - -.experiment-details > p { - margin: 0 0 30px; - color: rgba(255, 255, 255, .58); - font-size: 15px; -} - -.experiment-details ul { - margin: 0; - padding: 0; - list-style: none; -} - -.experiment-details li { - padding: 14px 0; - border-top: 1px solid rgba(255, 255, 255, .12); - color: rgba(255, 255, 255, .7); - font-size: 12px; -} - -.experiment-details li::before { - content: "◆"; - margin-right: 10px; - color: var(--orange); - font-size: 6px; -} - -.repositories { - padding-bottom: 130px; -} - -.repo-groups { - border: 1px solid var(--line); -} - -.repo-group { - display: grid; - grid-template-columns: 310px 1fr; - border-bottom: 1px solid var(--line); -} - -.repo-group:last-child { - border-bottom: 0; -} - -.repo-group > header { - padding: 30px; - display: grid; - grid-template-columns: 44px 1fr; - gap: 18px; - border-right: 1px solid var(--line); - background: var(--paper-2); -} - -.repo-group > header > span { - color: var(--orange-dark); - font: 700 9px var(--mono); -} - -.repo-group > header small { - color: rgba(23, 26, 24, .43); - font: 700 8px var(--mono); - letter-spacing: .08em; -} - -.repo-group > header h3 { - margin: 9px 0 0; - font: 500 22px/1.08 var(--serif); -} - -.repo-row { - min-height: 102px; - padding: 20px 24px; - display: grid; - grid-template-columns: 1fr 92px 20px; - align-items: center; - gap: 14px; - border-bottom: 1px solid var(--line); - text-decoration: none; - transition: background .2s ease, color .2s ease; -} - -.repo-row:last-child { - border-bottom: 0; -} - -.repo-row:hover { - color: var(--paper); - background: var(--ink); -} - -.repo-row strong { - font: 700 13px var(--mono); -} - -.repo-row p { - margin: 4px 0 0; - color: rgba(23, 26, 24, .5); - font-size: 11px; -} - -.repo-row:hover p { - color: rgba(255, 255, 255, .48); -} - -.repo-row em { - color: var(--orange-dark); - font: 700 8px var(--mono); - font-style: normal; - text-align: right; -} - -.repo-row b { - color: var(--orange); -} - -.dev-closing { - min-height: 410px; - grid-template-columns: 1fr auto; -} - -.dev-closing h2 { - font-size: clamp(42px, 5vw, 64px); -} - -/* Get started */ - -.start-main { - background: var(--paper); -} - -.start-hero { - min-height: 720px; - padding-block: 76px 100px; - display: grid; - grid-template-columns: 1fr .82fr; - align-items: center; - gap: 82px; -} - -.start-hero h1 { - margin: 22px 0 0; - font: 500 clamp(54px, 6vw, 84px)/.94 var(--serif); - letter-spacing: -.055em; -} - -.start-hero h1 em { - color: var(--orange-dark); - font-weight: 400; -} - -.start-contract { - padding: 22px; - color: var(--paper); - background: var(--ink); - border: 1px solid var(--ink); - box-shadow: 20px 24px 0 var(--paper-2), 20px 24px 0 1px var(--line); -} - -.start-contract header, -.terminal-card header { - padding-bottom: 14px; - display: flex; - justify-content: space-between; - gap: 20px; - border-bottom: 1px solid var(--line-dark); - color: rgba(255, 255, 255, .48); - font: 700 9px var(--mono); - letter-spacing: .1em; -} - -.start-contract header strong { - color: var(--orange); -} - -.start-contract > div { - min-height: 92px; - padding: 18px; - display: grid; - align-content: center; - gap: 5px; - border: 1px solid var(--line-dark); -} - -.start-contract > div b { - color: var(--orange); - font: 700 10px var(--mono); - letter-spacing: .1em; -} - -.start-contract > div span { - color: rgba(255, 255, 255, .62); - font-size: 12px; -} - -.start-contract > div.contract-focus { - color: var(--ink); - background: var(--paper); - border-color: var(--paper); -} - -.start-contract > div.contract-focus span { - color: rgba(23, 26, 24, .62); -} - -.start-contract > i { - height: 28px; - display: grid; - place-items: center; - color: var(--sage-light); - font-style: normal; -} - -.start-contract footer { - padding-top: 16px; - color: rgba(255, 255, 255, .46); - font: 700 9px/1.6 var(--mono); -} - -.path-section { - color: var(--paper); - background: var(--ink); -} - -.path-section .section-heading p { - color: rgba(255, 255, 255, .56); -} - -.path-grid { - display: grid; - grid-template-columns: repeat(3, 1fr); - border: 1px solid var(--line-dark); -} - -.path-grid article { - min-height: 390px; - padding: 30px; - display: flex; - flex-direction: column; - border-right: 1px solid var(--line-dark); -} - -.path-grid article:last-child { - border-right: 0; +.repo-row { + padding: 22px 0; + display: grid; + grid-template-columns: 84px 240px minmax(0, 1fr) auto; + align-items: center; + gap: 24px; + border-bottom: 1px solid var(--line); } -.path-grid article > span { - color: var(--orange); - font: 700 9px var(--mono); - letter-spacing: .1em; +.repo-row > span:first-child { + color: var(--muted); + font: 800 9px var(--mono); + text-transform: uppercase; } -.path-grid h3 { - margin: 62px 0 18px; - font: 500 30px/1.04 var(--serif); +.repo-row a { + width: max-content; + max-width: 100%; + color: var(--gold-bright); + text-decoration-thickness: 1px; + text-underline-offset: 4px; + font: 700 13px var(--mono); + overflow-wrap: anywhere; } -.path-grid p { +.repo-row p { margin: 0; - color: rgba(255, 255, 255, .55); - font-size: 13px; + color: var(--vellum-dim); } -.path-grid a { - margin-top: auto; - color: var(--sage-light); - text-decoration: none; - font: 700 10px var(--mono); +.repo-row .tag { + color: var(--moss-bright); } -.path-grid a:hover { - color: var(--orange); +.authority-map { + display: grid; + grid-template-columns: repeat(5, minmax(0, 1fr)); + gap: 9px; } -.local-section { - background: var(--paper-2); +.authority-node { + min-height: 126px; + padding: 18px; + display: flex; + flex-direction: column; + justify-content: space-between; + background: var(--forge); + border: 1px solid var(--line); + border-radius: 10px; } -.terminal-card { - max-width: 1000px; - margin: 0 auto 18px; - padding: 22px; - color: var(--paper); - background: var(--ink); +.authority-node strong { + color: var(--gold-bright); + font: 700 12px var(--mono); } -.terminal-card header b { - color: var(--sage-light); - font-weight: 700; +.authority-node span { + color: var(--muted); + font-size: 12px; } -.terminal-card pre, -.profile-grid pre { - margin: 0; - overflow-x: auto; +.authority-node.primary { + color: #17140f; + background: var(--gold); + border-color: var(--gold); } -.terminal-card code, -.profile-grid code { - display: block; - padding: 20px 0 2px; - color: var(--paper); - font: 12px/1.8 var(--mono); +.authority-node.primary strong, +.authority-node.primary span { + color: #17140f; } -.profile-grid { - max-width: 1000px; - margin: 0 auto 18px; +.stat-strip { display: grid; - grid-template-columns: 1fr 1fr; + grid-template-columns: repeat(4, minmax(0, 1fr)); border: 1px solid var(--line); + border-radius: 14px; + overflow: hidden; } -.profile-grid article { - min-width: 0; - padding: 24px; +.stat { + min-height: 150px; + padding: 26px; + display: flex; + flex-direction: column; + justify-content: space-between; + background: var(--forge); border-right: 1px solid var(--line); - border-bottom: 1px solid var(--line); - background: rgba(255, 255, 255, .25); } -.profile-grid article:nth-child(2n) { +.stat:last-child { border-right: 0; } -.profile-grid article:nth-last-child(-n + 2) { - border-bottom: 0; -} - -.profile-grid header { - display: flex; - justify-content: space-between; - gap: 20px; +.stat strong { + color: var(--gold-bright); + font: 500 48px/1 var(--display); } -.profile-grid header strong { - font: 700 12px var(--mono); +.stat span { + color: var(--muted); + font: 700 10px/1.5 var(--mono); } -.profile-grid header span { - color: var(--orange-dark); - font: 700 9px var(--mono); +.callout { + padding: clamp(24px, 4vw, 44px); + display: grid; + grid-template-columns: minmax(0, 1fr) auto; + align-items: center; + gap: 40px; + background: linear-gradient(120deg, rgba(217, 173, 91, .16), rgba(113, 141, 106, .07)); + border: 1px solid rgba(217, 173, 91, .34); + border-radius: 14px; } -.profile-grid p { - min-height: 48px; - margin: 12px 0 2px; - color: rgba(23, 26, 24, .58); - font-size: 12px; +.callout h2 { + margin: 0; + font: 500 clamp(30px, 4vw, 49px)/1 var(--display); } -.profile-grid pre { - padding: 0 14px 14px; - background: var(--ink); +.callout p { + max-width: 720px; + margin: 14px 0 0; + color: var(--vellum-dim); } -.terminal-final p { - margin: 16px 0 0; - color: rgba(255, 255, 255, .52); - font-size: 11px; +.prose { + width: min(860px, calc(100% - 48px)); + margin: 0 auto; + padding-block: 92px 120px; } -.start-actions { - max-width: 1000px; - margin: 28px auto 0; - display: flex; - gap: 12px; +.prose h1 { + font-size: clamp(50px, 7vw, 80px); + line-height: .95; } -.client-section { - background: var(--paper); +.prose .lede { + margin: 24px 0 54px; + color: var(--vellum-dim); + font-size: 19px; } -.client-panel { - display: grid; - grid-template-columns: .92fr 1.08fr; - align-items: center; - gap: 90px; +.prose section { + margin-top: 48px; + padding-top: 34px; + border-top: 1px solid var(--line); } -.client-panel h2 { - margin: 20px 0; - font: 500 clamp(42px, 5vw, 66px)/.98 var(--serif); - letter-spacing: -.04em; +.prose h2 { + margin: 0 0 16px; + font: 500 30px/1.1 var(--display); } -.client-panel p { - color: rgba(23, 26, 24, .62); +.prose p, +.prose li { + color: var(--vellum-dim); } -.contract-list { - border: 1px solid var(--line); +.prose a { + color: var(--gold-bright); + text-underline-offset: 4px; } -.contract-list > div { - min-height: 116px; - padding: 24px; +.not-found { + min-height: 70vh; display: grid; - align-content: center; - gap: 8px; - border-bottom: 1px solid var(--line); + place-items: center; + text-align: center; } -.contract-list > div:last-child { - border-bottom: 0; +.not-found strong { + display: block; + color: rgba(217, 173, 91, .2); + font: 500 clamp(120px, 26vw, 280px)/.72 var(--display); } -.contract-list b { - color: var(--orange-dark); - font: 700 10px var(--mono); +.not-found h1 { + margin: -10px 0 12px; + font: 500 clamp(38px, 6vw, 64px)/1 var(--display); } -.contract-list span { - color: rgba(23, 26, 24, .58); - font: 12px var(--mono); +.not-found p { + margin: 0; + color: var(--vellum-dim); } -.start-closing { - min-height: 440px; - padding: 76px max(24px, calc((100vw - 1240px) / 2)); +.footer { + min-height: 210px; + padding-block: 46px; display: grid; grid-template-columns: 1fr auto; align-items: center; - gap: 70px; - color: var(--paper); - background: var(--ink); + gap: 32px; + border-top: 1px solid var(--line-strong); } -.start-closing h2 { - margin: 16px 0; - font: 500 clamp(43px, 5vw, 66px)/.98 var(--serif); - letter-spacing: -.045em; +.footer-brand p { + margin: 14px 0 0; + color: var(--muted); + font-size: 13px; } -.start-closing p { - max-width: 700px; - color: rgba(255, 255, 255, .55); +.footer-status { + display: flex; + align-items: center; + gap: 10px; + color: var(--moss-bright); + font: 750 10px var(--mono); } -/* Keep supporting copy compact without making it decorative or hard to scan. */ -.topbar nav a, -.language, -.button, -.eyebrow, -.section-index { - font-size: 12px; +.footer nav { + grid-column: 1 / -1; + display: flex; + flex-wrap: wrap; + gap: 9px 24px; } -.promise-row, -.story-track small, -.role-kicker, -.role-list li > b, -.hosting-grid small, -.lab-tag, -.bridge-actions > a:last-child, -.closing-actions > a:last-child, -.footer p, -.footer-links a, -.dev-status-card p b, -.dev-principle-grid h3 { - font-size: 11px; +.footer nav a { + color: var(--muted); + text-decoration: none; + font: 700 10px var(--mono); } -.window-head, -.scene-banner small, -.perspective small, -.perspective header em, -.choice-node, -.choice-node b, -.alpha-note, -.system-card > header, -.system-card li, -.experiment-code, -.boundary-grid article > span, -.news-card header, -.dev-status-card > header, -.dev-status-card p small, -.dev-status-card > div > strong, -.dev-status-card footer, -.architecture-stack article > span, -.architecture-stack article small, -.stack-connector code, -.repo-group > header > span, -.repo-group > header small, -.repo-row em { - font-size: 10px; -} - -.scene-banner p, -.architecture-stack article p, -.repo-row p { - font-size: 13px; +.footer nav a:hover { + color: var(--gold-bright); } -.story-track p, -.role-list p, -.hosting-grid p, -.system-card > p, -.experiment-note p, -.boundary-grid p, -.news-card > p, -.developer-bridge p, -.dev-principle-grid p, -.experiment-details li, -.repo-row strong { - font-size: 14px; +.js .hero [data-reveal], +.js .page-hero [data-reveal] { + animation: reveal-in .58s ease both; } -.avatar, -.perspective header strong { - font-size: 11px; +.js .hero [data-reveal]:nth-child(2), +.js .page-hero [data-reveal]:nth-child(2) { + animation-delay: .08s; } -.perspective li { - font-size: 12px; +@keyframes reveal-in { + from { + opacity: 0; + transform: translateY(16px); + } + to { + opacity: 1; + transform: none; + } } -@media (max-width: 1120px) { - .topbar nav { +@media (max-width: 1040px) { + .topbar { + grid-template-columns: 1fr auto; + } + + .desktop-nav { display: none; } @@ -2206,462 +1232,215 @@ button:focus-visible { display: block; } - .mobile-menu nav { - display: grid; - } - - .hero, - .dev-hero, - .start-hero { + .hero { grid-template-columns: 1fr; - gap: 70px; + gap: 58px; } - .hero-copy { - max-width: 820px; - padding-top: 24px; + .hero-copy h1 { + max-width: 10em; } - .campaign-window, - .dev-status-card, - .start-contract { - width: min(760px, 100%); + .contract-map { + max-width: 760px; margin-inline: auto; } .boundary-grid, - .dev-principle-grid { - grid-template-columns: 1fr 1fr; - } - - .boundary-grid article:nth-child(2), - .dev-principle-grid article:nth-child(2) { - border-right: 0; - } - - .boundary-grid article:nth-child(-n+2), - .dev-principle-grid article:nth-child(-n+2) { - border-bottom: 1px solid var(--line); - } - - .dev-principle-grid article:nth-child(-n+2) { - border-bottom-color: rgba(255, 255, 255, .12); - } - - .developer-bridge { - grid-template-columns: 90px 1fr; - } - - .bridge-actions { - grid-column: 2; - } - - .repo-group { - grid-template-columns: 260px 1fr; - } -} - -@media (max-width: 900px) { - .section { - padding-block: 92px; - } - - .split-heading, - .experiment-panel, - .client-panel { - grid-template-columns: 1fr; - gap: 24px; - } - - .path-grid { - grid-template-columns: 1fr; - } - - .path-grid article { - min-height: 300px; - border-right: 0; - border-bottom: 1px solid var(--line-dark); - } - - .path-grid article:last-child { - border-bottom: 0; - } - - .story-track { - grid-template-columns: 1fr; - gap: 12px; - } - - .story-arrow { - height: 24px; - transform: rotate(90deg); - } - - .role-grid, - .hosting-grid, - .system-grid, .news-grid { - grid-template-columns: 1fr; - } - - .role-card + .role-card, - .news-card { - border-left: 0; - border-right: 0; - border-top: 1px solid var(--line); - } - - .news-card:first-child { - border-top: 0; - } - - .system-card { - min-height: auto; - } - - .experiment-note { - grid-template-columns: 1fr; - gap: 14px; - } - - .experiment-code { - display: none; + grid-template-columns: 1fr 1fr; } - .closing, - .dev-closing, - .start-closing { - grid-template-columns: 100px 1fr; + .news-card:last-child { + grid-column: 1 / -1; } - .closing-actions { - grid-column: 2; + .repo-row { + grid-template-columns: 70px 220px minmax(0, 1fr); } - .repo-group { - grid-template-columns: 1fr; + .repo-row .tag { + grid-column: 2 / -1; } - .repo-group > header { - border-right: 0; - border-bottom: 1px solid var(--line); + .authority-map { + grid-template-columns: repeat(3, 1fr); } } -@media (max-width: 680px) { - .shell { - width: min(100% - 28px, 1240px); - } - - .topbar { - width: calc(100% - 16px); - height: 60px; - margin-top: 8px; - padding: 0 11px; - top: 8px; - } - - .hero, - .dev-hero, - .start-hero { - min-height: auto; - padding-block: 64px 78px; - gap: 58px; - } - - .hero h1, - .dev-hero h1, - .start-hero h1, - [data-lang="en"] .hero h1, - [data-lang="en"] .dev-hero h1, - [data-lang="en"] .start-hero h1 { - font-size: 48px; - line-height: .94; - } - - .hero h1 em, - .dev-hero h1 em, - .start-hero h1 em { - width: auto; - font-size: .72em; - white-space: normal; - } - - .lede { - margin-top: 24px; +@media (max-width: 720px) { + body { font-size: 15px; } - .hero-actions { - display: grid; + .shell, + .prose { + width: min(100% - 30px, 1180px); } - .button { - width: 100%; + .topbar { + width: calc(100% - 20px); + margin-top: 10px; + top: 10px; + padding-left: 12px; } - .promise-row { - display: grid; + .brand span { + font-size: 20px; } - .campaign-window { - padding: 10px; - box-shadow: 10px 13px 0 var(--paper-2), 10px 13px 0 1px var(--line); - transform: none; + .github-link { + display: none; } - .start-contract { - padding: 14px; - box-shadow: 10px 13px 0 var(--paper-2), 10px 13px 0 1px var(--line); + .section { + padding-block: 78px; } - .profile-grid { + .section-head.split, + .page-hero, + .mode-grid, + .choice-grid, + .feature-grid, + .boundary-grid, + .news-grid, + .callout { grid-template-columns: 1fr; } - .profile-grid article, - .profile-grid article:nth-child(2n), - .profile-grid article:nth-last-child(-n + 2) { - border-right: 0; - border-bottom: 1px solid var(--line); - } - - .profile-grid article:last-child { - border-bottom: 0; - } - - .start-actions { - display: grid; - } - - .terminal-card code, - .profile-grid code { - font-size: 10px; - } - - .window-head { - grid-template-columns: 1fr auto; - } - - .window-head > span { - display: none !important; - } - - .scene-banner { - min-height: 128px; - padding: 20px; + .page-hero { + min-height: 0; + padding-block: 84px 60px; + gap: 34px; } - .scene-banner h2 { - font-size: 25px; + .hero { + min-height: 0; + padding-block: 76px 84px; } - .scene-mark { - font-size: 44px; + .hero-copy h1 { + font-size: clamp(54px, 17vw, 78px); } - .perspective-grid { + .contract-body { grid-template-columns: 1fr; + gap: 14px; } - .choice-thread { - grid-template-columns: 1fr; - gap: 9px; + .contract-junction { + min-height: 38px; } - .choice-thread > i { + .contract-junction::before { width: 1px; - height: 18px; - margin-left: 11px; - } - - .window-seal { - width: 64px; - height: 64px; - right: -6px; - bottom: -28px; + height: 100%; } - .section { - padding-block: 78px; - } - - .section-heading { - margin-bottom: 40px; + .contract-junction::after { + display: none; } - .section-heading h2 { - font-size: 40px; + .runtime-grid { + grid-template-columns: repeat(3, minmax(0, 1fr)); } - .story-track article { - min-height: 290px; - padding: 26px; + .contract-foot { + flex-direction: column; } - .table-promise { + .ledger-row { grid-template-columns: 1fr; - padding-inline: 12px; - } - - .table-promise p { - font-size: 20px; - } - - .role-card { - padding: 28px; - } - - .role-card h3 { - margin-block: 38px 28px; - font-size: 33px; + gap: 10px; } - .hosting-modes { - margin-top: 70px; + .ledger-row .state { + margin-top: 4px; } - .hosting-modes > header h2 { - font-size: 38px; + .news-card:last-child { + grid-column: auto; } - .hosting-grid article { + .transport-grid, + .authority-map, + .stat-strip { grid-template-columns: 1fr; - padding: 28px; - } - - .system-card { - padding: 24px; } - .system-emblem { - margin-top: 40px; - } - - .system-card ul { + .repo-row { grid-template-columns: 1fr; + gap: 9px; } - .system-card li, - .system-card li:last-child { - min-height: 48px; - border-right: 1px solid rgba(255, 255, 255, .09); - border-bottom: 0; - } - - .system-card li:last-child { - border-bottom: 1px solid rgba(255, 255, 255, .09); - } - - .experiment-note { - padding: 22px; - } - - .boundary-grid, - .dev-principle-grid { - grid-template-columns: 1fr; + .repo-row .tag { + grid-column: auto; } - .boundary-grid article, - .boundary-grid article:nth-child(2), - .dev-principle-grid article, - .dev-principle-grid article:nth-child(2) { - min-height: 245px; + .stat { + min-height: 112px; border-right: 0; border-bottom: 1px solid var(--line); } - .dev-principle-grid article, - .dev-principle-grid article:nth-child(2) { - border-bottom-color: rgba(255, 255, 255, .12); - } - - .boundary-grid article:last-child, - .dev-principle-grid article:last-child { + .stat:last-child { border-bottom: 0; } - .developer-bridge { - margin-top: 58px; - padding: 30px 24px; - grid-template-columns: 1fr; - } - - .bridge-mark { - width: 66px; - height: 66px; - } - - .bridge-actions { - grid-column: auto; - } - - .closing, - .dev-closing, - .start-closing { - min-height: 570px; - padding-block: 70px; - grid-template-columns: 1fr; - gap: 28px; - } - - .closing-mark { - width: 80px; - height: 80px; - font-size: 50px; + .callout { + gap: 12px; } - .closing h2, - .dev-closing h2 { - font-size: 45px; + .step { + grid-template-columns: 44px minmax(0, 1fr); + gap: 12px; } - .closing-actions { - grid-column: auto; + .profile-table { + display: block; + overflow-x: auto; } .footer { - padding-block: 32px; - flex-direction: column; - align-items: flex-start; - } - - .footer p { - text-align: left; - } - - .dev-status-card { - box-shadow: 10px 13px 0 var(--paper-2), 10px 13px 0 1px var(--line); - } - - .stack-pair { grid-template-columns: 1fr; + align-items: start; } - .architecture-stack article { - padding-left: 78px; + .footer nav { + grid-column: auto; } +} - .architecture-stack article > span { - width: 54px; +@media (prefers-reduced-motion: reduce) { + *, + *::before, + *::after { + scroll-behavior: auto !important; + animation-duration: .001ms !important; + animation-iteration-count: 1 !important; + transition-duration: .001ms !important; } - .repo-row { - grid-template-columns: 1fr auto; + .js .hero [data-reveal], + .js .page-hero [data-reveal] { + animation: none; } +} - .repo-row em { - display: none; +@media print { + .topbar, + .footer, + .ambient-grid, + .button-row { + display: none !important; } -} -@media (prefers-reduced-motion: reduce) { - html { - scroll-behavior: auto; + body { + color: #111; + background: white; } - [data-reveal], - .button { - transition: none; + a { + color: inherit; } }