From ebd4f2c6a878be132245d4a1ddac7232bc4bbdac Mon Sep 17 00:00:00 2001 From: Austin Pickett Date: Fri, 13 Mar 2026 15:03:38 -0400 Subject: [PATCH] fix: redesign landing page with Nous blue palette and cleaner layout (#974) * fix: redesign landing page with Nous blue palette and cleaner layout * fix: add features link * fix: misc refactors, easings * fix: animations, easings * fix: mobile --- landingpage/index.html | 1084 +++++++++++++++++++++++----------------- landingpage/script.js | 739 ++++++++++++++++----------- landingpage/style.css | 694 ++++++++++++------------- 3 files changed, 1399 insertions(+), 1118 deletions(-) diff --git a/landingpage/index.html b/landingpage/index.html index 6f8dc3b3..e24ed11c 100644 --- a/landingpage/index.html +++ b/landingpage/index.html @@ -1,410 +1,325 @@ - + - - - + + + Hermes Agent — An Agent That Grows With You - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + +
- -
-
-
- - Open Source · MIT License -
- - - -

- An agent that
- grows with you. -

- -

- Install it on a machine, give it your messaging accounts, and it becomes a - persistent personal agent that grows with you — learning your projects, - building its own skills, and reaching you wherever you are. -

- -
-
-
-
- - - -
-
- -
-
-
- $ - curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash - -
-
-

Works on Linux, macOS & WSL2 · No prerequisites · Installs everything automatically

-
- - +
+
+ + Open Source • MIT License
+ + + + +

+ An agent that
+ grows with you. +

+ +

+ It's not a coding copilot tethered to an IDE or a chatbot wrapper + around a single API. It's an autonomous agent that + lives on your server, remembers what it learns, and gets more capable + the longer it runs. +

+ +
+
+
+
+ + + +
+
+ +
+
+
+ $ + curl -fsSL + https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh + | bash + +
+
+

+ Works on Linux, macOS & WSL2 · No prerequisites · Installs + everything automatically +

+
+ + +
- -
-
-

- It's not a coding copilot tethered to an IDE or a chatbot wrapper around a single API. - It's an autonomous agent that lives on your server, remembers what it learns, - and gets more capable the longer it runs. -

-
-
- - -
-
-
- -

What it does

-
- -
-
-
💬
-

Lives Where You Do

-

Telegram, Discord, Slack, WhatsApp, and CLI — all from a single gateway process. Voice memo transcription, cross-platform continuation. Start a conversation on Telegram, pick it up in your terminal.

-
- -
-
🧠
-

Grows the Longer It Runs

-

Persistent memory across sessions — it learns your preferences, projects, and environment. When it solves a hard problem, it writes a skill document so it never forgets how. Skills are searchable and shareable.

-
- -
-
-

Scheduled Automations

-

Built-in cron scheduler with delivery to any platform. Natural language scheduling for daily reports, nightly backups, weekly audits, morning briefings — all running unattended through the gateway.

-
- -
-
🔀
-

Delegates & Parallelizes

-

Spawn isolated subagents for parallel workstreams. Each gets its own conversation and terminal. Write Python scripts that call tools via RPC, collapsing multi-step pipelines into zero-context-cost turns.

-
- -
-
🔒
-

Real Sandboxing

-

Five terminal backends: local, Docker, SSH, Singularity, and Modal. Container security hardening with read-only root, dropped capabilities, PID limits, and namespace isolation.

-
- -
-
🌐
-

Full Web & Browser Control

-

Web search, page extraction, full browser automation — navigate, click, type, screenshot. Plus vision analysis, image generation, text-to-speech, and multi-model collaborative reasoning.

-
-
-
-
- - -
-
-
- -

See it in action

-
- -
-
-
- - - -
- hermes -
-
-
- -
-
-
-
- - -
-
-
- -

40+ built-in tools

-
- -
-
- 🔍 Web Search -
-
- 💻 Terminal -
-
- 📁 File System -
-
- 🌐 Browser -
-
- 👁 Vision -
-
- 🎨 Image Gen -
-
- 🔊 Text-to-Speech -
-
- 🧠 Memory -
-
- 📋 Task Planning -
-
- Cron Jobs -
-
- 🐍 Code Execution -
-
- 🔀 Subagents -
-
- 📚 Skills -
-
- 🤖 Multi-Model Reasoning -
-
- 📨 Messaging -
-
- 🔎 Session Search -
-
-
-
- - -
-
-
- -

Works with everything

-
- -
-
-

Chat Platforms

-
- Telegram - Discord - Slack - WhatsApp - CLI -
-
-
-

LLM Providers

-
- Nous Portal - OpenRouter - Custom API -
-
-
-

Execution Environments

-
- Local - Docker - SSH - Singularity - Modal -
-
-
-
-
- - -
-
-
- -

40+ built-in skills & growing

-
- -

- Skills are procedural memory — reusable approaches for recurring tasks. - The agent creates them when it solves hard problems, and loads them automatically when similar tasks come up. - Install more from community hubs with a single command. -

- -
-
-

Built-in Skills

-

40+ skills bundled out of the box covering MLOps, GitHub workflows, diagramming, note-taking, and more. The agent also creates new skills on the fly as it works.

-
-
-

Skills Hub Integrations

-
- agentskills.io - GitHub Repos - ClawHub - LobeHub - Claude Code Marketplace -
-

Browse, install, and manage skills from multiple community hubs. Quarantine and audit systems keep your agent safe.

-
-
-

Open Standard

-

Skills follow the agentskills.io open format — portable SKILL.md files that any agent can use. Create your own and share them.

-
-
-
-
- -
-
-
- -

Get started in 60 seconds

-
- -
-
-
1
-
-

Install

-
-
-
- -
- -
-
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
-
-

Installs uv, Python 3.11, clones the repo, sets up everything. No sudo needed.

-
-
+
+
+

Get started in 60 seconds

+
-
-
2
-
-

Configure

-
-
- bash - -
-
# Interactive setup wizard
+        
+
+
1
+
+

Install

+
+
+
+ +
+ +
+
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
+
+

+ Installs uv, Python 3.11, clones the repo, sets up everything. + No sudo needed. +

+
+
+ +
+
2
+
+

Configure

+
+
+ bash + +
+
# Interactive setup wizard
 hermes setup
 
 # Or choose your model
 hermes model
-
-

Connect to Nous Portal (OAuth), OpenRouter (API key), or your own endpoint.

-
-
+
+

+ Connect to Nous Portal (OAuth), OpenRouter (API key), or your + own endpoint. +

+
+
-
-
3
-
-

Start chatting

-
-
- bash - -
-
hermes
-
-

That's it. Full interactive CLI with tools, memory, and skills.

-
+
+
3
+
+

Start chatting

+
+
+ bash +
+
hermes
+
+

+ That's it. Full interactive CLI with tools, memory, and skills. +

+
+
-
-
4
-
-

Go multi-platform (optional)

-
-
- bash - -
-
# Interactive gateway setup wizard
+          
+
4
+
+

+ Go multi-platform (optional) +

+
+
+ bash + +
+
# Interactive gateway setup wizard
 hermes gateway setup
 
 # Start the messaging gateway
@@ -412,94 +327,339 @@ hermes gateway
 
 # Install as a system service
 hermes gateway install
-
-

Walk through connecting Telegram, Discord, Slack, or WhatsApp. Runs as a systemd service.

-
-
- -
-
5
-
-

Keep it up to date

-
-
- bash - -
-
hermes update
-
-

Pulls the latest changes and reinstalls dependencies. Run anytime to get new features and fixes.

-
-
+
+

+ Walk through connecting Telegram, Discord, Slack, or WhatsApp. + Runs as a systemd service. +

+
-
-

🪟 Native Windows support is extremely experimental and unsupported. Please install WSL2 and run Hermes Agent from there.

+
+
5
+
+

Keep it up to date

+
+
+ bash + +
+
hermes update
+
+

+ Pulls the latest changes and reinstalls dependencies. Run + anytime to get new features and fixes. +

+
+ +
+

+ Native Windows support is extremely experimental and unsupported. + Please install + WSL2 + and run Hermes Agent from there. +

+
+
- -
-
-
- -

Research-ready

-
- -
-
-

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.

-
-
+ +
+
+
+

See it in action

+ +
+
+
+ + + +
+ hermes +
+
+
+
+
+ + +
+
+
+

Features

+
+ +
+
+
+
+ + + +
+

Lives Where You Do

+
+

+ Telegram, Discord, Slack, WhatsApp, and CLI from a single gateway + — start on one, pick up on another. +

+
+ +
+
+
+ + + + +
+

Grows the Longer It Runs

+
+

+ Persistent memory and auto-generated skills — it learns your + projects and never forgets how it solved a problem. +

+
+ +
+
+
+ + + + +
+

Scheduled Automations

+
+

+ Natural language cron scheduling for reports, backups, and + briefings — running unattended through the gateway. +

+
+ +
+
+
+ + + + + + +
+

Delegates & Parallelizes

+
+

+ Isolated subagents with their own conversations, terminals, and + Python RPC scripts for zero-context-cost pipelines. +

+
+ +
+
+
+ + + + +
+

Real Sandboxing

+
+

+ Five backends — local, Docker, SSH, Singularity, Modal — with + container hardening and namespace isolation. +

+
+ +
+
+
+ + + + + +
+

Full Web & Browser Control

+
+

+ 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); }