-
-
-
-
-
Batch Processing
-
Generate thousands of tool-calling trajectories in parallel with automatic checkpointing. Configurable workers, batch sizes, and toolset distributions.
-
-
-
RL Training
-
Atropos integration for reinforcement learning on agent behaviors. 11 tool-call parsers for training any model architecture.
-
-
-
Trajectory Export
-
Export conversations in ShareGPT format for fine-tuning. Trajectory compression fits training data into token budgets.
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ Telegram, Discord, Slack, WhatsApp, and CLI from a single gateway
+ — start on one, pick up on another.
+
+
+
+
+
+
+ Persistent memory and auto-generated skills — it learns your
+ projects and never forgets how it solved a problem.
+
+
+
+
+
+
+ Natural language cron scheduling for reports, backups, and
+ briefings — running unattended through the gateway.
+
+
+
+
+
+
+ Isolated subagents with their own conversations, terminals, and
+ Python RPC scripts for zero-context-cost pipelines.
+
+
+
+
+
+
+ Five backends — local, Docker, SSH, Singularity, Modal — with
+ container hardening and namespace isolation.
+
+
+
+
+
+
+ Web search, browser automation, vision, image generation,
+ text-to-speech, and multi-model reasoning.
+
+
+
+
+
+
+
+
+
+
+
+
Tools
+
+ 40+ built-in — web search, terminal, file system, browser
+ automation, vision, image generation, text-to-speech, code
+ execution, subagent delegation, memory, task planning, cron
+ scheduling, multi-model reasoning, and more.
+
+
+
+
+
Platforms
+
+ Telegram, Discord, Slack, WhatsApp, Signal, Email, and CLI — all
+ from a single gateway. Connect to
+ Nous Portal, OpenRouter, or any OpenAI-compatible API.
+
+
+
+
+
Environments
+
+ Run locally, in Docker, over SSH, on Modal, Daytona, or
+ Singularity. Container hardening with read-only root, dropped
+ capabilities, and namespace isolation.
+
+
+
+
+
Skills
+
+ 40+ bundled skills covering MLOps, GitHub workflows, research,
+ and more. The agent creates new skills on the fly and shares
+ them via the open
+ agentskills.io
+ format. Install community skills from
+ ClawHub,
+ LobeHub, and GitHub.
+
+
+
+
+
Research
+
+ Batch trajectory generation with parallel workers and
+ checkpointing. Atropos integration for RL training. Export to
+ ShareGPT for fine-tuning with trajectory compression.
+
+
+
+
+
-
-
+
diff --git a/landingpage/script.js b/landingpage/script.js
index 8af7d95d..4cd097bd 100644
--- a/landingpage/script.js
+++ b/landingpage/script.js
@@ -4,339 +4,518 @@
// --- Platform install commands ---
const PLATFORMS = {
- linux: {
- command: 'curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash',
- prompt: '$',
- note: 'Works on Linux, macOS & WSL2 · No prerequisites · Installs everything automatically',
- stepNote: 'Installs uv, Python 3.11, clones the repo, sets up everything. No sudo needed.',
- },
+ linux: {
+ command:
+ "curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash",
+ prompt: "$",
+ note: "Works on Linux, macOS & WSL2 · No prerequisites · Installs everything automatically",
+ stepNote:
+ "Installs uv, Python 3.11, clones the repo, sets up everything. No sudo needed.",
+ },
};
function detectPlatform() {
- return 'linux';
+ return "linux";
}
function switchPlatform(platform) {
- const cfg = PLATFORMS[platform];
- if (!cfg) return;
+ const cfg = PLATFORMS[platform];
+ if (!cfg) return;
- // Update hero install widget
- const commandEl = document.getElementById('install-command');
- const promptEl = document.getElementById('install-prompt');
- const noteEl = document.getElementById('install-note');
+ // Update hero install widget
+ const commandEl = document.getElementById("install-command");
+ const promptEl = document.getElementById("install-prompt");
+ const noteEl = document.getElementById("install-note");
- if (commandEl) commandEl.textContent = cfg.command;
- if (promptEl) promptEl.textContent = cfg.prompt;
- if (noteEl) noteEl.textContent = cfg.note;
+ if (commandEl) commandEl.textContent = cfg.command;
+ if (promptEl) promptEl.textContent = cfg.prompt;
+ if (noteEl) noteEl.textContent = cfg.note;
- // Update active tab in hero
- document.querySelectorAll('.install-tab').forEach(tab => {
- tab.classList.toggle('active', tab.dataset.platform === platform);
- });
+ // Update active tab in hero
+ document.querySelectorAll(".install-tab").forEach((tab) => {
+ tab.classList.toggle("active", tab.dataset.platform === platform);
+ });
- // Sync the step section tabs too
- switchStepPlatform(platform);
+ // Sync the step section tabs too
+ switchStepPlatform(platform);
}
function switchStepPlatform(platform) {
- const cfg = PLATFORMS[platform];
- if (!cfg) return;
+ const cfg = PLATFORMS[platform];
+ if (!cfg) return;
- const commandEl = document.getElementById('step1-command');
- const copyBtn = document.getElementById('step1-copy');
- const noteEl = document.getElementById('step1-note');
+ const commandEl = document.getElementById("step1-command");
+ const copyBtn = document.getElementById("step1-copy");
+ const noteEl = document.getElementById("step1-note");
- if (commandEl) commandEl.textContent = cfg.command;
- if (copyBtn) copyBtn.setAttribute('data-text', cfg.command);
- if (noteEl) noteEl.textContent = cfg.stepNote;
+ if (commandEl) commandEl.textContent = cfg.command;
+ if (copyBtn) copyBtn.setAttribute("data-text", cfg.command);
+ if (noteEl) noteEl.textContent = cfg.stepNote;
- // Update active tab in step section
- document.querySelectorAll('.code-tab').forEach(tab => {
- tab.classList.toggle('active', tab.dataset.platform === platform);
+ // Update active tab in step section
+ document.querySelectorAll(".code-tab").forEach((tab) => {
+ tab.classList.toggle("active", tab.dataset.platform === platform);
+ });
+}
+
+function toggleMobileNav() {
+ document.getElementById("nav-mobile").classList.toggle("open");
+ document.getElementById("nav-hamburger").classList.toggle("open");
+}
+
+function toggleSpecs() {
+ const wrapper = document.getElementById("specs-wrapper");
+ const btn = document.getElementById("specs-toggle");
+ const label = btn.querySelector(".toggle-label");
+ const isOpen = wrapper.classList.contains("open");
+
+ if (isOpen) {
+ wrapper.style.maxHeight = wrapper.scrollHeight + "px";
+ requestAnimationFrame(() => {
+ wrapper.style.maxHeight = "0";
});
+ wrapper.classList.remove("open");
+ btn.classList.remove("open");
+ if (label) label.textContent = "More details";
+ } else {
+ wrapper.classList.add("open");
+ wrapper.style.maxHeight = wrapper.scrollHeight + "px";
+ btn.classList.add("open");
+ if (label) label.textContent = "Less";
+ wrapper.addEventListener(
+ "transitionend",
+ () => {
+ if (wrapper.classList.contains("open")) {
+ wrapper.style.maxHeight = "none";
+ }
+ },
+ { once: true }
+ );
+ }
}
// --- Copy to clipboard ---
function copyInstall() {
- const text = document.getElementById('install-command').textContent;
- navigator.clipboard.writeText(text).then(() => {
- const btn = document.querySelector('.install-widget-body .copy-btn');
- const original = btn.querySelector('.copy-text').textContent;
- btn.querySelector('.copy-text').textContent = 'Copied!';
- btn.style.color = 'var(--gold)';
- setTimeout(() => {
- btn.querySelector('.copy-text').textContent = original;
- btn.style.color = '';
- }, 2000);
- });
+ const text = document.getElementById("install-command").textContent;
+ navigator.clipboard.writeText(text).then(() => {
+ const btn = document.querySelector(".install-widget-body .copy-btn");
+ const original = btn.querySelector(".copy-text").textContent;
+ btn.querySelector(".copy-text").textContent = "Copied!";
+ btn.style.color = "var(--primary-light)";
+ setTimeout(() => {
+ btn.querySelector(".copy-text").textContent = original;
+ btn.style.color = "";
+ }, 2000);
+ });
}
function copyText(btn) {
- const text = btn.getAttribute('data-text');
- navigator.clipboard.writeText(text).then(() => {
- const original = btn.textContent;
- btn.textContent = 'Copied!';
- btn.style.color = 'var(--gold)';
- setTimeout(() => {
- btn.textContent = original;
- btn.style.color = '';
- }, 2000);
- });
+ const text = btn.getAttribute("data-text");
+ navigator.clipboard.writeText(text).then(() => {
+ const original = btn.textContent;
+ btn.textContent = "Copied!";
+ btn.style.color = "var(--primary-light)";
+ setTimeout(() => {
+ btn.textContent = original;
+ btn.style.color = "";
+ }, 2000);
+ });
}
// --- Scroll-triggered fade-in ---
function initScrollAnimations() {
- const elements = document.querySelectorAll(
- '.feature-card, .tool-pill, .platform-group, .skill-category, ' +
- '.install-step, .research-card, .footer-card, .section-header, ' +
- '.lead-text, .section-desc, .terminal-window'
- );
+ const elements = document.querySelectorAll(
+ ".feature-card, .install-step, " +
+ ".section-header, .terminal-window",
+ );
- elements.forEach(el => el.classList.add('fade-in'));
+ elements.forEach((el) => el.classList.add("fade-in"));
- const observer = new IntersectionObserver((entries) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- // Stagger children within grids
- const parent = entry.target.parentElement;
- if (parent) {
- const siblings = parent.querySelectorAll('.fade-in');
- let idx = Array.from(siblings).indexOf(entry.target);
- if (idx < 0) idx = 0;
- setTimeout(() => {
- entry.target.classList.add('visible');
- }, idx * 60);
- } else {
- entry.target.classList.add('visible');
- }
- observer.unobserve(entry.target);
- }
- });
- }, { threshold: 0.1, rootMargin: '0px 0px -40px 0px' });
+ const observer = new IntersectionObserver(
+ (entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) {
+ // Stagger children within grids
+ const parent = entry.target.parentElement;
+ if (parent) {
+ const siblings = parent.querySelectorAll(".fade-in");
+ let idx = Array.from(siblings).indexOf(entry.target);
+ if (idx < 0) idx = 0;
+ setTimeout(() => {
+ entry.target.classList.add("visible");
+ }, idx * 60);
+ } else {
+ entry.target.classList.add("visible");
+ }
+ observer.unobserve(entry.target);
+ }
+ });
+ },
+ { threshold: 0.1, rootMargin: "0px 0px -40px 0px" },
+ );
- elements.forEach(el => observer.observe(el));
+ elements.forEach((el) => observer.observe(el));
}
// --- Terminal Demo ---
+const CURSOR = '
█';
+
const demoSequence = [
- // Scene 1: Research task with delegation
- { type: 'prompt', text: '❯ ' },
- { type: 'type', text: 'Research the latest approaches to GRPO training and write a summary', delay: 30 },
- { type: 'pause', ms: 600 },
- { type: 'output', lines: [
- '',
- '
┊ 🔍 web_search "GRPO reinforcement learning 2026" 1.2s',
- ]},
- { type: 'pause', ms: 400 },
- { type: 'output', lines: [
- '
┊ 📄 web_extract arxiv.org/abs/2402.03300 3.1s',
- ]},
- { type: 'pause', ms: 400 },
- { type: 'output', lines: [
- '
┊ 🔍 web_search "GRPO vs PPO ablation results" 0.9s',
- ]},
- { type: 'pause', ms: 400 },
- { type: 'output', lines: [
- '
┊ 📄 web_extract huggingface.co/blog/grpo 2.8s',
- ]},
- { type: 'pause', ms: 400 },
- { type: 'output', lines: [
- '
┊ ✍️ write_file ~/research/grpo-summary.md 0.1s',
- ]},
- { type: 'pause', ms: 500 },
- { type: 'output', lines: [
- '',
- '
Done! I\'ve written a summary covering:',
- '',
- '
✓ GRPO\'s group-relative advantage (no critic model needed)',
- '
✓ Comparison with PPO/DPO on reasoning benchmarks',
- '
✓ Implementation notes for Axolotl and TRL',
- '',
- '
Saved to ~/research/grpo-summary.md',
- ]},
- { type: 'pause', ms: 2500 },
+ { type: "prompt", text: "❯ " },
+ {
+ type: "type",
+ text: "Research the latest approaches to GRPO training and write a summary",
+ delay: 30,
+ },
+ { type: "pause", ms: 600 },
+ {
+ type: "output",
+ lines: [
+ "",
+ '
web_search "GRPO reinforcement learning 2026" 1.2s',
+ ],
+ },
+ { type: "pause", ms: 400 },
+ {
+ type: "output",
+ lines: [
+ '
web_extract arxiv.org/abs/2402.03300 3.1s',
+ ],
+ },
+ { type: "pause", ms: 400 },
+ {
+ type: "output",
+ lines: [
+ '
web_search "GRPO vs PPO ablation results" 0.9s',
+ ],
+ },
+ { type: "pause", ms: 400 },
+ {
+ type: "output",
+ lines: [
+ '
web_extract huggingface.co/blog/grpo 2.8s',
+ ],
+ },
+ { type: "pause", ms: 400 },
+ {
+ type: "output",
+ lines: [
+ '
write_file ~/research/grpo-summary.md 0.1s',
+ ],
+ },
+ { type: "pause", ms: 500 },
+ {
+ type: "output",
+ lines: [
+ "",
+ '
Done! I\'ve written a summary covering:',
+ "",
+ '
✓ GRPO\'s group-relative advantage (no critic model needed)',
+ '
✓ Comparison with PPO/DPO on reasoning benchmarks',
+ '
✓ Implementation notes for Axolotl and TRL',
+ "",
+ '
Saved to ~/research/grpo-summary.md',
+ ],
+ },
+ { type: "pause", ms: 2500 },
- // Scene 2: Quick delegation
- { type: 'clear' },
- { type: 'prompt', text: '❯ ' },
- { type: 'type', text: 'Review the PR at NousResearch/hermes-agent#42 and fix any issues', delay: 30 },
- { type: 'pause', ms: 600 },
- { type: 'output', lines: [
- '',
- '
┊ 🔀 delegate_task "review PR #42 changes" 2.1s',
- ]},
- { type: 'pause', ms: 500 },
- { type: 'output', lines: [
- '
┊ 💻 git diff main..pr-42 0.4s',
- ]},
- { type: 'pause', ms: 400 },
- { type: 'output', lines: [
- '
┊ ✏️ patch tools/registry.py 0.1s',
- ]},
- { type: 'pause', ms: 400 },
- { type: 'output', lines: [
- '
┊ 💻 python -m pytest tests/ -x 3.2s',
- ]},
- { type: 'pause', ms: 400 },
- { type: 'output', lines: [
- '
┊ 💻 git commit -m "fix: handle empty tool schemas" 0.3s',
- ]},
- { type: 'pause', ms: 500 },
- { type: 'output', lines: [
- '',
- '
Found 2 issues in the PR and fixed both:',
- '',
- '
✓ Empty tool schema crash in registry.py — added guard',
- '
✓ Missing error handling in delegate_tool.py — added try/except',
- '',
- '
Tests pass. Committed the fix and pushed to the PR branch.',
- '
I also saved a skill for this PR review pattern.',
- ]},
- { type: 'pause', ms: 2500 },
+ { type: "clear" },
+ { type: "prompt", text: "❯ " },
+ {
+ type: "type",
+ text: "Review the PR at NousResearch/hermes-agent#42 and fix any issues",
+ delay: 30,
+ },
+ { type: "pause", ms: 600 },
+ {
+ type: "output",
+ lines: [
+ "",
+ '
delegate_task "review PR #42 changes" 2.1s',
+ ],
+ },
+ { type: "pause", ms: 500 },
+ {
+ type: "output",
+ lines: [
+ '
git diff main..pr-42 0.4s',
+ ],
+ },
+ { type: "pause", ms: 400 },
+ {
+ type: "output",
+ lines: [
+ '
patch tools/registry.py 0.1s',
+ ],
+ },
+ { type: "pause", ms: 400 },
+ {
+ type: "output",
+ lines: [
+ '
python -m pytest tests/ -x 3.2s',
+ ],
+ },
+ { type: "pause", ms: 400 },
+ {
+ type: "output",
+ lines: [
+ '
git commit -m "fix: handle empty tool schemas" 0.3s',
+ ],
+ },
+ { type: "pause", ms: 500 },
+ {
+ type: "output",
+ lines: [
+ "",
+ '
Found 2 issues in the PR and fixed both:',
+ "",
+ '
✓ Empty tool schema crash in registry.py — added guard',
+ '
✓ Missing error handling in delegate_tool.py — added try/except',
+ "",
+ '
Tests pass. Committed the fix and pushed to the PR branch.',
+ '
I also saved a skill for this PR review pattern.',
+ ],
+ },
+ { type: "pause", ms: 2500 },
- // Scene 3: Session search by keyword
- { type: 'clear' },
- { type: 'prompt', text: '❯ ' },
- { type: 'type', text: 'How did we fix that Docker networking issue?', delay: 35 },
- { type: 'pause', ms: 500 },
- { type: 'output', lines: [
- '',
- '
┊ 🔎 session_search "Docker networking" 1.4s',
- ]},
- { type: 'pause', ms: 500 },
- { type: 'output', lines: [
- '',
- '
Found it — from a session on February 12th:',
- '',
- '
The containers couldn\'t reach each other because the compose',
- '
file was using the default bridge network. We switched to a',
- '
custom network with driver: overlay, added explicit',
- '
aliases, and set dns: 8.8.8.8 as a fallback.',
- '',
- '
The fix was committed in docker-compose.prod.yml.',
- ]},
- { type: 'pause', ms: 3000 },
+ { type: "clear" },
+ { type: "prompt", text: "❯ " },
+ {
+ type: "type",
+ text: "How did we fix that Docker networking issue?",
+ delay: 35,
+ },
+ { type: "pause", ms: 500 },
+ {
+ type: "output",
+ lines: [
+ "",
+ '
session_search "Docker networking" 1.4s',
+ ],
+ },
+ { type: "pause", ms: 500 },
+ {
+ type: "output",
+ lines: [
+ "",
+ '
Found it — from a session on February 12th:',
+ "",
+ '
The containers couldn\'t reach each other because the compose',
+ '
file was using the default bridge network. We switched to a',
+ '
custom network with driver: overlay, added explicit',
+ '
aliases, and set dns: 8.8.8.8 as a fallback.',
+ "",
+ '
The fix was committed in docker-compose.prod.yml.',
+ ],
+ },
+ { type: "pause", ms: 3000 },
];
class TerminalDemo {
- constructor(element, cursorElement) {
- this.el = element;
- this.cursor = cursorElement;
- this.running = false;
- this.content = '';
- this.observer = null;
- }
+ constructor(container) {
+ this.container = container;
+ this.running = false;
+ this.content = "";
+ }
- async start() {
- if (this.running) return;
- this.running = true;
-
- while (this.running) {
- for (const step of demoSequence) {
- if (!this.running) return;
- await this.execute(step);
- }
- // Loop
- this.clear();
- await this.sleep(1000);
+ async start() {
+ if (this.running) return;
+ this.running = true;
+
+ while (this.running) {
+ for (const step of demoSequence) {
+ if (!this.running) return;
+ await this.execute(step);
+ }
+ this.clear();
+ await this.sleep(1000);
+ }
+ }
+
+ stop() {
+ this.running = false;
+ }
+
+ async execute(step) {
+ switch (step.type) {
+ case "prompt":
+ this.append(`
${step.text}`);
+ break;
+ case "type":
+ for (const char of step.text) {
+ if (!this.running) return;
+ this.append(`
${char}`);
+ await this.sleep(step.delay || 30);
}
- }
-
- stop() {
- this.running = false;
- }
-
- async execute(step) {
- switch (step.type) {
- case 'prompt':
- this.append(`
${step.text}`);
- break;
-
- case 'type':
- for (const char of step.text) {
- if (!this.running) return;
- this.append(`
${char}`);
- await this.sleep(step.delay || 30);
- }
- break;
-
- case 'output':
- for (const line of step.lines) {
- if (!this.running) return;
- this.append('\n' + line);
- await this.sleep(50);
- }
- break;
-
- case 'pause':
- await this.sleep(step.ms);
- break;
-
- case 'clear':
- this.clear();
- break;
+ break;
+ case "output":
+ for (const line of step.lines) {
+ if (!this.running) return;
+ this.append("\n" + line);
+ await this.sleep(50);
}
+ break;
+ case "pause":
+ await this.sleep(step.ms);
+ break;
+ case "clear":
+ this.clear();
+ break;
}
+ }
- append(html) {
- this.content += html;
- this.el.innerHTML = this.content;
- // Keep cursor at end
- this.el.parentElement.scrollTop = this.el.parentElement.scrollHeight;
- }
+ append(html) {
+ this.content += html;
+ this.render();
+ }
- clear() {
- this.content = '';
- this.el.innerHTML = '';
- }
+ render() {
+ this.container.innerHTML = this.content + CURSOR;
+ this.container.scrollTop = this.container.scrollHeight;
+ }
- sleep(ms) {
- return new Promise(resolve => setTimeout(resolve, ms));
- }
+ clear() {
+ this.content = "";
+ this.container.innerHTML = "";
+ }
+
+ sleep(ms) {
+ return new Promise((resolve) => setTimeout(resolve, ms));
+ }
+}
+
+// --- Noise Overlay (ported from hermes-chat NoiseOverlay) ---
+function initNoiseOverlay() {
+ if (window.matchMedia("(prefers-reduced-motion: reduce)").matches) return;
+ if (typeof THREE === "undefined") return;
+
+ const canvas = document.getElementById("noise-overlay");
+ if (!canvas) return;
+
+ const vertexShader = `
+ varying vec2 vUv;
+ void main() {
+ vUv = uv;
+ gl_Position = projectionMatrix * modelViewMatrix * vec4(position, 1.0);
+ }
+ `;
+
+ const fragmentShader = `
+ uniform vec2 uRes;
+ uniform float uDpr, uSize, uDensity, uOpacity;
+ uniform vec3 uColor;
+ varying vec2 vUv;
+
+ float hash(vec2 p) {
+ vec3 p3 = fract(vec3(p.xyx) * 0.1031);
+ p3 += dot(p3, p3.yzx + 33.33);
+ return fract((p3.x + p3.y) * p3.z);
+ }
+
+ void main() {
+ float n = hash(floor(vUv * uRes / (uSize * uDpr)));
+ gl_FragColor = vec4(uColor, step(1.0 - uDensity, n)) * uOpacity;
+ }
+ `;
+
+ function hexToVec3(hex) {
+ const c = hex.replace("#", "");
+ return new THREE.Vector3(
+ parseInt(c.substring(0, 2), 16) / 255,
+ parseInt(c.substring(2, 4), 16) / 255,
+ parseInt(c.substring(4, 6), 16) / 255,
+ );
+ }
+
+ const renderer = new THREE.WebGLRenderer({
+ alpha: true,
+ canvas,
+ premultipliedAlpha: false,
+ });
+ renderer.setClearColor(0x000000, 0);
+
+ const scene = new THREE.Scene();
+ const camera = new THREE.OrthographicCamera(-1, 1, 1, -1, 0, 1);
+ const geo = new THREE.PlaneGeometry(2, 2);
+
+ const mat = new THREE.ShaderMaterial({
+ vertexShader,
+ fragmentShader,
+ transparent: true,
+ uniforms: {
+ uColor: { value: hexToVec3("#8090BB") },
+ uDensity: { value: 0.1 },
+ uDpr: { value: 1 },
+ uOpacity: { value: 0.4 },
+ uRes: { value: new THREE.Vector2() },
+ uSize: { value: 1.0 },
+ },
+ });
+
+ scene.add(new THREE.Mesh(geo, mat));
+
+ function resize() {
+ const dpr = window.devicePixelRatio;
+ const w = window.innerWidth;
+ const h = window.innerHeight;
+ renderer.setSize(w, h);
+ renderer.setPixelRatio(dpr);
+ mat.uniforms.uRes.value.set(w * dpr, h * dpr);
+ mat.uniforms.uDpr.value = dpr;
+ }
+
+ resize();
+ window.addEventListener("resize", resize);
+
+ function loop() {
+ requestAnimationFrame(loop);
+ renderer.render(scene, camera);
+ }
+ loop();
}
// --- Initialize ---
-document.addEventListener('DOMContentLoaded', () => {
- // Auto-detect platform and set the right install command
- const detectedPlatform = detectPlatform();
- switchPlatform(detectedPlatform);
+document.addEventListener("DOMContentLoaded", () => {
+ const detectedPlatform = detectPlatform();
+ switchPlatform(detectedPlatform);
- initScrollAnimations();
+ initScrollAnimations();
+ initNoiseOverlay();
- // Terminal demo - start when visible
- const terminalEl = document.getElementById('terminal-content');
- const cursorEl = document.getElementById('terminal-cursor');
-
- if (terminalEl && cursorEl) {
- const demo = new TerminalDemo(terminalEl, cursorEl);
-
- const observer = new IntersectionObserver((entries) => {
- entries.forEach(entry => {
- if (entry.isIntersecting) {
- demo.start();
- } else {
- demo.stop();
- }
- });
- }, { threshold: 0.3 });
+ const terminalEl = document.getElementById("terminal-demo");
- observer.observe(document.querySelector('.terminal-window'));
- }
+ if (terminalEl) {
+ const demo = new TerminalDemo(terminalEl);
- // Smooth nav background on scroll
- const nav = document.querySelector('.nav');
- let ticking = false;
- window.addEventListener('scroll', () => {
- if (!ticking) {
- requestAnimationFrame(() => {
- if (window.scrollY > 50) {
- nav.style.borderBottomColor = 'rgba(255, 215, 0, 0.1)';
- } else {
- nav.style.borderBottomColor = '';
- }
- ticking = false;
- });
- ticking = true;
+ const observer = new IntersectionObserver(
+ (entries) => {
+ entries.forEach((entry) => {
+ if (entry.isIntersecting) {
+ demo.start();
+ } else {
+ demo.stop();
+ }
+ });
+ },
+ { threshold: 0.3 },
+ );
+
+ observer.observe(document.querySelector(".terminal-window"));
+ }
+
+ const nav = document.querySelector(".nav");
+ let ticking = false;
+ window.addEventListener("scroll", () => {
+ if (!ticking) {
+ requestAnimationFrame(() => {
+ if (window.scrollY > 50) {
+ nav.style.borderBottomColor = "rgba(48, 80, 255, 0.15)";
+ } else {
+ nav.style.borderBottomColor = "";
}
- });
+ ticking = false;
+ });
+ ticking = true;
+ }
+ });
});
diff --git a/landingpage/style.css b/landingpage/style.css
index cf05a7a8..30334df0 100644
--- a/landingpage/style.css
+++ b/landingpage/style.css
@@ -1,6 +1,6 @@
/* =========================================================================
Hermes Agent Landing Page
- Colors: Gold (#FFD700) / Amber (#FFBF00) / Bronze (#CD7F32)
+ Colors: Nous Blue (#3050FF) palette
========================================================================= */
/* --- Reset & Base --- */
@@ -11,23 +11,44 @@
}
:root {
- --gold: #FFD700;
- --amber: #FFBF00;
- --bronze: #CD7F32;
- --dark-gold: #B8860B;
- --bg: #07070d;
- --bg-card: #0f0f18;
- --bg-card-hover: #14142a;
- --border: rgba(255, 215, 0, 0.08);
- --border-hover: rgba(255, 215, 0, 0.18);
- --text: #e8e4dc;
- --text-dim: #9a968e;
- --text-muted: #6a665e;
+ --primary: #3050FF;
+ --primary-light: #5070FF;
+ --primary-dim: #2040CC;
+ --primary-dark: #1E30AA;
+ --bg: #0A0E1A;
+ --bg-card: #12182A;
+ --bg-card-hover: #1A2240;
+ --border: rgba(48, 80, 255, 0.1);
+ --border-hover: rgba(48, 80, 255, 0.22);
+ --text: #E8ECFF;
+ --text-dim: #8090BB;
+ --text-muted: #506090;
--font-sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
--font-mono: 'JetBrains Mono', 'Fira Code', 'Cascadia Code', monospace;
--container: 1080px;
--radius: 12px;
--radius-sm: 8px;
+
+ --ease-in-quad: cubic-bezier(.55, .085, .68, .53);
+ --ease-in-cubic: cubic-bezier(.550, .055, .675, .19);
+ --ease-in-quart: cubic-bezier(.895, .03, .685, .22);
+ --ease-in-quint: cubic-bezier(.755, .05, .855, .06);
+ --ease-in-expo: cubic-bezier(.95, .05, .795, .035);
+ --ease-in-circ: cubic-bezier(.6, .04, .98, .335);
+
+ --ease-out-quad: cubic-bezier(.25, .46, .45, .94);
+ --ease-out-cubic: cubic-bezier(.215, .61, .355, 1);
+ --ease-out-quart: cubic-bezier(.165, .84, .44, 1);
+ --ease-out-quint: cubic-bezier(.23, 1, .32, 1);
+ --ease-out-expo: cubic-bezier(.19, 1, .22, 1);
+ --ease-out-circ: cubic-bezier(.075, .82, .165, 1);
+
+ --ease-in-out-quad: cubic-bezier(.455, .03, .515, .955);
+ --ease-in-out-cubic: cubic-bezier(.645, .045, .355, 1);
+ --ease-in-out-quart: cubic-bezier(.77, 0, .175, 1);
+ --ease-in-out-quint: cubic-bezier(.86, 0, .07, 1);
+ --ease-in-out-expo: cubic-bezier(1, 0, 0, 1);
+ --ease-in-out-circ: cubic-bezier(.785, .135, .15, .86);
}
html {
@@ -45,17 +66,17 @@ body {
overflow-x: hidden;
width: 100%;
max-width: 100vw;
- background-image: radial-gradient(rgba(255, 215, 0, 0.03) 1px, transparent 1px);
+ background-image: radial-gradient(rgba(48, 80, 255, 0.04) 1px, transparent 1px);
background-size: 32px 32px;
}
a {
- color: var(--gold);
+ color: var(--primary);
text-decoration: none;
- transition: color 0.2s;
+ transition: color 0.2s var(--ease-out-quad);
}
a:hover {
- color: var(--amber);
+ color: var(--primary-light);
}
strong {
@@ -63,6 +84,17 @@ strong {
font-weight: 600;
}
+/* --- Noise Overlay --- */
+#noise-overlay {
+ position: fixed;
+ inset: 0;
+ width: 100%;
+ height: 100%;
+ z-index: 50;
+ pointer-events: none;
+ mix-blend-mode: soft-light;
+}
+
/* --- Ambient Glow --- */
.ambient-glow {
position: fixed;
@@ -75,7 +107,7 @@ strong {
.glow-1 {
width: 600px;
height: 600px;
- background: var(--gold);
+ background: var(--primary);
top: -200px;
left: -200px;
opacity: 0.08;
@@ -83,7 +115,7 @@ strong {
.glow-2 {
width: 500px;
height: 500px;
- background: var(--bronze);
+ background: var(--primary-dim);
bottom: 20%;
right: -150px;
opacity: 0.06;
@@ -107,6 +139,7 @@ strong {
backdrop-filter: blur(20px);
-webkit-backdrop-filter: blur(20px);
border-bottom: 1px solid var(--border);
+ transition: border-bottom-color 0.3s var(--ease-out-quad);
}
.nav-inner {
@@ -126,12 +159,20 @@ strong {
color: var(--text);
font-weight: 600;
font-size: 15px;
+ transition: color 0.2s var(--ease-out-quad);
}
-.nav-logo:hover { color: var(--gold); }
+.nav-logo:hover { color: var(--primary-light); }
-.nav-symbol {
- font-size: 22px;
- color: var(--gold);
+.nav-nous-logo {
+ width: 22px;
+ height: 22px;
+ border-radius: 4px;
+}
+
+.nav-by {
+ font-weight: 400;
+ color: var(--text-muted);
+ font-size: 13px;
}
.nav-links {
@@ -147,12 +188,79 @@ strong {
display: flex;
align-items: center;
gap: 4px;
- transition: color 0.2s;
+ transition: color 0.2s var(--ease-out-quad);
}
.nav-links a:hover { color: #fff; }
.external-icon { opacity: 0.4; }
+/* --- Hamburger & Mobile Nav --- */
+.nav-hamburger {
+ display: none;
+ background: none;
+ border: none;
+ cursor: pointer;
+ padding: 6px;
+ width: 34px;
+ height: 34px;
+ flex-direction: column;
+ justify-content: center;
+ gap: 5px;
+}
+
+.hamburger-bar {
+ display: block;
+ width: 20px;
+ height: 2px;
+ background: var(--text-dim);
+ border-radius: 1px;
+ transition: transform 0.25s var(--ease-out-quint), opacity 0.2s var(--ease-out-quad);
+ transform-origin: center;
+}
+
+.nav-hamburger.open .hamburger-bar:nth-child(1) {
+ transform: translateY(7px) rotate(45deg);
+}
+
+.nav-hamburger.open .hamburger-bar:nth-child(2) {
+ opacity: 0;
+}
+
+.nav-hamburger.open .hamburger-bar:nth-child(3) {
+ transform: translateY(-7px) rotate(-45deg);
+}
+
+.nav-mobile {
+ display: none;
+}
+
+.nav-mobile.open {
+ display: flex;
+ flex-direction: column;
+ position: absolute;
+ top: 60px;
+ left: 0;
+ right: 0;
+ background: rgba(7, 7, 13, 0.95);
+ backdrop-filter: blur(20px);
+ -webkit-backdrop-filter: blur(20px);
+ border-bottom: 1px solid var(--border);
+ padding: 16px 24px;
+ gap: 16px;
+}
+
+.nav-mobile a {
+ color: var(--text-dim);
+ font-size: 15px;
+ font-weight: 500;
+ padding: 4px 0;
+ transition: color 0.2s var(--ease-out-quad);
+}
+
+.nav-mobile a:hover {
+ color: #fff;
+}
+
/* --- Hero --- */
.hero {
position: relative;
@@ -174,8 +282,8 @@ strong {
align-items: center;
gap: 8px;
padding: 6px 16px;
- background: rgba(255, 215, 0, 0.06);
- border: 1px solid rgba(255, 215, 0, 0.15);
+ background: rgba(48, 80, 255, 0.08);
+ border: 1px solid rgba(48, 80, 255, 0.18);
border-radius: 100px;
font-size: 13px;
color: var(--text-dim);
@@ -187,9 +295,9 @@ strong {
width: 6px;
height: 6px;
border-radius: 50%;
- background: var(--gold);
+ background: var(--primary);
display: inline-block;
- animation: pulse-dot 2s ease-in-out infinite;
+ animation: pulse-dot 2s var(--ease-in-out-quad) infinite;
}
@keyframes pulse-dot {
@@ -199,21 +307,20 @@ strong {
.hero-ascii {
margin-bottom: 28px;
- display: flex;
- justify-content: center;
+ font-family: 'JetBrains Mono', monospace;
+ font-variant-ligatures: none;
+ font-size: clamp(4px, 0.95vw, 11px);
+ line-height: 1.15;
+ color: var(--primary-light);
+ text-align: center;
+ text-shadow: 0 0 20px rgba(48, 80, 255, 0.3);
+ opacity: 0.85;
+ transition: opacity 0.3s var(--ease-out-cubic);
+ overflow-x: auto;
+ white-space: pre;
}
-.hero-logo {
- max-width: 700px;
- width: 100%;
- height: auto;
- display: block;
- filter: drop-shadow(0 0 24px rgba(255, 215, 0, 0.15));
- transition: opacity 0.3s;
- opacity: 0.9;
-}
-
-.hero-ascii:hover .hero-logo {
+.hero-ascii:hover {
opacity: 1;
}
@@ -227,7 +334,7 @@ strong {
}
.hero-gradient {
- background: linear-gradient(135deg, var(--gold), var(--amber), var(--bronze));
+ background: linear-gradient(135deg, var(--primary), var(--primary-light), #90B0FF);
-webkit-background-clip: text;
-webkit-text-fill-color: transparent;
background-clip: text;
@@ -253,7 +360,7 @@ strong {
border: 1px solid var(--border);
border-radius: var(--radius);
overflow: hidden;
- transition: border-color 0.3s;
+ transition: border-color 0.3s var(--ease-out-quad);
}
.install-widget:hover {
@@ -298,7 +405,7 @@ strong {
font-size: 12px;
font-weight: 500;
cursor: pointer;
- transition: all 0.2s;
+ transition: color 0.2s var(--ease-out-quad), background 0.2s var(--ease-out-quad);
background: transparent;
color: var(--text-muted);
}
@@ -309,8 +416,8 @@ strong {
}
.install-tab.active {
- background: rgba(255, 215, 0, 0.12);
- color: var(--gold);
+ background: rgba(48, 80, 255, 0.14);
+ color: var(--primary-light);
}
.install-tab svg {
@@ -329,7 +436,7 @@ strong {
}
.install-prompt {
- color: var(--gold);
+ color: var(--primary-light);
font-weight: 600;
flex-shrink: 0;
opacity: 0.7;
@@ -341,7 +448,7 @@ strong {
overflow: hidden;
text-overflow: ellipsis;
text-align: left;
- transition: opacity 0.15s;
+ transition: opacity 0.15s var(--ease-out-quad);
}
/* --- Code block tabs (install step section) --- */
@@ -358,7 +465,7 @@ strong {
font-size: 11px;
font-weight: 500;
cursor: pointer;
- transition: all 0.2s;
+ transition: color 0.2s var(--ease-out-quad), background 0.2s var(--ease-out-quad);
background: transparent;
color: var(--text-muted);
}
@@ -369,8 +476,8 @@ strong {
}
.code-tab.active {
- background: rgba(255, 215, 0, 0.1);
- color: var(--gold);
+ background: rgba(48, 80, 255, 0.12);
+ color: var(--primary-light);
}
.copy-btn {
@@ -386,11 +493,14 @@ strong {
border-radius: 6px;
font-family: var(--font-sans);
font-size: 12px;
- transition: all 0.2s;
+ transition: color 0.2s var(--ease-out-quad), background 0.2s var(--ease-out-quad);
}
.copy-btn:hover {
- color: var(--gold);
- background: rgba(255, 215, 0, 0.08);
+ color: var(--primary-light);
+ background: rgba(48, 80, 255, 0.1);
+}
+.copy-btn:active {
+ transform: scale(0.95);
}
.install-note {
@@ -414,32 +524,29 @@ strong {
border-radius: var(--radius);
font-size: 14px;
font-weight: 550;
- transition: all 0.25s;
+ transition: background 0.25s var(--ease-out-quint), border-color 0.25s var(--ease-out-quad), color 0.2s var(--ease-out-quad), transform 0.25s var(--ease-out-quint);
border: 1px solid transparent;
+ will-change: transform;
}
.btn-primary {
- background: rgba(255, 215, 0, 0.1);
- color: var(--gold);
- border-color: rgba(255, 215, 0, 0.2);
+ background: rgba(48, 80, 255, 0.12);
+ color: var(--primary-light);
+ border-color: rgba(48, 80, 255, 0.25);
}
.btn-primary:hover {
- background: rgba(255, 215, 0, 0.18);
- border-color: rgba(255, 215, 0, 0.35);
- color: var(--gold);
- transform: translateY(-1px);
+ background: rgba(48, 80, 255, 0.22);
+ border-color: rgba(48, 80, 255, 0.4);
+ color: #fff;
}
-.btn-secondary {
- background: rgba(255, 255, 255, 0.04);
- color: var(--text-dim);
- border-color: rgba(255, 255, 255, 0.08);
+@media (hover: hover) and (pointer: fine) {
+ .btn-primary:hover {
+ transform: translateY(-1px);
+ }
}
-.btn-secondary:hover {
- background: rgba(255, 255, 255, 0.08);
- border-color: rgba(255, 255, 255, 0.15);
- color: var(--text);
- transform: translateY(-1px);
+.btn:active {
+ transform: scale(0.97);
}
/* --- Sections --- */
@@ -457,12 +564,6 @@ strong {
margin-bottom: 48px;
}
-.section-marker {
- font-size: 20px;
- color: var(--gold);
- opacity: 0.7;
-}
-
.section-header h2 {
font-size: 28px;
font-weight: 650;
@@ -479,21 +580,6 @@ strong {
text-align: center;
}
-/* --- Section: What --- */
-.section-what {
- padding: 60px 0 20px;
- border-top: 1px solid var(--border);
-}
-
-.lead-text {
- font-size: 20px;
- line-height: 1.75;
- color: var(--text-dim);
- max-width: 720px;
- margin: 0 auto;
- text-align: center;
-}
-
/* --- Features Grid --- */
.features-grid {
display: grid;
@@ -505,26 +591,41 @@ strong {
background: var(--bg-card);
border: 1px solid var(--border);
border-radius: var(--radius);
- padding: 28px 24px;
- transition: all 0.3s;
+ padding: 20px;
+ transition: border-color 0.3s var(--ease-out-quad), background 0.3s var(--ease-out-quad), transform 0.3s var(--ease-out-quint);
+ will-change: transform;
}
.feature-card:hover {
border-color: var(--border-hover);
background: var(--bg-card-hover);
- transform: translateY(-2px);
+}
+
+@media (hover: hover) and (pointer: fine) {
+ .feature-card:hover {
+ transform: translateY(-2px);
+ }
+}
+
+.feature-header {
+ display: flex;
+ align-items: center;
+ gap: 10px;
+ margin-bottom: 10px;
}
.feature-icon {
- font-size: 28px;
- margin-bottom: 16px;
+ color: var(--primary-light);
+ opacity: 0.85;
+ flex-shrink: 0;
+ display: flex;
+ line-height: 0;
}
.feature-card h3 {
- font-size: 16px;
+ font-size: 15px;
font-weight: 600;
color: #fff;
- margin-bottom: 10px;
letter-spacing: -0.01em;
}
@@ -537,6 +638,8 @@ strong {
/* --- Terminal Demo --- */
.section-demo {
padding-bottom: 60px;
+ border-top: 1px solid var(--border);
+ border-bottom: 1px solid var(--border);
}
.terminal-window {
@@ -590,7 +693,7 @@ strong {
.terminal-cursor {
animation: blink 1s step-end infinite;
- color: var(--gold);
+ color: var(--primary-light);
opacity: 0.8;
}
@@ -600,141 +703,108 @@ strong {
}
/* Terminal demo colors */
-.t-prompt { color: var(--gold); }
+.t-prompt { color: var(--primary-light); }
.t-cmd { color: #fff; }
.t-dim { color: var(--text-muted); }
.t-text { color: var(--text-dim); }
.t-green { color: #4ade80; }
.t-blue { color: #60a5fa; }
-.t-amber { color: var(--amber); }
-.t-bronze { color: var(--bronze); }
+.t-accent { color: var(--primary-light); }
+.t-highlight { color: #90B0FF; }
.t-tool { color: var(--text-muted); }
-/* --- Tools Grid --- */
-.tools-grid {
- display: flex;
- flex-wrap: wrap;
- gap: 10px;
- justify-content: center;
+/* --- Specs Toggle --- */
+.features-more {
+ text-align: center;
+ margin-top: 32px;
}
-.tool-pill {
+.more-toggle {
+ background: none;
+ border: 1px solid var(--border);
+ color: var(--text-dim);
+ font-size: 14px;
+ font-family: inherit;
+ padding: 8px 20px;
+ border-radius: 6px;
+ cursor: pointer;
display: inline-flex;
align-items: center;
- gap: 8px;
- padding: 10px 18px;
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: 100px;
- font-size: 14px;
- color: var(--text-dim);
- transition: all 0.25s;
+ gap: 6px;
+ transition: color 0.2s var(--ease-out-quad), border-color 0.2s var(--ease-out-quad);
}
-.tool-pill:hover {
- border-color: var(--border-hover);
- color: var(--text);
- background: var(--bg-card-hover);
+.more-toggle:hover {
+ color: var(--primary-light);
+ border-color: var(--primary-light);
+}
+.more-toggle:active {
+ transform: scale(0.97);
}
-.tool-emoji {
- font-size: 16px;
+.more-chevron {
+ transition: transform 0.3s var(--ease-in-out-cubic);
}
-/* --- Platforms --- */
-.platforms-row {
+.more-toggle.open .more-chevron {
+ transform: rotate(180deg);
+}
+
+.specs-wrapper {
+ max-height: 0;
+ overflow: hidden;
+ transition: max-height 0.4s var(--ease-out-quart), opacity 0.3s var(--ease-out-quad);
+ opacity: 0;
+}
+
+.specs-wrapper.open {
+ opacity: 1;
+}
+
+/* --- Specs --- */
+.section-specs {
+}
+
+.specs-list {
+ max-width: 720px;
+ margin: 0 auto;
+ padding-top: 24px;
+}
+
+.spec-row {
display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 32px;
-}
-
-.platform-group {
- text-align: center;
-}
-
-.platform-label {
- font-size: 12px;
- text-transform: uppercase;
- letter-spacing: 0.08em;
- color: var(--text-muted);
- margin-bottom: 16px;
- font-weight: 550;
-}
-
-.platform-pills {
- display: flex;
- flex-wrap: wrap;
- gap: 8px;
- justify-content: center;
-}
-
-.platform-pill {
- padding: 8px 16px;
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: 100px;
- font-size: 13px;
- color: var(--text-dim);
- transition: all 0.25s;
-}
-
-.platform-pill:hover {
- border-color: var(--border-hover);
- color: var(--text);
-}
-
-/* --- Skills --- */
-.section-skills {
- border-top: 1px solid var(--border);
-}
-
-.skills-categories {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
+ grid-template-columns: 120px 1fr;
gap: 24px;
+ padding: 24px 0;
+ border-bottom: 1px solid var(--border);
}
-.skill-category {
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: var(--radius);
- padding: 24px;
+.spec-row:last-child {
+ border-bottom: none;
}
-.skill-category h4 {
+.spec-label {
font-size: 14px;
font-weight: 600;
- color: var(--gold);
- margin-bottom: 14px;
+ color: var(--primary-light);
+ padding-top: 2px;
}
-.skill-tags {
- display: flex;
- flex-wrap: wrap;
- gap: 6px;
-}
-
-.skill-tags span {
- padding: 4px 10px;
- background: rgba(255, 215, 0, 0.04);
- border: 1px solid rgba(255, 215, 0, 0.08);
- border-radius: 6px;
- font-size: 12px;
+.spec-value {
+ font-size: 15px;
color: var(--text-dim);
+ line-height: 1.7;
}
-.skill-tags span a {
- color: inherit;
-}
-.skill-tags span a:hover {
- color: var(--gold);
+.spec-value a {
+ color: var(--text);
+ border-bottom: 1px solid var(--border-hover);
+ transition: border-color 0.2s var(--ease-out-quad), color 0.2s var(--ease-out-quad);
}
-.skill-hub-desc {
- font-size: 13px;
- color: var(--text-muted);
- line-height: 1.6;
- margin-top: 12px;
+.spec-value a:hover {
+ color: var(--primary-light);
+ border-color: var(--primary-light);
}
/* --- Install Section --- */
@@ -761,12 +831,12 @@ strong {
display: flex;
align-items: center;
justify-content: center;
- background: rgba(255, 215, 0, 0.08);
- border: 1px solid rgba(255, 215, 0, 0.15);
+ background: rgba(48, 80, 255, 0.1);
+ border: 1px solid rgba(48, 80, 255, 0.2);
border-radius: 50%;
font-size: 14px;
font-weight: 600;
- color: var(--gold);
+ color: var(--primary-light);
margin-top: 2px;
}
@@ -847,119 +917,35 @@ strong {
margin-bottom: 12px;
}
-/* --- Research --- */
-.research-grid {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 16px;
-}
-
-.research-card {
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: var(--radius);
- padding: 24px;
- transition: all 0.3s;
-}
-
-.research-card:hover {
- border-color: var(--border-hover);
- transform: translateY(-2px);
-}
-
-.research-card h4 {
- font-size: 15px;
- font-weight: 600;
- color: #fff;
- margin-bottom: 8px;
-}
-
-.research-card p {
- font-size: 14px;
- color: var(--text-dim);
- line-height: 1.6;
-}
-
/* --- Footer --- */
.footer {
position: relative;
z-index: 1;
- padding: 80px 0 40px;
+ padding: 40px 0 32px;
border-top: 1px solid var(--border);
}
-.footer-grid {
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- gap: 12px;
- margin-bottom: 48px;
-}
-
-.footer-card {
- background: var(--bg-card);
- border: 1px solid var(--border);
- border-radius: var(--radius);
- transition: all 0.25s;
-}
-
-.footer-card:hover {
- border-color: var(--border-hover);
- background: var(--bg-card-hover);
- transform: translateY(-2px);
-}
-
-.footer-card a {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 10px;
- padding: 28px 20px;
- color: var(--text-dim);
- font-size: 14px;
- font-weight: 500;
-}
-
-.footer-card a:hover {
- color: var(--text);
-}
-
-.footer-card svg {
- opacity: 0.7;
-}
-
-.footer-nous-logo {
- width: 28px;
- height: 28px;
- border-radius: 6px;
-}
-
-.footer-skills-icon {
- font-size: 22px;
-}
-
-.footer-bottom {
+.footer-copy {
text-align: center;
- padding-top: 24px;
- border-top: 1px solid var(--border);
-}
-
-.footer-bottom p {
font-size: 13px;
color: var(--text-muted);
}
-.footer-bottom a {
+.footer-copy a {
color: var(--text-dim);
+ transition: color 0.2s var(--ease-out-quad);
}
-.footer-bottom a:hover {
- color: var(--gold);
+
+.footer-copy a:hover {
+ color: var(--primary-light);
}
/* --- Scroll Animations --- */
.fade-in {
opacity: 0;
transform: translateY(20px);
- transition: opacity 0.6s ease, transform 0.6s ease;
+ transition: opacity 0.6s var(--ease-out-quart), transform 0.6s var(--ease-out-quart);
+ will-change: transform, opacity;
}
.fade-in.visible {
@@ -973,16 +959,10 @@ strong {
@media (max-width: 900px) {
.ambient-glow { display: none; }
- .features-grid,
- .research-grid,
- .platforms-row,
- .skills-categories {
+ .features-grid {
grid-template-columns: repeat(2, 1fr);
}
- .footer-grid {
- grid-template-columns: repeat(2, 1fr);
- }
}
@media (max-width: 640px) {
@@ -1012,10 +992,14 @@ strong {
padding: 0 16px;
}
- .nav-links a:not(:last-child):not(:nth-last-child(2)) {
+ .nav-links {
display: none;
}
+ .nav-hamburger {
+ display: flex;
+ }
+
/* --- Hero --- */
.hero {
padding: 90px 16px 50px;
@@ -1032,8 +1016,8 @@ strong {
margin-bottom: 24px;
}
- .hero-logo {
- max-width: 85%;
+ .hero-ascii {
+ font-size: 3.5px;
}
.hero-title {
@@ -1096,39 +1080,23 @@ strong {
}
/* --- Grids → single column --- */
- .features-grid,
- .research-grid,
- .platforms-row,
- .skills-categories,
- .footer-grid {
+ .features-grid {
grid-template-columns: 1fr;
}
+ .spec-row {
+ grid-template-columns: 1fr;
+ gap: 6px;
+ padding: 18px 0;
+ }
+
.feature-card {
- padding: 20px 18px;
- }
-
- .feature-icon {
- font-size: 24px;
- margin-bottom: 12px;
- }
-
- .feature-card h3 {
- font-size: 15px;
+ padding: 16px 18px;
}
.feature-card p {
font-size: 13px;
- }
-
- /* --- Tools pills wrap tighter --- */
- .tools-grid {
- gap: 8px;
- }
-
- .tool-pill {
- padding: 8px 14px;
- font-size: 13px;
+ line-height: 1.5;
}
/* --- Terminal demo --- */
@@ -1163,59 +1131,33 @@ strong {
}
/* --- Footer --- */
- .footer-card a {
- padding: 20px 16px;
- }
-
.footer {
- padding: 50px 0 30px;
+ padding: 32px 0 24px;
}
- .footer-bottom p {
- font-size: 11px;
+}
+
+/* --- Reduced Motion --- */
+@media (prefers-reduced-motion: reduce) {
+ *, *::before, *::after {
+ animation-duration: 0.01ms !important;
+ animation-iteration-count: 1 !important;
+ transition-duration: 0.01ms !important;
}
- /* --- Platform pills --- */
- .platform-pills {
- gap: 6px;
+ .fade-in {
+ opacity: 1;
+ transform: none;
}
- .platform-pill {
- font-size: 12px;
- padding: 6px 12px;
- }
-
- /* --- Skills --- */
- .skill-tags {
- gap: 5px;
- }
-
- .skill-tags span {
- font-size: 11px;
- padding: 3px 8px;
- }
-
- .skill-hub-desc {
- font-size: 12px;
- }
-
- /* --- Research cards --- */
- .research-card {
- padding: 20px;
- }
-
- .research-card h4 {
- font-size: 14px;
- }
-
- .research-card p {
- font-size: 13px;
+ .hero-ascii {
+ opacity: 0.85;
}
}
/* --- Selection --- */
::selection {
- background: rgba(255, 215, 0, 0.2);
+ background: rgba(48, 80, 255, 0.25);
color: #fff;
}
@@ -1232,5 +1174,5 @@ strong {
border-radius: 3px;
}
::-webkit-scrollbar-thumb:hover {
- background: var(--dark-gold);
+ background: var(--primary-dim);
}