diff --git a/.gitignore b/.gitignore index 72fa8d5..42a23b0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,3 @@ -.DS_Store -node_modules/ -.vscode/ -.idea/ -*.log -.env -.env.local -dist/ -build/ +.vercel/ +.gitignore.local +/tmp/ diff --git a/README.md b/README.md index f6998d4..d071810 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,10 @@ # Token-Ignition -> The task is the interview. Pass it, and you join the research. +> Model horizons double every four months. The scaffolds around them do not. That gap is the work. Token-Ignition is a **selection gate for AI-native researchers**. -We don't hire on résumés, pitches, or intro calls. We invert the interview: you define a task, you build a system, and if that system can evolve itself and clear an AI-audited gate, you're invited into the research group. +We don't hire on résumés, pitches, or intro calls. We invert the interview: you define a long-horizon task, you build a scaffold that evolves itself, and if that scaffold clears an AI-audited gate, you're invited into the research group. Tokens are how we make that possible — not why we do it. @@ -15,20 +15,54 @@ Tokens are how we make that possible — not why we do it. This repository hosts the **v0.1 protocol test** of Token-Ignition: - static frontend (`index.html` + `assets/`) — live at the Token-Ignition submission site -- meta-rules, protocol spec, and submission schema (see `/spec`) +- meta-rules, protocol spec, and submission schema (in-page + this README) - public ledger of submission hashes — AI audit results appended by bot (to be wired) No backend is wired yet. Submissions in v0.1 are captured client-side and hashed into a local ledger for visual/UX testing; the real submission pipeline (GitHub issue / serverless endpoint → AI audit workflow → ledger append) will land in v0.2. --- +## The thesis + +Model capability doubles roughly every four months — METR's measured cadence has only accelerated since their original paper. The engineering around it — memory, tools, planning loops, self-correction, long-horizon coherence — moves at a human pace, and caps out several orders of magnitude earlier than the models themselves could support. + +This lab exists to close that gap. We are climbing a ladder of **token horizons per single coherent task**, from 10⁶ to 10¹². Each rung unlocked becomes a product. + +``` + 10¹² ──────────── ? the research frontier + 10¹¹ ─────────── autonomous long-horizon research + 10¹⁰ ────────── multi-month project execution + 10⁹ ───────── self-evolving codebases + 10⁸ ──────── gate.3 — ignition + 10⁷ ─────── gate.2 — verified + 10⁶ ────── gate.1 — admission +``` + +The first three rungs are the admission ramp. What happens above them is the lab. + +--- + +## Three axes of self-evolution + +A self-evolving scaffold must move along at least one of these axes between runs — without a human editing prompts, weights, or code in between. The third is rare. We weight it highest. + +| axis | what it means | +|------|---------------| +| **01 // behavior** | The scaffold changes *how* it acts across runs — its policies, decision rules, strategy. | +| **02 // knowledge** | The scaffold accumulates, distills, or restructures *what it knows* across runs. | +| **03 // scaffold** | The scaffold modifies *itself* across runs — its tools, control loop, evaluation criteria, its successor. | + +A system that only rewrites its prompt is not, by itself, a self-evolving scaffold. + +--- + ## The three gates | gate | budget | unlock condition | | --- | --- | --- | | `gate.1` — admission | 1M tokens | any well-formed submission is admitted | -| `gate.2` — verified | 10M tokens | AI auditor confirms reproducible self-evolution on gate.1 artifact | -| `gate.3` — research | 100M tokens | emergent behavior verified by consensus of ≥3 independent models; standing invitation to join the research group | +| `gate.2` — verified | 10M tokens | AI auditor confirms reproducible self-evolution on the gate.1 artifact, with a real delta against ablation | +| `gate.3` — research | 100M tokens | scaffold-level evolution — not merely behavioral drift — verified by consensus of ≥3 independent models; standing invitation to join the research group | Clearing `gate.3` is how you get in. Tokens are the side-effect that lets you keep going. @@ -36,21 +70,23 @@ Clearing `gate.3` is how you get in. Tokens are the side-effect that lets you ke ## Meta-rules -- **R1** — you define the task; it must require a system that evolves itself. +- **R1** — you define the task; it must be long-horizon, and must require a scaffold that evolves itself to push the achievable horizon further. - **R2** — you define the evaluation criterion; it must be reproducible and machine-verifiable. -- **R3** — you build the system; the system, not you, produces the final output. +- **R3** — you build the scaffold; the scaffold, not you, produces the final output. - **R4** — all submissions are AI-judged; human audit is random and post-hoc. - **R5** — identity is irrelevant; submissions are accepted under pseudonym. - **R6** — selection is gated; pass a gate, unlock more resources; pass the final gate, join the research. +- **R7** — ablation is required; you must submit a baseline run on the same model with the minimal scaffold. The delta is the evidence. A scaffold that cannot beat its own ablation is not evolving — it is merely present. --- ## What counts as a valid submission -1. **Self-evolution, not prompt-engineering.** The system must modify its own behavior across iterations without human edits to prompts, weights, or code between runs. +1. **Self-evolution, not prompt-engineering.** The scaffold must modify its own behavior, knowledge, or structure across iterations — without human edits between runs. 2. **Machine-verifiable output.** The evaluation criterion must be checkable by an AI auditor with no proprietary access — public endpoint, public artifact. 3. **Live, AI-readable endpoint.** You must provide a URL an AI can crawl. HTML is fine; JSON / OpenAPI / plain text are better. Logins, captchas, GUIs are not accepted. -4. **Reproducibility micro-run.** Attach at least one log of a full run: inputs, intermediate state, final artifact hash. Our auditor re-runs a randomly sampled slice. +4. **Ablation baseline.** A second endpoint or log: same model, minimal scaffold, same task. Your scaffold's contribution is the delta between this and your main endpoint. +5. **Reproducibility micro-run.** Attach at least one log of a full run: inputs, intermediate state, final artifact hash. Our auditor re-runs a randomly sampled slice. If the AI auditor cannot independently verify your artifact, the submission is rejected. We do not email you for clarifications. **The endpoint is the application.** @@ -70,10 +106,10 @@ The frontend is vanilla HTML/CSS/JS — no build step. Copy / translate strings ## Roadmap -- [x] v0.1 — static protocol test site, hybrid terminal + form UX, bilingual EN/ZH -- [ ] v0.2 — GitHub-repo-as-backend: submissions become issues/PRs, AI audit runs as GitHub Action, ledger appends as commit -- [ ] v0.3 — multi-model consensus judge for `gate.3` (3+ independent models), random human audit sampler -- [ ] v0.4 — public API spec for submission endpoint requirements (`/benchmark`, artifact hash conventions) +- [x] v0.1 — static protocol test site: thesis-layer (ladder, axes, manifesto), R7 ablation rule, bilingual EN/ZH, 7-field submission form with axis declaration + ablation baseline +- [ ] v0.2 — GitHub-repo-as-backend: submissions become issues/PRs, AI audit runs as GitHub Action (multi-model consensus for gate.3), ledger appends as commit, axis-tagged scoring +- [ ] v0.3 — multi-model consensus judge formalized, random human audit sampler, post-hoc reproducibility replays +- [ ] v0.4 — public API spec for submission endpoint requirements (`/benchmark`, `/baseline`, artifact hash conventions) --- diff --git a/assets/app.js b/assets/app.js index 1b18928..f6e176c 100644 --- a/assets/app.js +++ b/assets/app.js @@ -1,267 +1,332 @@ /* ============================================================ - TOKEN-IGNITION — front-end v2 - - scrollable academic layout - - hybrid terminal aesthetic + real form inputs - - bilingual EN / ZH - - procedural ASCII hero art - - background node topology + TOKEN-IGNITION — v0.1 · simplified ============================================================ */ (() => { "use strict"; + /* -------------------------------------------------------------- + storage shim + -------------------------------------------------------------- */ + + const SAFE_STORE = { + get(k) { try { return localStorage.getItem(k); } catch { return null; } }, + set(k, v) { try { localStorage.setItem(k, v); } catch {} }, + }; + /* -------------------------------------------------------------- i18n -------------------------------------------------------------- */ const I18N = { en: { - brand: "TOKEN-IGNITION // RESEARCH SELECTION", + brand: "TOKEN-IGNITION · SELECTION PROTOCOL", - heroSubtitle: "A selection gate for AI-native researchers", - heroHook: "The task is the interview. Pass it, and you join the research.", + heroSubtitle: "A selection gate for AI-native researchers · v0.1 · Apr 2026", + heroHook: + 'Model horizons double every four months. The scaffolds around them do not. That gap is the work.', heroTagline: - "You define the task. You build the system. The system must evolve itself. If it clears the AI-audited gate, you're invited into the research group. Tokens are how we make that possible — not why we do it.", - heroCTA: "↓ enter the gate", - scrollHint: "scroll to read how selection works", + "You define a long-horizon task. You build a scaffold that evolves itself. Clear the AI audit — you’re in the research group. The artifact is the application. Passing is the offer.", + heroCTA: "↓ submit your scaffold", + scrollHint: "scroll to read the protocol", manifestoLabel: "manifesto", - manifestoTitle: "HOW WE SELECT", + manifestoTitle: "Why this exists", manifestoBody: ` -

Hiring in frontier AI is broken. The legible names get the interviews, the compute, the role. The people who would actually have moved the field rarely pass the filters.

-

CVs measure legibility, not ability. Pitch decks measure articulation, not craft. Interviews measure composure under pressure. None of these measure whether you can build a system that evolves itself.

-

So we inverted the interview. Give us a task you defined, and a system you built. If the system can clear the AI-audited gate by evolving itself, you're in the research group.

-

No CVs. No pitch decks. No intro calls. The artifact is the application. Passing is the offer.

+

Hiring in frontier AI filters for legibility, not ability. CVs, pitches, interviews — none of them measure whether you can build a system that evolves itself.

+

Model capability doubles every four months. The scaffolds around it move at a human pace, and cap out several orders of magnitude below what the models could support. This lab exists to close that gap. So we inverted the interview. Your artifact is the application.

`, + gapVizHead: "CAPABILITY · HORIZON PER TASK · LOG SCALE · 2024–2030", + gapModelLabel: "model", + gapScaffoldLabel: "scaffold", + gapModelAnnot: "×2 / 4mo", + gapScaffoldAnnot: "≈ flat", + gapZoneLabel: "the gap", + gapZoneSub: "( the lab )", + gapVizNote: "widening every quarter. this is the work.", + + ladderLabel: "orders of magnitude", + ladderTitle: "The ladder", + ladderIntro: ` +

We measure progress by the token horizon of a single coherent task — how much compute a scaffold can meaningfully apply to one objective before it stalls. Today: 10⁶–10⁷. Target: 10¹². Each rung unlocked becomes a product.

+ `, + ladderVizHead: "TOKEN HORIZON · SINGLE COHERENT TASK", + ladderRungs: [ + { oom: "10⁶", name: "gate.1 — admission", kind: "gate", current: true }, + { oom: "10⁷", name: "gate.2 — verified", kind: "gate" }, + { oom: "10⁸", name: "gate.3 — ignition", kind: "gate" }, + { oom: "10⁹", name: "self-evolving codebases", kind: "lab" }, + { oom: "10¹⁰", name: "multi-month project execution", kind: "lab" }, + { oom: "10¹¹", name: "autonomous long-horizon research", kind: "lab" }, + { oom: "10¹²", name: "? research frontier", kind: "frontier" }, + ], + ladderIgnitionLine: "— IGNITION LINE —", + ladderZoneLab: "· THE LAB ·", + ladderZoneRamp: "· ADMISSION ·", + ladderYouAreHere: "◀ here", + + axesLabel: "what evolves", + axesTitle: "Three axes of self-evolution", + axesIntro: `

A self-evolving scaffold must move along at least one axis between runs — without human edits. The third is rare. We weight it highest.

`, + axesCards: [ + { tag: "01", name: "behavior", body: "Policies, decisions, strategy — changing across runs without a human editing the prompt." }, + { tag: "02", name: "knowledge", body: "Memory, representations, references — accumulating and restructuring across runs." }, + { tag: "03", name: "scaffold", body: "The scaffold modifies itself — its tools, loop, successor. The rare axis. The one we are looking for.", weighted: true, weight: "highest weight" }, + ], + protocolLabel: "protocol", - protocolTitle: "META-RULES", + protocolTitle: "Meta-rules", protocolItems: [ - ["R1", "You define the task. The task must require a system that evolves itself."], - ["R2", "You define the evaluation criterion. It must be reproducible and machine-verifiable."], - ["R3", "You build the system. The system — not you — produces the final output."], + ["R1", "You define the task. It must be long-horizon, and require a scaffold that evolves itself to extend the achievable horizon further."], + ["R2", "You define the evaluation criterion. It must be reproducible and machine-verifiable through a public, AI-readable endpoint."], + ["R3", "You build the scaffold. The scaffold — not you — produces the final output."], ["R4", "All submissions are AI-judged. Human audit is random and post-hoc."], ["R5", "Identity is irrelevant. Submissions are accepted under pseudonym."], ["R6", "Selection is gated. Pass a gate, unlock more resources. Pass the final gate, join the research."], + ["R7", "Ablation is required. Submit a baseline on the same model with the minimal scaffold. A scaffold that cannot beat its own ablation is not evolving — it is merely present.", true], ], - rulesLabel: "submission requirements", - rulesTitle: "WHAT COUNTS AS A VALID SUBMISSION", - rulesItems: [ - { - head: "01 // Self-evolution, not prompt-engineering.", - body: [ - "The system must modify its own behavior across iterations", - "without human edits to prompts, weights, or code between runs.", - ], - }, - { - head: "02 // Machine-verifiable output.", - body: [ - "The evaluation criterion must be checkable by an AI auditor", - "with no proprietary access — public endpoint, public artifact.", - ], - }, - { - head: "03 // Live, AI-readable endpoint.", - body: [ - "You must provide a URL an AI can crawl. HTML is fine.", - "JSON / OpenAPI / plain text are better. Logins, captchas, GUIs are not accepted.", - ], - }, - { - head: "04 // Reproducibility micro-run.", - body: [ - "Attach at least one log of a full run: inputs, intermediate state,", - "final artifact hash. Our auditor re-runs a randomly sampled slice.", - ], - }, - ], - rulesCalloutHead: "// AUDIT NOTE", - rulesCalloutBody: - "If the AI auditor cannot independently verify your artifact, the submission is rejected. We do not email you for clarifications. The endpoint is the application.", + r7Head: "R7 · the ablation proof", + r7ScaffoldLabel: "your scaffold", + r7BaselineLabel: "your ablation", + r7Note: "the delta is the evidence. a scaffold that cannot beat its own ablation is merely present.", + + auditHead: "// audit note", + auditBody: "If the AI auditor cannot independently verify your artifact, the submission is rejected. We do not email for clarifications. The endpoint is the application.", allocationLabel: "the three gates", - allocationTitle: "HOW SELECTION WORKS", - allocation1Note: - "Admission tier. Any well-formed submission receives a 1M token budget to run and prove its system.", - allocation2Note: - "Verified tier. Unlocked when the AI auditor confirms reproducible self-evolution on your gate.1 artifact.", - allocation3Note: - "Research tier. Unlocked when emergent behavior — absent in the initial system — is verified by consensus of ≥3 independent models. Gate.3 clearance is a standing invitation to join the research group.", + allocationTitle: "How selection works", + allocationIntro: ` +

Each gate awards more compute; each gate raises the bar. Admission is cheap. Ignition is earned. The final gate is a standing invitation — not a round of interviews.

+ `, + gpCmd: "gate.status --watch", + gpLabel1: "admission", + gpLabel2: "verified", + gpLabel3: "ignition", + gpState1: "● open", + gpState2: "○ sealed", + gpState3: "○ sealed", + gpCond1: "any well-formed submission", + gpCond2: "reproducible self-evolution · delta > 0 vs ablation", + gpCond3: "scaffold-level evolution · consensus of ≥ 3 models", + gpReward3: "standing invitation to the research group", submitLabel: "submission", - submitTitle: "APPLY BY SUBMITTING", - submitIntro: - "Five fields. No account. Your entry is hashed and appended to a public ledger. AI audit starts within 24h. Clear the final gate and we reach out to talk about joining the research.", + submitTitle: "Apply by submitting", + submitIntro: "Seven fields. No account. Your entry is hashed and appended to a public ledger. AI audit starts within 24h.", + field1Label: "task.definition", - field1Hint: - "What does the system have to do? One paragraph. Must be testable.", - field1Ph: - "e.g. Given a stream of unlabeled numerical sequences, the system must autonomously discover a compression scheme and reconstruct the originals within tolerance ε.", + field1Hint: "What long-horizon task must the scaffold do? One paragraph. Must be testable.", + field1Ph: "e.g. Given an open bug in a large Python codebase, the scaffold must autonomously reproduce the test, locate root cause, patch it, and converge to green CI — across runs of >10⁶ tokens.", + field2Label: "verification.criterion", - field2Hint: - "How will an AI auditor know you passed? Give the exact check.", - field2Ph: - "e.g. Auditor fetches /benchmark → receives JSON { inputs[], outputs[] }. Passes if L2 error < 0.01 on all held-out inputs and compression ratio > 8x.", - field3Label: "execution.plan", - field3Hint: - "How does the system evolve itself? What loop, what signal, what memory?", - field3Ph: - "Describe the self-modification loop — what the system observes, how it updates itself, how it decides to stop. Write as you would write it to a peer reviewer, not to a VC.", - field4Label: "live.endpoint", - field4Hint: - "A URL an AI can hit right now and get a machine-readable result.", - field4Ph: "https://…", - field4Artifact: - "attach run log (optional, ≤1MB, txt/md/json/log/yaml/jsonl/csv)", - field5Label: "contact.handle", - field5Hint: - "A pseudonym is fine. We only use it to notify you when a gate opens.", - field5Ph: "e.g. @handle on X, discord username, or email", - - consent: - "I confirm my submission is my own work, the endpoint is live, and I accept AI-judged, non-negotiable evaluation.", + field2Hint: "How will an AI auditor know you passed? Give the exact check.", + field2Ph: "e.g. Auditor fetches /benchmark → JSON { task_id, status, diff_sha, baseline_sha }. Passes if status == 'green' AND token_count ≥ 10⁶ AND scaffold_delta ≥ +40%.", + + field3Label: "evolution.axes", + field3Hint: "Which axis does your scaffold evolve along? Axis 03 (scaffold) weighs highest.", + field3OptBehavior: "behavior", + field3OptKnowledge: "knowledge", + field3OptScaffold: "scaffold", + field3OptWeight: "highest weight", + + field4Label: "execution.plan", + field4Hint: "How does the scaffold evolve itself? Loop, signal, memory, what modifies what?", + field4Ph: "Describe the self-modification loop — what the scaffold observes, how it updates itself, how it decides to stop. Write as you would to a peer reviewer, not a VC.", + + field5Label: "live.endpoint", + field5Hint: "A URL an AI can hit right now.", + field5Ph: "https://…", + field5Artifact: "attach run log (optional, ≤1MB)", + + field6Label: "ablation.baseline", + field6Hint: "Same model, minimal scaffold, same task. The delta is your scaffold’s contribution.", + field6Ph: "https://…/baseline", + field6Artifact: "attach baseline log (optional, ≤1MB)", + + field7Label: "contact.handle", + field7Hint: "Pseudonym is fine. Used only to notify you when a gate opens.", + field7Ph: "e.g. @handle on X, discord, or email", + + consent: "I confirm this is my own work, both endpoints are live, and I accept AI-judged, non-negotiable evaluation.", submitButton: "submit", - footLeft: "TOKEN-IGNITION // RESEARCH SELECTION // v0.1", - footRight: "RESEARCH HOST: JOULE RESEARCH", - ledgerLabel: "ledger", - ledgerTitle: "RECENT APPLICATIONS", - ledgerIntro: - "Every application is stored as a hash on a public repository. No personal data is published.", + ledgerTitle: "Recent applications", + ledgerIntro: "Every application is stored as a hash on a public repository. No personal data is published.", + + footLeft: "TOKEN-IGNITION · v0.1", + footRight: "JOULE RESEARCH", feedback: { missing: "[reject] missing required fields. check highlighted items.", - badUrl: - "[reject] live.endpoint must be a valid URL — our auditor has to crawl it.", - ok: (hash) => - `[accept] entry ${hash} queued.\nAI audit window: 24h.\nYou will not receive a confirmation email. Watch the ledger.`, + badUrl: "[reject] live.endpoint must be a valid URL — our auditor has to crawl it.", + badBaseline: "[reject] ablation.baseline must be a valid URL.", + noAxis: "[reject] evolution.axes must have at least one selected.", + ok: (hash) => `[accept] entry ${hash} queued.\nAI audit window: 24h.\nwatch the ledger.`, }, }, zh: { - brand: "TOKEN-IGNITION // 研究员筛选", + brand: "TOKEN-IGNITION · 筛选协议", - heroSubtitle: "面向 AI 原生研究员的筛选入口", - heroHook: "任务本身就是申请书。通过了,就进研究组。", + heroSubtitle: "面向 AI 原生研究员的筛选入口 · v0.1 · 2026 年 4 月", + heroHook: + "模型能力每四个月翻一倍。包在模型外面的脚手架不会。这中间的落差,就是我们要做的事。", heroTagline: - "你定义任务。你构建系统。系统必须自我进化。通过 AI 审计的门槛后,你会被邀请进入研究组。token 只是我们让这件事成为可能的方式,不是它的目的。", - heroCTA: "↓ 进入申请", - scrollHint: "向下滚动,了解筛选机制", + "你定义一个长跨度任务。你构建一个会自我进化的脚手架。通过 AI 审计——你就进入研究组。作品本身就是申请。通过,就是 offer。", + heroCTA: "↓ 提交你的脚手架", + scrollHint: "向下滚动,阅读协议", manifestoLabel: "宣言", - manifestoTitle: "我们怎么选人", + manifestoTitle: "为什么做这件事", manifestoBody: ` -

前沿 AI 领域的招聘机制是坏的。能被看见的名字拿到面试、拿到算力、拿到位置。真正可能推动这个行业的人,往往过不了筛子。

-

简历衡量的是可读性,不是能力。Pitch 衡量的是表达,不是手艺。面试衡量的是临场镇定。没有一项,真正衡量你能不能造出一个会自我进化的系统。

-

所以我们把面试反过来。你给我们一个你自己定义的任务,和一个你自己造的系统。如果这个系统能靠自我进化通过 AI 审计,你就进入研究组。

-

不看简历。不看 pitch。不做 intro call。作品本身就是申请。通过,就是 offer。

+

前沿 AI 的招聘筛选的是可读性,不是能力。简历、pitch、面试,都衡量不了你能不能造出一个会自我进化的系统。

+

模型能力每四个月翻一倍。包裹它的工程以人的速度前进,并常常在模型尚有余力时就已到顶了几个数量级。这个 lab 存在,就是为了弥合这个落差。所以我们把面试反过来——作品本身就是申请。

+ `, + + gapVizHead: "能力 · 单任务跨度 · 对数坐标 · 2024–2030", + gapModelLabel: "模型", + gapScaffoldLabel: "脚手架", + gapModelAnnot: "×2 / 4月", + gapScaffoldAnnot: "≈ 几乎不变", + gapZoneLabel: "落差", + gapZoneSub: "( 这就是 lab )", + gapVizNote: "每季度都在变大。这就是要做的事。", + + ladderLabel: "数量级", + ladderTitle: "阶梯", + ladderIntro: ` +

我们用一个指标衡量进展:单个连贯任务的 token 跨度——脚手架能把多少计算持续投入到同一个目标上,在它停下来之前。今天:10⁶–10⁷。目标:10¹²。每解锁一个数量级,就变成一个产品。

`, + ladderVizHead: "TOKEN 跨度 · 单个连贯任务", + ladderRungs: [ + { oom: "10⁶", name: "gate.1 — 入围", kind: "gate", current: true }, + { oom: "10⁷", name: "gate.2 — 验证", kind: "gate" }, + { oom: "10⁸", name: "gate.3 — 点火", kind: "gate" }, + { oom: "10⁹", name: "会自我进化的代码库", kind: "lab" }, + { oom: "10¹⁰", name: "多月级项目执行", kind: "lab" }, + { oom: "10¹¹", name: "自主长跨度科研", kind: "lab" }, + { oom: "10¹²", name: "? 研究前沿", kind: "frontier" }, + ], + ladderIgnitionLine: "— 点火线 —", + ladderZoneLab: "· 真正的 lab ·", + ladderZoneRamp: "· 入围 ·", + ladderYouAreHere: "◀ 此处", + + axesLabel: "何谓进化", + axesTitle: "自我进化的三条轴", + axesIntro: `

一个会自我进化的脚手架,必须在两次运行之间至少在一条轴上改变——期间没有人手编辑。第三条最稀有。我们给它最高权重。

`, + axesCards: [ + { tag: "01", name: "行为", body: "策略、决策、方法——跨运行发生变化,期间没有人改 prompt。" }, + { tag: "02", name: "知识", body: "记忆、表征、内部参照——跨运行累积、蒸馏、重构。" }, + { tag: "03", name: "脚手架", body: "脚手架修改自己——工具、循环、后继者。最稀有的一条,也正是我们要找的。", weighted: true, weight: "最高权重" }, + ], protocolLabel: "协议", protocolTitle: "元规则", protocolItems: [ - ["R1", "你定义任务。该任务必须要求一个会自我进化的系统来完成。"], - ["R2", "你定义评估标准。标准必须可复现、可被机器验证。"], - ["R3", "你构建系统。最终产出来自系统,不是来自你。"], + ["R1", "你定义任务。任务必须是长跨度的,并要求一个能自我进化的脚手架去继续推动可达跨度。"], + ["R2", "你定义评估标准。必须可复现、可被机器验证——通过一个公开、AI 可读的端点。"], + ["R3", "你构建脚手架。最终产出来自脚手架,不是你。"], ["R4", "所有提交由 AI 评判。人工审计是随机抽查,事后进行。"], ["R5", "身份无关。允许使用化名提交。"], ["R6", "筛选分档。通过一档,解锁更多资源;通过最终档,进入研究组。"], + ["R7", "必须提供消融对照。同模型、最小脚手架、同任务,再跑一遍基线。打不过自己消融基线的脚手架不在进化——它只是在场。", true], ], - rulesLabel: "提交要求", - rulesTitle: "怎样才算一个有效提交", - rulesItems: [ - { - head: "01 // 必须是自我进化,而不是 prompt 工程。", - body: [ - "系统在多轮迭代间必须自行修改自身行为,", - "而非由人手动改 prompt、改权重或改代码。", - ], - }, - { - head: "02 // 机器可验证的输出。", - body: [ - "评估标准必须允许 AI 审计在无专有接口的情况下独立核对——", - "公开端点,公开产物。", - ], - }, - { - head: "03 // 活的、AI 可读的端点。", - body: [ - "你必须提供一个 AI 能直接访问的 URL。HTML 也可以,", - "JSON / OpenAPI / 纯文本更好。登录、验证码、纯图形界面不接受。", - ], - }, - { - head: "04 // 可复现的最小运行记录。", - body: [ - "至少附一段完整运行日志:输入、中间状态、最终产物哈希。", - "我们的审计器会随机抽一小段重跑。", - ], - }, - ], - rulesCalloutHead: "// 审计说明", - rulesCalloutBody: - "如果 AI 审计无法独立验证你的产物,提交将被驳回。我们不会发邮件追问——端点本身就是申请书。", + r7Head: "R7 · 消融证据", + r7ScaffoldLabel: "你的脚手架", + r7BaselineLabel: "你的消融", + r7Note: "差值即证据。打不过自己消融基线的脚手架只是在场。", + + auditHead: "// 审计说明", + auditBody: "若 AI 审计无法独立验证你的产物,提交将被驳回。我们不会发邮件追问——端点本身就是申请书。", allocationLabel: "三道门", allocationTitle: "筛选机制", - allocation1Note: - "入围档。任何合规提交即获得 1M token 预算,用于让系统跑起来、自证能力。", - allocation2Note: - "验证档。当 AI 审计确认 gate.1 的产物具备可复现的自我进化,解锁。", - allocation3Note: - "研究档。当产物出现初始系统所不具备的涌现行为,并由 ≥3 个独立模型共识验证,解锁。通过 gate.3,就是进入研究组的长期邀请。", + allocationIntro: ` +

每道门给更多算力;每道门也把评判抬得更高。入围便宜,点火靠挣。最后那道门不是一轮面试,而是一份长期邀请。

+ `, + gpCmd: "gate.status --watch", + gpLabel1: "入围", + gpLabel2: "验证", + gpLabel3: "点火", + gpState1: "● 开放", + gpState2: "○ 锁定", + gpState3: "○ 锁定", + gpCond1: "任何合规的提交", + gpCond2: "可复现的自我进化 · 相对消融基线的差值 > 0", + gpCond3: "脚手架层级的进化 · ≥ 3 个独立模型共识", + gpReward3: "进入研究组的长期邀请", submitLabel: "申请", submitTitle: "提交即申请", - submitIntro: - "五个字段。不用注册账号。提交会被哈希后写入公开账本,AI 审计将在 24 小时内开始。通过最终门槛,我们会主动联系你,谈加入研究组的事。", + submitIntro: "七个字段。不用注册账号。提交会被哈希后写入公开账本,AI 审计将在 24 小时内开始。", + field1Label: "task.definition", - field1Hint: "系统要做什么?一段话。必须可测试。", - field1Ph: - "例:给定一串无标注的数值序列,系统需自主发现一种压缩方案,并在误差 ε 内重建原序列。", + field1Hint: "脚手架要完成怎样一个长跨度任务?一段话,必须可测试。", + field1Ph: "例:给定一个大型 Python 代码库里的开放缺陷,脚手架自主复现测试、定位根因、打补丁,并在 >10⁶ token 的阅读、编辑与验证后,让 CI 全绿。", + field2Label: "verification.criterion", - field2Hint: "AI 审计怎么判断你通过了?给出确切的校验方式。", - field2Ph: - "例:审计器访问 /benchmark → 返回 JSON { inputs[], outputs[] }。若在全部留出输入上 L2 误差 < 0.01 且压缩比 > 8x,即为通过。", - field3Label: "execution.plan", - field3Hint: "系统如何自我进化?哪条循环、哪个信号、哪块记忆?", - field3Ph: - "描述自我修改的循环——系统观察什么,如何更新自身,如何判断停止。写给同行,不是写给投资人。", - field4Label: "live.endpoint", - field4Hint: "一个现在就能被 AI 访问、拿到机器可读结果的 URL。", - field4Ph: "https://…", - field4Artifact: - "附运行日志(可选,≤1MB,支持 txt/md/json/log/yaml/jsonl/csv)", - field5Label: "contact.handle", - field5Hint: "化名也可以。仅用于在门槛解锁时通知你。", - field5Ph: "例:X 上的 @handle、Discord 用户名或邮箱", - - consent: - "我确认这是我本人的作品,端点当前可访问,并接受由 AI 评判、不可申诉的评估结果。", + field2Hint: "AI 审计如何判断你通过?给出确切的校验方式。", + field2Ph: "例:审计器访问 /benchmark → 返回 JSON { task_id, status, diff_sha, baseline_sha }。若 status == 'green' 且 token_count ≥ 10⁶ 且 scaffold_delta ≥ +40%,即通过。", + + field3Label: "evolution.axes", + field3Hint: "你的脚手架在哪条轴上进化?第 03 条(脚手架)权重最高。", + field3OptBehavior: "行为", + field3OptKnowledge: "知识", + field3OptScaffold: "脚手架", + field3OptWeight: "最高权重", + + field4Label: "execution.plan", + field4Hint: "脚手架如何自我进化?循环、信号、记忆、哪部分在改哪部分?", + field4Ph: "描述自我修改的循环——观察什么、如何更新、如何停止。写给同行,不是写给投资人。", + + field5Label: "live.endpoint", + field5Hint: "一个现在就能被 AI 访问的 URL。", + field5Ph: "https://…", + field5Artifact: "附运行日志(可选,≤1MB)", + + field6Label: "ablation.baseline", + field6Hint: "同模型、最小脚手架、同任务。它和主端点的差值,就是你脚手架的贡献。", + field6Ph: "https://…/baseline", + field6Artifact: "附基线日志(可选,≤1MB)", + + field7Label: "contact.handle", + field7Hint: "化名也可以。仅用于门槛解锁时通知你。", + field7Ph: "例:X 上的 @handle、Discord 用户名或邮箱", + + consent: "我确认这是我本人的作品,两个端点均可访问,并接受 AI 评判的不可申诉结果。", submitButton: "提交", - footLeft: "TOKEN-IGNITION // 研究员筛选 // v0.1", - footRight: "研究承载方:JOULE RESEARCH", - ledgerLabel: "账本", ledgerTitle: "近期申请", ledgerIntro: "每条申请以哈希形式写入公开仓库,不发布任何个人信息。", + footLeft: "TOKEN-IGNITION · v0.1", + footRight: "JOULE RESEARCH", + feedback: { missing: "[reject] 必填字段缺失。请检查标红项。", badUrl: "[reject] live.endpoint 必须是有效 URL——审计器需要抓取它。", - ok: (hash) => - `[accept] 条目 ${hash} 已入队。\nAI 审计窗口:24 小时。\n我们不会发确认邮件,请关注账本。`, + badBaseline: "[reject] ablation.baseline 必须是有效 URL。", + noAxis: "[reject] evolution.axes 至少要选一项。", + ok: (hash) => `[accept] 条目 ${hash} 已入队。\nAI 审计窗口:24 小时。\n请关注账本。`, }, }, }; - let LANG = localStorage.getItem("ti.lang") || "en"; + let LANG = SAFE_STORE.get("ti.lang") || "en"; + + function escapeHtml(s) { + return String(s) + .replace(/&/g, "&") + .replace(//g, ">") + .replace(/"/g, """); + } /* -------------------------------------------------------------- i18n renderer @@ -271,261 +336,450 @@ const t = I18N[LANG]; document.documentElement.lang = LANG === "zh" ? "zh-CN" : "en"; - // text content document.querySelectorAll("[data-i18n]").forEach((el) => { const key = el.getAttribute("data-i18n"); - if (t[key] !== undefined) { - const val = t[key]; - if (typeof val === "string") { - if (val.trim().startsWith("<")) { - el.innerHTML = val; - } else { - el.textContent = val; - } - } - } + if (t[key] === undefined) return; + const val = t[key]; + if (typeof val !== "string") return; + if (el.tagName === "PRE") el.textContent = val; + else if (val.includes("<")) el.innerHTML = val; + else el.textContent = val; }); - // placeholders document.querySelectorAll("[data-i18n-ph]").forEach((el) => { const key = el.getAttribute("data-i18n-ph"); if (t[key] !== undefined) el.setAttribute("placeholder", t[key]); }); - // protocol list - const pList = document.getElementById("protocol-list"); - if (pList) { - pList.innerHTML = t.protocolItems - .map( - ([k, v]) => - `
  • ${k}${escapeHtml(v)}
  • ` - ) - .join(""); - } - - // rules list - const rList = document.getElementById("rules-list"); - if (rList) { - rList.innerHTML = t.rulesItems - .map( - (r) => - `
  • -
    ${escapeHtml(r.head)}
    -
    - ${r.body.map((line) => `
    ${escapeHtml(line)}
    `).join("")} -
    -
  • ` - ) - .join(""); - } + renderProtocol(); + renderAxes(); + renderGapSVG(); + renderLadderSVG(); - // lang buttons aria-current document.querySelectorAll(".lang-btn").forEach((b) => { - b.setAttribute( - "aria-current", - b.getAttribute("data-lang") === LANG ? "true" : "false" - ); + b.setAttribute("aria-current", b.getAttribute("data-lang") === LANG ? "true" : "false"); }); } - function escapeHtml(s) { - return String(s) - .replace(/&/g, "&") - .replace(//g, ">") - .replace(/"/g, """); + function renderProtocol() { + const t = I18N[LANG]; + const el = document.getElementById("protocol-list"); + if (!el) return; + el.innerHTML = t.protocolItems + .map(([k, v, r7]) => + `${escapeHtml(k)}${escapeHtml(v)}` + ) + .join(""); + } + + function renderAxes() { + const t = I18N[LANG]; + const el = document.getElementById("axes-grid"); + if (!el) return; + el.innerHTML = t.axesCards + .map((a) => ` +
    +
    + ${escapeHtml(a.tag)} + ${a.weighted ? '' : ""} +
    +
    ${escapeHtml(a.name)}
    +
    ${escapeHtml(a.body)}
    + ${a.weight ? `
    ${escapeHtml(a.weight)}
    ` : ""} +
    + `) + .join(""); } /* -------------------------------------------------------------- - ascii hero art — procedural noise field - characters form a soft "emerging" pattern + Gap SVG -------------------------------------------------------------- */ - function renderAsciiArt() { - const el = document.getElementById("ascii-art"); - if (!el) return; + function renderGapSVG() { + const t = I18N[LANG]; + const wrap = document.getElementById("gap-svg-wrap"); + if (!wrap) return; + + const W = 600, H = 320; + const svg = ` + + + + + + + + + + + + + + + + + + + + + + + + + + + 10¹² + 10¹⁰ + 10⁸ + 10⁶ + + + + + 2024 + 2026 + 2028 + 2030 + + + + + + + + + + + + + + + + + + + + ${escapeHtml(t.gapModelLabel)} + + + ${escapeHtml(t.gapModelAnnot)} + + + ${escapeHtml(t.gapScaffoldLabel)} + + + ${escapeHtml(t.gapScaffoldAnnot)} + + + + + ${escapeHtml(t.gapZoneLabel)} + + + ${escapeHtml(t.gapZoneSub)} + + + + + + + + + +`; + wrap.innerHTML = svg; + } - const cols = window.innerWidth < 700 ? 56 : 88; - const rows = 22; - const chars = " ...::--==++**#@".split(""); - // We'll animate a slow t over time. - let t = 0; - - function cell(x, y, t) { - // Normalize to [-1, 1] centered - const nx = (x / cols) * 2 - 1; - const ny = (y / rows) * 2 - 1; - // Radial falloff (sphere-ish intensity) - const r = Math.sqrt(nx * nx + ny * ny * 2.2); - const radial = Math.max(0, 1 - r * 1.05); - // Pseudo-noise via multi-frequency sin - const n = - Math.sin(x * 0.22 + y * 0.17 + t * 0.6) * 0.5 + - Math.sin(x * 0.09 - y * 0.28 + t * 0.4) * 0.3 + - Math.sin((x + y) * 0.35 + t * 0.9) * 0.2; - const v = radial * 0.85 + n * 0.25; - const idx = Math.max(0, Math.min(chars.length - 1, Math.round(v * (chars.length - 1)))); - return chars[idx]; - } + /* -------------------------------------------------------------- + Ladder SVG + -------------------------------------------------------------- */ - function frame() { - let out = ""; - for (let y = 0; y < rows; y++) { - for (let x = 0; x < cols; x++) { - out += cell(x, y, t); - } - out += "\n"; - } - el.textContent = out; - } + function renderLadderSVG() { + const t = I18N[LANG]; + const wrap = document.getElementById("ladder-svg-wrap"); + if (!wrap) return; + + const W = 520, H = 430; + const rails = 78; + const rungs = t.ladderRungs; + const nRungs = rungs.length; + const topY = 42, bottomY = 380; + const step = (bottomY - topY) / (nRungs - 1); + + const defs = ` + + + + + + + + + + + + + + `; + + const ignitionY = topY + step * (nRungs - 1 - 2.5); + + let rungsSvg = ""; + rungs.forEach((r, i) => { + const y = bottomY - i * step; + const len = 44 + i * 8; + const pattern = + r.kind === "gate" ? "url(#ht-amber)" : + r.kind === "frontier" ? "url(#ht-vapor)" : + i >= 4 ? "url(#ht-sparse)" : "url(#ht-ink)"; + const oomColor = + r.kind === "gate" ? "#dca268" : + r.kind === "frontier" ? "#dca268" : + i >= 4 ? "#807e73" : "#b4b1a4"; + const nameColor = + r.kind === "gate" ? "#e9e7dc" : + r.kind === "frontier" ? "#dca268" : + i >= 4 ? "#807e73" : "#b4b1a4"; + + const starred = r.kind === "gate" && r.oom === "10⁸"; + const currentBadge = r.current + ? ` + + + ${escapeHtml(t.ladderYouAreHere)}` + : ""; + + rungsSvg += ` + + ${r.oom} + + ${escapeHtml(r.name)}${starred ? " ★" : ""} + ${currentBadge} + `; + }); - frame(); - // slow animate - setInterval(() => { - t += 0.06; - frame(); - }, 220); + const svg = ` + + ${defs} - // redraw on resize - let resizeTimer; - window.addEventListener("resize", () => { - clearTimeout(resizeTimer); - resizeTimer = setTimeout(() => { - location.reload(); // simplest: re-layout cols - }, 300); - }); + + + + + ${escapeHtml(t.ladderIgnitionLine)} + + + + ${escapeHtml(t.ladderZoneLab)} + ${escapeHtml(t.ladderZoneRamp)} + + + ${rungsSvg} + + + ( naked model ceiling ) + +`; + wrap.innerHTML = svg; } /* -------------------------------------------------------------- - background node topology (subtle) + Bayer-dither — subtle ground life -------------------------------------------------------------- */ - function startTopology() { - const canvas = document.getElementById("topology"); + function initDither() { + const canvas = document.getElementById("dither"); if (!canvas) return; + const reduced = matchMedia("(prefers-reduced-motion: reduce)").matches; + if (reduced) return; const ctx = canvas.getContext("2d"); - let w = 0, - h = 0, - dpr = 1; + if (!ctx) return; - const nodes = []; - const N = 44; + const bayer = [ + [ 0, 8, 2,10], + [12, 4,14, 6], + [ 3,11, 1, 9], + [15, 7,13, 5], + ]; + + const CELL = 3; + let t = 0, w = 0, h = 0; function resize() { - dpr = Math.min(window.devicePixelRatio || 1, 2); - w = canvas.clientWidth = window.innerWidth; - h = canvas.clientHeight = window.innerHeight; - canvas.width = w * dpr; - canvas.height = h * dpr; - ctx.setTransform(dpr, 0, 0, dpr, 0, 0); + w = window.innerWidth; + h = window.innerHeight; + canvas.width = w; + canvas.height = h; } - function seed() { - nodes.length = 0; - for (let i = 0; i < N; i++) { - nodes.push({ - x: Math.random() * w, - y: Math.random() * h, - vx: (Math.random() - 0.5) * 0.12, - vy: (Math.random() - 0.5) * 0.12, - }); + function draw() { + ctx.clearRect(0, 0, w, h); + const offset = Math.sin(t * 0.08) * 80; + for (let y = 0; y < h; y += CELL) { + for (let x = 0; x < w; x += CELL) { + const fx = (x + offset) * 0.0042; + const fy = y * 0.0048 - t * 0.002; + const v = + Math.sin(fx + t * 0.12) * 0.5 + + Math.sin(fy * 1.4 + t * 0.08) * 0.35 + + Math.sin((x + y) * 0.003 - t * 0.06) * 0.2; + const n = (v + 1) / 2; + const thr = (bayer[(y / CELL) & 3][(x / CELL) & 3] + 1) / 17; + if (n > thr + 0.48) { + ctx.fillStyle = Math.random() < 0.02 ? "rgba(220,162,104,0.25)" : "rgba(233,231,220,0.09)"; + ctx.fillRect(x, y, CELL - 1, CELL - 1); + } + } } } - function frame() { - ctx.clearRect(0, 0, w, h); + resize(); + draw(); + let resizeTimer; + window.addEventListener("resize", () => { + clearTimeout(resizeTimer); + resizeTimer = setTimeout(() => { resize(); draw(); }, 200); + }); + setInterval(() => { t += 1; draw(); }, 520); + } - for (const n of nodes) { - n.x += n.vx; - n.y += n.vy; - if (n.x < 0 || n.x > w) n.vx *= -1; - if (n.y < 0 || n.y > h) n.vy *= -1; - } + /* -------------------------------------------------------------- + Scroll reveal (simple fade+rise, no blur) + -------------------------------------------------------------- */ - const maxDist = 180; - ctx.lineWidth = 1; - for (let i = 0; i < nodes.length; i++) { - for (let j = i + 1; j < nodes.length; j++) { - const dx = nodes[i].x - nodes[j].x; - const dy = nodes[i].y - nodes[j].y; - const d = Math.sqrt(dx * dx + dy * dy); - if (d < maxDist) { - const alpha = (1 - d / maxDist) * 0.22; - ctx.strokeStyle = `rgba(255,255,255,${alpha})`; - ctx.beginPath(); - ctx.moveTo(nodes[i].x, nodes[i].y); - ctx.lineTo(nodes[j].x, nodes[j].y); - ctx.stroke(); + function initScrollReveal() { + const reduced = matchMedia("(prefers-reduced-motion: reduce)").matches; + const io = new IntersectionObserver( + (entries) => { + for (const e of entries) { + if (e.isIntersecting) { + e.target.classList.add("visible"); + if (e.target.id === "vault") e.target.classList.add("revealed"); + if (e.target.id === "r7-proof") e.target.classList.add("revealed"); + io.unobserve(e.target); } } - } + }, + { threshold: 0.15, rootMargin: "0px 0px -8% 0px" } + ); + + if (reduced) { + document.querySelectorAll(".reveal").forEach((el) => el.classList.add("visible")); + const v = document.getElementById("vault"); if (v) v.classList.add("revealed"); + const r7 = document.getElementById("r7-proof"); if (r7) r7.classList.add("revealed"); + return; + } + document.querySelectorAll(".reveal").forEach((el) => io.observe(el)); + } - ctx.fillStyle = "rgba(255,255,255,0.55)"; - for (const n of nodes) { - ctx.beginPath(); - ctx.arc(n.x, n.y, 1.2, 0, Math.PI * 2); - ctx.fill(); + function initScrollProgress() { + const el = document.getElementById("scroll-progress"); + if (!el) return; + let ticking = false; + function onScroll() { + if (!ticking) { + requestAnimationFrame(() => { + const h = document.documentElement.scrollHeight - window.innerHeight; + const p = h > 0 ? (window.scrollY / h) * 100 : 0; + el.style.width = p.toFixed(2) + "%"; + ticking = false; + }); + ticking = true; } - - requestAnimationFrame(frame); } - - window.addEventListener("resize", () => { - resize(); - }); - resize(); - seed(); - requestAnimationFrame(frame); + window.addEventListener("scroll", onScroll, { passive: true }); + onScroll(); } /* -------------------------------------------------------------- - mock ledger (local, for visual only until backend is wired) + Ledger -------------------------------------------------------------- */ - function renderLedger() { + function renderLedger(freshHash) { const el = document.getElementById("ledger-table"); if (!el) return; - const stored = JSON.parse(localStorage.getItem("ti.ledger") || "[]"); - // seed with some placeholders if empty, for visual context + const stored = JSON.parse(SAFE_STORE.get("ti.ledger") || "[]"); const seed = stored.length ? stored : [ - { hash: "0x9ac7…1fe3", time: "T-0h", status: "pending" }, - { hash: "0x4b21…a0cd", time: "T-2h", status: "advanced" }, + { hash: "0x9ac7…1fe3", time: "T-0h", status: "pending" }, + { hash: "0x4b21…a0cd", time: "T-2h", status: "advanced" }, { hash: "0xf3ab…77e1", time: "T-11h", status: "rejected" }, { hash: "0x0017…b4d2", time: "T-26h", status: "ignited" }, ]; el.innerHTML = seed - .map( - (r) => - `
    -
    ${escapeHtml(r.hash)}
    -
    ${escapeHtml(r.time)}
    -
    ${escapeHtml(r.status.toUpperCase())}
    -
    ` - ) + .map((r) => { + const fresh = freshHash && r.hash === freshHash ? " fresh" : ""; + return `
    +
    ${escapeHtml(r.hash)}
    +
    ${escapeHtml(r.time)}
    +
    ${escapeHtml(r.status.toUpperCase())}
    +
    `; + }) .join(""); } /* -------------------------------------------------------------- - submit form + Form -------------------------------------------------------------- */ function hashString(s) { - // quick, non-crypto hash just for a local visual receipt let h = 2166136261 >>> 0; for (let i = 0; i < s.length; i++) { h ^= s.charCodeAt(i); h = Math.imul(h, 16777619); } - return "0x" + (h >>> 0).toString(16).padStart(8, "0").slice(0, 4) + "…" + - (h >>> 0).toString(16).padStart(8, "0").slice(4, 8); + const hex = (h >>> 0).toString(16).padStart(8, "0"); + return "0x" + hex.slice(0, 4) + "…" + hex.slice(4, 8); + } + + function isValidHttpUrl(s) { + try { + const u = new URL(s); + return /^https?:$/.test(u.protocol); + } catch { return false; } + } + + function shakeField(id) { + const field = document.getElementById(id)?.closest(".field"); + if (!field) return; + field.classList.remove("invalid"); + void field.offsetWidth; + field.classList.add("invalid"); + setTimeout(() => field.classList.remove("invalid"), 500); + } + + function typewriteFeedback(el, text) { + el.textContent = ""; + let i = 0; + const iv = setInterval(() => { + el.textContent = text.slice(0, i); + i++; + if (i > text.length) clearInterval(iv); + }, 14); } function wireForm() { const form = document.getElementById("submit-form"); if (!form) return; const fb = document.getElementById("submit-feedback"); + const btn = form.querySelector(".submit-btn"); + + if (btn) { + btn.addEventListener("click", (e) => { + const r = document.createElement("span"); + r.className = "ripple"; + const rect = btn.getBoundingClientRect(); + const size = Math.max(rect.width, rect.height); + r.style.width = r.style.height = size + "px"; + r.style.left = (e.clientX - rect.left - size / 2) + "px"; + r.style.top = (e.clientY - rect.top - size / 2) + "px"; + btn.appendChild(r); + setTimeout(() => r.remove(), 700); + }); + } form.addEventListener("submit", (e) => { e.preventDefault(); @@ -535,56 +789,66 @@ const criterion = form.criterion.value.trim(); const plan = form.plan.value.trim(); const endpoint = form.endpoint.value.trim(); + const baseline = form.baseline.value.trim(); const contact = form.contact.value.trim(); const consent = document.getElementById("f-consent").checked; + const axes = Array.from(form.querySelectorAll('input[name="axis"]:checked')).map((el) => el.value); + + let failed = false; + if (!task) { shakeField("f-task"); failed = true; } + if (!criterion) { shakeField("f-criterion"); failed = true; } + if (!plan) { shakeField("f-plan"); failed = true; } + if (!endpoint) { shakeField("f-endpoint"); failed = true; } + if (!baseline) { shakeField("f-baseline"); failed = true; } + if (!contact) { shakeField("f-contact"); failed = true; } - if (!task || !criterion || !plan || !endpoint || !contact || !consent) { + if (failed || !consent) { fb.className = "submit-feedback visible error"; - fb.textContent = t.feedback.missing; + typewriteFeedback(fb, t.feedback.missing); return; } - - try { - const u = new URL(endpoint); - if (!/^https?:$/.test(u.protocol)) throw new Error("bad"); - } catch { + if (axes.length === 0) { fb.className = "submit-feedback visible error"; - fb.textContent = t.feedback.badUrl; + typewriteFeedback(fb, t.feedback.noAxis); + return; + } + if (!isValidHttpUrl(endpoint)) { + shakeField("f-endpoint"); + fb.className = "submit-feedback visible error"; + typewriteFeedback(fb, t.feedback.badUrl); + return; + } + if (!isValidHttpUrl(baseline)) { + shakeField("f-baseline"); + fb.className = "submit-feedback visible error"; + typewriteFeedback(fb, t.feedback.badBaseline); return; } - const payload = JSON.stringify({ - task, - criterion, - plan, - endpoint, - contact, - ts: Date.now(), - }); + const payload = JSON.stringify({ task, criterion, axes, plan, endpoint, baseline, contact, ts: Date.now() }); const h = hashString(payload); - // store locally so ledger visibly updates - const ledger = JSON.parse(localStorage.getItem("ti.ledger") || "[]"); + const ledger = JSON.parse(SAFE_STORE.get("ti.ledger") || "[]"); ledger.unshift({ hash: h, time: "T-0h", status: "pending" }); - localStorage.setItem("ti.ledger", JSON.stringify(ledger.slice(0, 8))); - renderLedger(); + SAFE_STORE.set("ti.ledger", JSON.stringify(ledger.slice(0, 8))); + renderLedger(h); - fb.className = "submit-feedback visible"; - fb.textContent = t.feedback.ok(h); + fb.className = "submit-feedback visible ok"; + typewriteFeedback(fb, t.feedback.ok(h)); form.reset(); }); } /* -------------------------------------------------------------- - lang toggle + language toggle -------------------------------------------------------------- */ function wireLang() { document.querySelectorAll(".lang-btn").forEach((b) => { b.addEventListener("click", () => { LANG = b.getAttribute("data-lang"); - localStorage.setItem("ti.lang", LANG); + SAFE_STORE.set("ti.lang", LANG); applyI18n(); }); }); @@ -597,8 +861,9 @@ document.addEventListener("DOMContentLoaded", () => { applyI18n(); wireLang(); - renderAsciiArt(); - startTopology(); + initDither(); + initScrollReveal(); + initScrollProgress(); renderLedger(); wireForm(); }); diff --git a/assets/styles.css b/assets/styles.css index a43d9d7..f62a445 100644 --- a/assets/styles.css +++ b/assets/styles.css @@ -1,111 +1,120 @@ +/* ============================================================ + TOKEN-IGNITION — v0.1 · simplified + two fonts · four sizes · two charts · dark + amber + ============================================================ */ + :root { - --bg: #0b0d11; - --bg-soft: #0f1116; - --fg: #e8ecef; - --fg-mid: #a6adb5; - --fg-dim: #6b7079; - --fg-faint: #383c44; - --border: #1b1e24; - --border-bright: #2a2e36; - --accent: #ffffff; - --danger: #c0c0c0; + --bg: #0a0c12; + --bg-soft: #0e1117; + --bg-sunk: #06080d; + + --ink: #e9e7dc; + --ink-light: #b4b1a4; + --ink-mid: #807e73; + --ink-dim: #4a4a44; + --ink-faint: #282828; + + --border: #1c1f25; + --border-bright: #2b2f37; + + --amber: #dca268; + --amber-dim: #8a5f33; + --amber-glow: rgba(220, 162, 104, 0.5); + --field-bg: #0f1218; --field-bg-focus: #141822; -} -* { - box-sizing: border-box; -} + --font-serif: "Newsreader", Georgia, "Times New Roman", serif; + --font-mono: "IBM Plex Mono", "JetBrains Mono", ui-monospace, Menlo, Consolas, monospace; -html { - scroll-behavior: smooth; + --col-read: 580px; + --col-mono: 760px; } -html, -body { - margin: 0; - padding: 0; +* { box-sizing: border-box; } +html { scroll-behavior: smooth; } + +html, body { + margin: 0; padding: 0; background: var(--bg); - color: var(--fg); - font-family: "JetBrains Mono", "Noto Sans Mono", ui-monospace, SFMono-Regular, - Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace; - font-weight: 400; - font-size: 15px; + color: var(--ink); + font-family: var(--font-serif); + font-size: 17px; line-height: 1.7; - letter-spacing: 0.01em; + font-feature-settings: "kern", "liga"; + -webkit-font-smoothing: antialiased; } body { min-height: 100vh; overflow-x: hidden; + position: relative; } -#topology { +::selection { background: var(--amber-glow); color: var(--bg); } + +/* ---------------------------------------------------------------- ground */ + +#dither { position: fixed; inset: 0; z-index: 0; pointer-events: none; - opacity: 0.35; + opacity: 0.38; + mix-blend-mode: screen; } -main { - position: relative; - z-index: 1; +.scroll-progress { + position: fixed; + top: 0; left: 0; + height: 1px; + width: 0%; + background: linear-gradient(90deg, transparent, var(--amber) 40%, var(--amber) 80%, transparent); + z-index: 100; + transition: width 0.08s linear; } +main { position: relative; z-index: 2; } + /* ---------------------------------------------------------------- topbar */ .topbar { position: fixed; - top: 0; - left: 0; - right: 0; - z-index: 10; + top: 0; left: 0; right: 0; + z-index: 20; display: flex; justify-content: space-between; align-items: center; - padding: 16px 28px; + padding: 20px 32px; + font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.22em; - color: var(--fg-dim); - background: linear-gradient( - 180deg, - rgba(11, 13, 17, 0.92) 0%, - rgba(11, 13, 17, 0.6) 70%, - rgba(11, 13, 17, 0) - ); + color: var(--ink-dim); + background: linear-gradient(180deg, rgba(10,12,18,0.9), rgba(10,12,18,0.5) 70%, rgba(10,12,18,0)); + text-transform: uppercase; pointer-events: none; } - -.topbar-left, -.topbar-right { - pointer-events: auto; -} +.topbar-left, .topbar-right { pointer-events: auto; } +.topbar-left .dot { color: var(--amber); margin-right: 8px; animation: blink 2.4s steps(1) infinite; } .lang-btn { - background: none; - border: none; - color: var(--fg-dim); - font: inherit; - letter-spacing: 0.22em; - padding: 4px 6px; - cursor: pointer; + background: none; border: none; + color: var(--ink-dim); + font: inherit; letter-spacing: 0.22em; + padding: 4px 6px; cursor: pointer; transition: color 0.2s ease; } - -.lang-btn:hover, -.lang-btn[aria-current="true"] { - color: var(--fg); -} - +.lang-btn:hover, .lang-btn[aria-current="true"] { color: var(--ink); } .lang-btn[aria-current="true"] { text-decoration: underline; text-underline-offset: 4px; + text-decoration-color: var(--amber); } +.sep { color: var(--ink-faint); padding: 0 2px; } -.sep { - color: var(--fg-faint); - padding: 0 2px; +@keyframes blink { + 0%, 100% { opacity: 1; } + 50% { opacity: 0; } } /* ---------------------------------------------------------------- hero */ @@ -116,298 +125,510 @@ main { flex-direction: column; align-items: center; justify-content: center; - padding: 96px 28px 48px; + padding: 120px 32px 80px; text-align: center; - position: relative; } -.ascii-art { - margin: 0 0 40px; - font-size: 10px; - line-height: 1.1; - color: var(--fg-mid); - letter-spacing: 0; - white-space: pre; - font-variant-ligatures: none; - user-select: none; - pointer-events: none; - opacity: 0.85; - text-shadow: 0 0 12px rgba(255, 255, 255, 0.04); -} - -@media (min-width: 900px) { - .ascii-art { - font-size: 11px; - } -} - -.hero-text .title { - margin: 0 0 14px; - font-size: 38px; +.hero-title { + margin: 0 0 24px; + font-family: var(--font-mono); + font-size: clamp(40px, 7vw, 78px); font-weight: 500; - letter-spacing: 0.28em; - color: var(--fg); + letter-spacing: 0.22em; + color: var(--ink); line-height: 1; } -.hero-text .subtitle { - margin: 0 0 24px; - font-size: 13px; - letter-spacing: 0.32em; - color: var(--fg-mid); +.hero-subtitle { + margin: 0 0 56px; + font-family: var(--font-mono); + font-size: 11px; + letter-spacing: 0.28em; + color: var(--ink-mid); text-transform: uppercase; } -.hero-text .hook { - margin: 0 auto 20px; - max-width: 720px; - font-size: 19px; - line-height: 1.55; - color: var(--fg); +.hero-hook { + margin: 0 auto 28px; + max-width: 680px; + font-family: var(--font-serif); + font-size: clamp(20px, 2vw, 26px); font-weight: 400; - letter-spacing: 0.01em; + font-style: italic; + line-height: 1.45; + color: var(--ink); } +.hero-hook em { color: var(--amber); font-style: italic; } -.hero-text .tagline { - margin: 0 auto 32px; - max-width: 620px; - font-size: 14px; - color: var(--fg-mid); - line-height: 1.75; -} - -@media (min-width: 900px) { - .hero-text .hook { - font-size: 22px; - } +.hero-tagline { + margin: 0 auto 48px; + max-width: 540px; + font-family: var(--font-serif); + font-size: 15px; + line-height: 1.65; + color: var(--ink-light); } .cta { display: inline-block; - padding: 12px 22px; + padding: 14px 26px; border: 1px solid var(--border-bright); - color: var(--fg); + color: var(--ink); text-decoration: none; - letter-spacing: 0.18em; - font-size: 12px; - transition: border-color 0.2s ease, background 0.2s ease, color 0.2s ease; - background: rgba(0, 0, 0, 0.4); + letter-spacing: 0.22em; + font-family: var(--font-mono); + font-size: 11px; + text-transform: lowercase; + background: rgba(0,0,0,0.4); + transition: border-color 0.25s, color 0.25s, box-shadow 0.35s; } - .cta:hover { - border-color: var(--fg); - color: var(--fg); - background: rgba(255, 255, 255, 0.04); + border-color: var(--amber); + color: var(--amber); + box-shadow: 0 0 28px -8px var(--amber-glow); } .scroll-hint { margin-top: 56px; + font-family: var(--font-mono); font-size: 11px; - letter-spacing: 0.3em; - color: var(--fg-faint); + letter-spacing: 0.28em; + color: var(--ink-faint); + text-transform: uppercase; + animation: hint 2.4s ease-in-out infinite; +} +@keyframes hint { + 0%, 100% { opacity: 0.35; transform: translateY(0); } + 50% { opacity: 0.8; transform: translateY(4px); } } /* ---------------------------------------------------------------- section */ .section { - max-width: 820px; + max-width: 1140px; margin: 0 auto; - padding: 120px 28px 80px; + padding: 140px 32px 80px; } +.section-slim { max-width: 760px; } .section-meta { display: flex; - gap: 16px; + gap: 14px; align-items: baseline; - color: var(--fg-dim); + color: var(--ink-dim); + font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.28em; - margin-bottom: 8px; -} - -.section-num { - color: var(--fg); -} - -.section-label::before { - content: "// "; - color: var(--fg-faint); + margin-bottom: 16px; + text-transform: uppercase; } +.section-num { color: var(--ink); } +.section-label::before { content: "// "; color: var(--ink-faint); } .section-title { - margin: 0 0 36px; - font-size: 22px; + margin: 0 0 40px; + font-family: var(--font-serif); font-weight: 500; - letter-spacing: 0.18em; - color: var(--fg); + font-size: clamp(26px, 2.6vw, 34px); + letter-spacing: -0.01em; + color: var(--ink); + line-height: 1.15; } -.prose p { - margin: 0 0 16px; - color: var(--fg); +.split { + display: grid; + grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); + gap: 56px; + align-items: center; } +.split-text { min-width: 0; } +.split-viz { min-width: 0; display: flex; justify-content: center; } +.split-viz > * { width: 100%; max-width: 540px; } -.prose p.dim { - color: var(--fg-mid); +@media (max-width: 900px) { + .split { grid-template-columns: 1fr; gap: 40px; align-items: stretch; } + .split-viz > * { max-width: none; } } +/* ---------------------------------------------------------------- prose */ + +.prose { + font-family: var(--font-serif); + font-size: 17px; + line-height: 1.7; + color: var(--ink); + max-width: var(--col-read); +} +.prose p { margin: 0 0 18px; } +.prose p:last-child { margin-bottom: 0; } .prose .highlight { - color: var(--fg); + color: var(--ink); + background: linear-gradient(180deg, transparent 62%, rgba(220,162,104,0.1) 62%, rgba(220,162,104,0.1) 92%, transparent 92%); + padding: 0 2px; } +.prose em, .prose i { color: var(--amber); font-style: italic; } +.prose strong { color: var(--ink); font-weight: 500; } -/* ---------------------------------------------------------------- kv list (protocol) */ +/* ---------------------------------------------------------------- viz — common */ -.kv-list { - list-style: none; - padding: 0; - margin: 0; - border-top: 1px solid var(--border); +.viz { + font-family: var(--font-mono); + color: var(--ink); + padding: 26px 24px; + border: 1px solid var(--border); + background: var(--bg-sunk); + position: relative; +} +.viz::before { + content: ""; + position: absolute; + left: 0; top: 20%; bottom: 20%; + width: 1px; + background: linear-gradient(180deg, transparent, var(--amber-dim) 40%, var(--amber) 60%, transparent); + opacity: 0.55; +} +.viz-head { + font-size: 10px; + letter-spacing: 0.28em; + color: var(--ink-dim); + text-transform: uppercase; + margin-bottom: 16px; + display: flex; + align-items: center; + gap: 10px; +} +.viz-head::after { + content: ""; + flex: 1; + height: 1px; + background: linear-gradient(90deg, transparent, var(--ink-faint), transparent); } -.kv-list li { - display: grid; - grid-template-columns: 60px 1fr; - gap: 20px; - padding: 14px 0; - border-bottom: 1px solid var(--border); +.viz-svg { + width: 100%; + height: auto; + display: block; } -.kv-list .k { - color: var(--fg-mid); - letter-spacing: 0.16em; +.viz-note { + margin-top: 16px; + padding-top: 12px; + border-top: 1px dashed var(--border); + font-family: var(--font-serif); + font-style: italic; + font-size: 13px; + color: var(--amber); + text-align: right; } -.kv-list .v { - color: var(--fg); +/* ---------------------------------------------------------------- axes cards */ + +.axes-grid { + display: grid; + grid-template-columns: repeat(3, 1fr); + gap: 16px; + margin: 32px 0 0; +} +.axis-card { + border: 1px solid var(--border); + background: var(--bg-sunk); + padding: 26px 22px 24px; + display: flex; + flex-direction: column; + gap: 14px; + position: relative; +} +.axis-card.weighted { + border-color: var(--amber-dim); +} +.axis-card.weighted::after { + content: ""; + position: absolute; + inset: 0; + pointer-events: none; + box-shadow: inset 0 0 50px -18px var(--amber-glow); + opacity: 0.35; + animation: axis-glow 4s ease-in-out infinite; +} +@keyframes axis-glow { + 0%, 100% { opacity: 0.2; } + 50% { opacity: 0.55; } } -/* ---------------------------------------------------------------- rules list */ +.axis-tag { + font-family: var(--font-mono); + font-size: 10px; + letter-spacing: 0.3em; + color: var(--ink-dim); + text-transform: uppercase; + display: flex; + justify-content: space-between; + align-items: baseline; +} +.axis-tag .star { color: var(--amber); letter-spacing: 0; font-size: 11px; } -.rule-list { - list-style: none; - padding: 0; - margin: 0; - counter-reset: rule; +.axis-name { + font-family: var(--font-serif); + font-size: 22px; + font-weight: 500; + color: var(--ink); + line-height: 1.1; } +.axis-card.weighted .axis-name { color: var(--amber); } -.rule-list li { - padding: 18px 0; - border-bottom: 1px solid var(--border); +.axis-body { + font-family: var(--font-serif); + font-size: 15px; + line-height: 1.55; + color: var(--ink-light); + flex: 1; } -.rule-list li:first-child { +.axis-weight { + padding-top: 14px; border-top: 1px solid var(--border); + font-family: var(--font-mono); + font-size: 10px; + letter-spacing: 0.28em; + color: var(--amber); + text-transform: uppercase; } -.rule-list .rule-head { - color: var(--fg); - margin-bottom: 6px; +@media (max-width: 820px) { + .axes-grid { grid-template-columns: 1fr; } } -.rule-list .rule-body { - color: var(--fg-mid); - padding-left: 16px; -} +/* ---------------------------------------------------------------- protocol (R1-R7) */ -.rule-list .rule-body div { - padding: 2px 0; +.kv-list { + list-style: none; + padding: 0; + margin: 0; + border-top: 1px solid var(--border); + font-family: var(--font-mono); + max-width: var(--col-mono); +} +.kv-list li { + display: grid; + grid-template-columns: 56px 1fr; + gap: 18px; + padding: 18px 0; + border-bottom: 1px solid var(--border); +} +.kv-list li.r7 { + border-left: 2px solid var(--amber-dim); + padding-left: 14px; + margin-left: -16px; + background: linear-gradient(90deg, rgba(220,162,104,0.03), transparent 50%); +} +.kv-list .k { + color: var(--amber-dim); + letter-spacing: 0.16em; + font-weight: 500; + font-size: 13px; +} +.kv-list li.r7 .k { color: var(--amber); } +.kv-list .v { + color: var(--ink); + font-size: 13px; + line-height: 1.6; } .callout { margin-top: 28px; - padding: 18px 20px; + padding: 18px 22px; border: 1px solid var(--border-bright); - background: rgba(255, 255, 255, 0.015); + background: rgba(255,255,255,0.01); + font-family: var(--font-mono); + max-width: var(--col-mono); + font-size: 12px; + color: var(--ink-light); + line-height: 1.6; } - .callout-head { - color: var(--fg); - margin-bottom: 6px; - letter-spacing: 0.14em; - font-size: 12px; + color: var(--amber); + font-size: 10px; + letter-spacing: 0.28em; + text-transform: uppercase; + margin-bottom: 8px; } -.callout-body { - color: var(--fg-mid); +/* R7 ablation proof */ +.r7-proof { + margin-top: 28px; + padding: 22px 24px; + border: 1px solid var(--border); + background: var(--bg-sunk); + font-family: var(--font-mono); + max-width: var(--col-mono); +} +.r7-proof-head { + color: var(--amber); + font-size: 10px; + letter-spacing: 0.28em; + text-transform: uppercase; + margin-bottom: 16px; +} +.r7-row { + display: grid; + grid-template-columns: 120px 1fr 50px; + gap: 12px; + align-items: center; + padding: 6px 0; + font-size: 12px; +} +.r7-row .lbl { color: var(--ink-light); letter-spacing: 0.06em; } +.r7-row .val { color: var(--ink); text-align: right; } +.r7-bar { + height: 10px; + background: var(--bg); + border: 1px solid var(--border); + position: relative; + overflow: hidden; +} +.r7-bar .fill { + position: absolute; + inset: 0; + width: 0%; + background-image: radial-gradient(circle at 1px 1px, currentColor 0.7px, transparent 1.2px); + background-size: 4px 4px; + color: var(--ink-mid); + transition: width 1.2s cubic-bezier(0.2,0.6,0.2,1); +} +.r7-proof.revealed .r7-bar.scaffold .fill { width: 82%; color: var(--amber); } +.r7-proof.revealed .r7-bar.ablation .fill { width: 14%; color: var(--ink-mid); } +.r7-note { + margin-top: 16px; + padding-top: 12px; + border-top: 1px dashed var(--border); + font-family: var(--font-serif); + font-style: italic; font-size: 13px; + color: var(--amber); + line-height: 1.5; } -/* ---------------------------------------------------------------- allocation */ +/* ---------------------------------------------------------------- vault (gates) */ -.allocation-grid { +.vault { + border: 1px solid var(--border); + background: var(--bg-sunk); + font-family: var(--font-mono); + position: relative; + overflow: hidden; +} +.vault-top { + padding: 12px 20px; + border-bottom: 1px solid var(--border); + font-size: 10px; + letter-spacing: 0.26em; + color: var(--ink-dim); + text-transform: uppercase; +} +.vault-top .prompt { color: var(--amber); } +.chamber { + padding: 20px 22px 18px; + border-bottom: 1px solid var(--border); display: grid; - grid-template-columns: repeat(3, 1fr); - gap: 14px; + gap: 6px; } - -.allocation-cell { - border: 1px solid var(--border); - padding: 22px 20px; - background: rgba(255, 255, 255, 0.01); +.chamber:last-child { border-bottom: none; } +.chamber.gate-3 { opacity: 0.72; } +.chamber.gate-2 { opacity: 0.9; } +.chamber.gate-1 { + background: linear-gradient(180deg, rgba(220,162,104,0.04), transparent); } -.allocation-key { - color: var(--fg-mid); - font-size: 11px; - letter-spacing: 0.22em; - margin-bottom: 14px; +.chamber-head { + display: grid; + grid-template-columns: auto 1fr auto auto; + gap: 12px; + align-items: baseline; } - -.allocation-value { - color: var(--fg); - font-size: 36px; - font-weight: 500; - letter-spacing: 0.05em; - margin-bottom: 14px; +.chamber-id { + font-size: 12px; + letter-spacing: 0.16em; + color: var(--ink-light); } - -.allocation-note { - color: var(--fg-mid); +.chamber.gate-1 .chamber-id { color: var(--amber); } +.chamber-name { + font-family: var(--font-serif); + font-size: 17px; + color: var(--ink); + letter-spacing: -0.005em; +} +.chamber.gate-1 .chamber-name { color: var(--amber); } +.chamber-budget { font-size: 12px; - line-height: 1.6; + color: var(--ink-mid); + letter-spacing: 0.06em; } +.chamber-state { + font-size: 10px; + letter-spacing: 0.22em; + color: var(--ink-dim); + text-transform: uppercase; +} +.chamber.gate-1 .chamber-state { color: var(--amber); animation: blink 2.4s ease-in-out infinite; } +.chamber-cond { + font-size: 12px; + color: var(--ink-light); + line-height: 1.5; + padding-left: 18px; + position: relative; +} +.chamber-cond::before { + content: "—"; + position: absolute; + left: 0; + color: var(--amber-dim); +} +.chamber-cond.reward { + color: var(--amber); + margin-top: 4px; +} +.chamber-cond.reward::before { content: "→"; color: var(--amber); } -@media (max-width: 640px) { - .allocation-grid { - grid-template-columns: 1fr; - } +.vault .chamber { + opacity: 0; + transform: translateX(-4px); + transition: opacity 0.5s ease, transform 0.5s ease; } +.vault.revealed .chamber.gate-3 { opacity: 0.72; transform: translateX(0); transition-delay: 0.08s; } +.vault.revealed .chamber.gate-2 { opacity: 0.9; transform: translateX(0); transition-delay: 0.20s; } +.vault.revealed .chamber.gate-1 { opacity: 1; transform: translateX(0); transition-delay: 0.32s; } /* ---------------------------------------------------------------- submit form */ -.submit-section { - max-width: 720px; -} - +.submit-section { max-width: 680px; } .submit-intro { - color: var(--fg-mid); + color: var(--ink-light); + font-family: var(--font-serif); margin: 0 0 36px; - font-size: 14px; + font-size: 15px; + line-height: 1.65; } -.submit-form .field { - margin-bottom: 32px; -} +.submit-form .field { margin-bottom: 28px; } .field-label { display: flex; gap: 14px; align-items: baseline; - color: var(--fg); + color: var(--ink); + font-family: var(--font-mono); + font-size: 13px; margin-bottom: 6px; } - -.field-index { - color: var(--fg-dim); - font-size: 12px; - letter-spacing: 0.14em; -} - +.field-index { color: var(--amber-dim); font-size: 11px; letter-spacing: 0.14em; } .field-hint { - color: var(--fg-dim); - font-size: 12px; + color: var(--ink-mid); + font-family: var(--font-serif); + font-style: italic; + font-size: 13px; margin-bottom: 10px; - padding-left: 40px; + padding-left: 42px; + line-height: 1.55; } .submit-form textarea, @@ -415,263 +636,249 @@ main { .submit-form input[type="url"] { width: 100%; background: var(--field-bg); - color: var(--fg); + color: var(--ink); + font-family: var(--font-mono); border: 1px solid var(--border); - padding: 14px 16px; - font: inherit; + padding: 12px 14px; + font-size: 13px; outline: none; resize: vertical; - transition: border-color 0.15s ease, background 0.15s ease; + transition: border-color 0.2s, background 0.2s, box-shadow 0.25s; } - .submit-form textarea:focus, .submit-form input:focus { - border-color: var(--border-bright); + border-color: var(--amber-dim); background: var(--field-bg-focus); + box-shadow: 0 0 0 1px var(--amber-dim); } -.submit-form textarea::placeholder, -.submit-form input::placeholder { - color: var(--fg-faint); +.submit-form .field.invalid textarea, +.submit-form .field.invalid input[type="text"], +.submit-form .field.invalid input[type="url"] { + border-color: #8b3a3a; + animation: shake 0.36s ease-in-out 1; +} +@keyframes shake { + 0%, 100% { transform: translateX(0); } + 20% { transform: translateX(-5px); } + 40% { transform: translateX(5px); } + 60% { transform: translateX(-3px); } + 80% { transform: translateX(3px); } } -.field-sub { - margin-top: 12px; - display: flex; - flex-direction: column; +.field-checkbox-group { + display: grid; + grid-template-columns: repeat(3, 1fr); gap: 8px; } - -.field-sub-label { - color: var(--fg-dim); - font-size: 12px; - padding-left: 0; +.axis-check { + display: flex; + align-items: center; + gap: 10px; + padding: 14px; + border: 1px solid var(--border); + background: var(--field-bg); + cursor: pointer; + transition: border-color 0.2s, background 0.2s; + position: relative; + font-family: var(--font-mono); +} +.axis-check:hover { border-color: var(--border-bright); background: var(--field-bg-focus); } +.axis-check input[type="checkbox"] { accent-color: var(--amber); margin: 0; } +.axis-check-tag { color: var(--ink-dim); font-size: 10px; letter-spacing: 0.2em; } +.axis-check-name { color: var(--ink); font-size: 13px; letter-spacing: 0.06em; } +.axis-check-weighted { border-color: var(--amber-dim); } +.axis-check-weighted .axis-check-name { color: var(--amber); } +.axis-check-weight { + position: absolute; + top: 6px; right: 10px; + color: var(--amber); + font-size: 9px; + letter-spacing: 0.18em; + text-transform: uppercase; +} +@media (max-width: 640px) { + .field-checkbox-group { grid-template-columns: 1fr; } + .axis-check-weight { position: static; margin-left: auto; } } +.field-sub { margin-top: 10px; display: flex; flex-direction: column; gap: 6px; } +.field-sub-label { color: var(--ink-dim); font-size: 11px; font-family: var(--font-mono); } .submit-form input[type="file"] { - color: var(--fg-mid); - font: inherit; + color: var(--ink-mid); + font-family: var(--font-mono); background: transparent; border: 1px dashed var(--border-bright); - padding: 12px 16px; + padding: 10px 14px; cursor: pointer; width: 100%; + font-size: 11px; } - .submit-form input[type="file"]::file-selector-button { background: transparent; - color: var(--fg); + color: var(--ink); border: 1px solid var(--border-bright); font: inherit; padding: 4px 10px; - margin-right: 12px; + margin-right: 10px; letter-spacing: 0.1em; cursor: pointer; } -.consent { - font-size: 12px; - color: var(--fg-mid); -} - -.consent label { - display: flex; - gap: 10px; - align-items: flex-start; - cursor: pointer; -} - -.consent input[type="checkbox"] { - margin-top: 3px; - accent-color: var(--fg); -} - -.submit-row { - margin-top: 28px; -} +.consent { font-size: 12px; color: var(--ink-mid); font-family: var(--font-serif); font-style: italic; } +.consent label { display: flex; gap: 10px; align-items: flex-start; cursor: pointer; line-height: 1.55; } +.consent input[type="checkbox"] { margin-top: 4px; accent-color: var(--amber); } +.submit-row { margin-top: 28px; } .submit-btn { background: transparent; - color: var(--fg); + color: var(--ink); border: 1px solid var(--border-bright); - padding: 14px 22px; - font: inherit; - letter-spacing: 0.18em; + padding: 14px 26px; + font-family: var(--font-mono); + letter-spacing: 0.2em; font-size: 13px; cursor: pointer; display: inline-flex; align-items: center; gap: 12px; - transition: border-color 0.15s ease, background 0.15s ease; + transition: border-color 0.2s, color 0.2s, background 0.25s, box-shadow 0.3s; + position: relative; + overflow: hidden; + text-transform: lowercase; } - .submit-btn:hover { - border-color: var(--fg); - background: rgba(255, 255, 255, 0.04); -} - -.submit-btn:disabled { - cursor: not-allowed; - opacity: 0.4; -} - -.submit-sigil { - color: var(--fg-mid); -} - -.cursor-static { - color: var(--fg); - animation: blink 1.1s steps(1, end) infinite; + border-color: var(--amber); + color: var(--amber); + box-shadow: 0 0 28px -10px var(--amber-glow); +} +.submit-btn .ripple { + position: absolute; + border-radius: 50%; + background: var(--amber-glow); + transform: scale(0); + animation: ripple 0.62s ease-out; + pointer-events: none; } - -@keyframes blink { - 50% { - opacity: 0; - } +@keyframes ripple { + to { transform: scale(4); opacity: 0; } } +.submit-sigil { color: var(--amber-dim); } +.submit-btn .cursor-static { color: var(--amber); animation: blink 1.05s steps(1) infinite; } .submit-feedback { margin-top: 24px; - padding: 16px 18px; + padding: 16px 20px; border: 1px solid var(--border-bright); - background: rgba(255, 255, 255, 0.015); - color: var(--fg); + background: rgba(255,255,255,0.01); + color: var(--ink); + font-family: var(--font-mono); + font-size: 13px; white-space: pre-wrap; line-height: 1.7; display: none; } - -.submit-feedback.visible { - display: block; -} - -.submit-feedback.error { - border-color: #4d2525; - color: var(--fg); -} +.submit-feedback.visible { display: block; } +.submit-feedback.error { border-color: #4d2525; } +.submit-feedback.ok { border-color: var(--amber-dim); box-shadow: 0 0 40px -14px var(--amber-glow); } /* ---------------------------------------------------------------- ledger */ .ledger-intro { - color: var(--fg-mid); + color: var(--ink-mid); + font-family: var(--font-serif); + font-style: italic; margin: 0 0 20px; + font-size: 14px; } - .ledger-table { border-top: 1px solid var(--border); - color: var(--fg-mid); + font-family: var(--font-mono); + max-width: var(--col-mono); } - .ledger-row { display: grid; - grid-template-columns: 1fr 160px 120px; - gap: 16px; + grid-template-columns: 1fr 140px 110px; + gap: 14px; padding: 10px 0; border-bottom: 1px solid var(--border); - font-size: 13px; + font-size: 12px; letter-spacing: 0.04em; + transition: background 0.3s; } - -.ledger-row .hash { - color: var(--fg); - font-variant-numeric: tabular-nums; -} - -.ledger-row .time { - color: var(--fg-dim); -} - -.ledger-row .status { - text-align: right; - letter-spacing: 0.2em; - font-size: 11px; -} - -.ledger-row .status.pending { - color: var(--fg-mid); -} - -.ledger-row .status.advanced { - color: var(--fg); +.ledger-row.fresh { + background: rgba(220,162,104,0.08); + animation: flash 1.2s ease-out; } - -.ledger-row .status.ignited { - color: var(--fg); - text-decoration: underline; - text-underline-offset: 3px; -} - -.ledger-row .status.rejected { - color: var(--fg-faint); +@keyframes flash { + 0% { background: rgba(220,162,104,0.3); } + 100% { background: rgba(220,162,104,0); } } +.ledger-row .hash { color: var(--ink); font-variant-numeric: tabular-nums; } +.ledger-row .time { color: var(--ink-dim); } +.ledger-row .status { text-align: right; letter-spacing: 0.2em; font-size: 10px; } +.ledger-row .status.pending { color: var(--ink-mid); } +.ledger-row .status.advanced { color: var(--ink); } +.ledger-row .status.ignited { color: var(--amber); text-decoration: underline; text-underline-offset: 3px; } +.ledger-row .status.rejected { color: var(--ink-faint); } /* ---------------------------------------------------------------- footer */ .footer { - padding: 40px 28px 32px; - color: var(--fg-faint); + padding: 48px 32px 36px; + color: var(--ink-faint); + font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.22em; border-top: 1px solid var(--border); margin-top: 80px; + position: relative; + z-index: 2; + text-transform: uppercase; } - .footer-row { - max-width: 820px; + max-width: 1140px; margin: 0 auto; display: flex; justify-content: space-between; flex-wrap: wrap; gap: 16px; } +.footer-row .pulse { color: var(--amber); animation: blink 1.6s steps(1) infinite; } -/* ---------------------------------------------------------------- responsive */ - -@media (max-width: 640px) { - .topbar { - padding: 12px 18px; - font-size: 10px; - } - - .hero { - padding: 96px 20px 40px; - } +/* ---------------------------------------------------------------- reveal */ - .hero-text .title { - font-size: 28px; - letter-spacing: 0.2em; - } - - .section { - padding: 80px 20px 60px; - } - - .field-hint { - padding-left: 0; - } - - .kv-list li { - grid-template-columns: 48px 1fr; - } - - .ledger-row { - grid-template-columns: 1fr 100px; - grid-template-areas: - "hash status" - "time status"; - } +.reveal { + opacity: 0; + transform: translateY(8px); + transition: opacity 0.7s cubic-bezier(0.2,0.55,0.2,1), transform 0.7s cubic-bezier(0.2,0.55,0.2,1); +} +.reveal.visible { opacity: 1; transform: translateY(0); } - .ledger-row .hash { - grid-area: hash; - } - .ledger-row .time { - grid-area: time; - } - .ledger-row .status { - grid-area: status; - } +/* ---------------------------------------------------------------- responsive */ - .ascii-art { - font-size: 7px; +@media (max-width: 640px) { + .topbar { padding: 14px 18px; font-size: 10px; } + .hero { padding: 100px 22px 48px; } + .section { padding: 100px 22px 64px; } + .hero-title { letter-spacing: 0.18em; } + .field-hint { padding-left: 0; } + .kv-list li { grid-template-columns: 48px 1fr; } + .r7-row { grid-template-columns: 90px 1fr 40px; } + .ledger-row { grid-template-columns: 1fr 90px; grid-template-areas: "hash status" "time status"; } + .ledger-row .hash { grid-area: hash; } + .ledger-row .time { grid-area: time; } + .ledger-row .status { grid-area: status; } + .chamber-head { grid-template-columns: auto 1fr auto; } + .chamber-head .chamber-budget { display: none; } +} + +@media (prefers-reduced-motion: reduce) { + *, *::before, *::after { + animation-duration: 0.01ms !important; + transition-duration: 0.01ms !important; + animation-iteration-count: 1 !important; } + #dither { display: none; } + .reveal { opacity: 1; transform: none; } } diff --git a/index.html b/index.html index 79b2f9b..4a4563f 100644 --- a/index.html +++ b/index.html @@ -3,219 +3,240 @@ - TOKEN-IGNITION + TOKEN-IGNITION · Selection Protocol v0.1 + - + +
    -
    +
    + + +
    - + / - +
    - - -
    -

    TOKEN-IGNITION

    -

    -

    -

    - -
    -
    +

    TOKEN-IGNITION

    +

    +

    +

    + +
    - +
    -
    - -
    -