chore: update the website (#1475)

Drew Smirnoff created

## What?
Updates the website

## Why?

Removes some outdated information, better UI

---------

Signed-off-by: drew <me@andrinoff.com>

Change summary

public/components/site.jsx | 408 +++++++------------
public/index.html          |  17 
public/styles.css          | 819 +++++++++++++--------------------------
3 files changed, 444 insertions(+), 800 deletions(-)

Detailed changes

public/components/site.jsx ๐Ÿ”—

@@ -1,5 +1,44 @@
 const { useState, useEffect } = React;
 
+// --- Global hooks ---
+
+function useScrollReveal() {
+  useEffect(() => {
+    const els = document.querySelectorAll(".reveal");
+    if (!els.length) return;
+    if (!("IntersectionObserver" in window)) {
+      els.forEach((el) => el.classList.add("visible"));
+      return;
+    }
+    const obs = new IntersectionObserver(
+      (entries) => {
+        entries.forEach((e) => {
+          if (e.isIntersecting) {
+            e.target.classList.add("visible");
+            obs.unobserve(e.target);
+          }
+        });
+      },
+      { threshold: 0.08, rootMargin: "0px 0px -40px 0px" },
+    );
+    els.forEach((el) => obs.observe(el));
+    return () => obs.disconnect();
+  }, []);
+}
+
+function useMouseGlow() {
+  useEffect(() => {
+    const move = (e) => {
+      document.documentElement.style.setProperty("--mx", e.clientX + "px");
+      document.documentElement.style.setProperty("--my", e.clientY + "px");
+    };
+    window.addEventListener("mousemove", move, { passive: true });
+    return () => window.removeEventListener("mousemove", move);
+  }, []);
+}
+
+// --- Components ---
+
 function FloatpaneMark({ size = 20 }) {
   return (
     <img
@@ -15,7 +54,6 @@ function FloatpaneMark({ size = 20 }) {
 function MatchaWordmark() {
   const [version, setVersion] = useState("v0.8.2");
   useEffect(() => {
-    // floatpane/matcha repo โ€” fetch latest release tag
     fetch("https://api.github.com/repos/floatpane/matcha/releases/latest")
       .then((r) => (r.ok ? r.json() : null))
       .then((d) => {
@@ -61,7 +99,6 @@ function TopNav() {
       </a>
       <nav className="nav-links">
         <a href="#features">Features</a>
-        <a href="#keys">Keybinds</a>
         <a href="#install">Install</a>
         <a href="https://docs.matcha.email">Docs โ†—</a>
         <a href="https://github.com/floatpane/matcha" className="nav-github">
@@ -78,9 +115,64 @@ function TopNav() {
   );
 }
 
-function Hero({ datasetKey, setDatasetKey }) {
-  const [pressed, setPressed] = useState(false);
-  const TUI = window.MatchaTUI;
+function QuickStart() {
+  const CMD1 = "brew install floatpane/matcha/matcha";
+  const CMD2 = "matcha";
+  const [l1, setL1] = useState("");
+  const [l2, setL2] = useState("");
+  const [phase, setPhase] = useState("pre"); // pre โ†’ t1 โ†’ pause โ†’ t2 โ†’ done
+
+  useEffect(() => {
+    let t;
+    if (phase === "pre") {
+      t = setTimeout(() => setPhase("t1"), 1400);
+    } else if (phase === "t1") {
+      if (l1.length < CMD1.length) {
+        t = setTimeout(() => setL1(CMD1.slice(0, l1.length + 1)), 36);
+      } else {
+        t = setTimeout(() => setPhase("pause"), 420);
+      }
+    } else if (phase === "pause") {
+      t = setTimeout(() => setPhase("t2"), 320);
+    } else if (phase === "t2") {
+      if (l2.length < CMD2.length) {
+        t = setTimeout(() => setL2(CMD2.slice(0, l2.length + 1)), 90);
+      } else {
+        setPhase("done");
+      }
+    }
+    return () => clearTimeout(t);
+  }, [phase, l1, l2]);
+
+  const caret1 = phase === "pre" || phase === "t1" || phase === "pause";
+  const showL2 = phase === "t2" || phase === "done";
+
+  return (
+    <div className="quickstart">
+      <div className="qs-bar">
+        <span className="qs-dot qs-r" />
+        <span className="qs-dot qs-y" />
+        <span className="qs-dot qs-g" />
+        <span className="qs-bar-label">terminal</span>
+      </div>
+      <pre className="qs-code">
+        <span className="qs-prompt">$ </span>
+        {l1}
+        {caret1 && <span className="qs-caret" />}
+        {showL2 && (
+          <>
+            {"\n"}
+            <span className="qs-prompt">$ </span>
+            {l2}
+            <span className="qs-caret" />
+          </>
+        )}
+      </pre>
+    </div>
+  );
+}
+
+function Hero() {
   return (
     <section className="hero" id="top">
       <div className="hero-copy">
@@ -89,25 +181,24 @@ function Hero({ datasetKey, setDatasetKey }) {
           <span>by floatpane ยท local-first ยท secure ยท no telemetry</span>
         </div>
         <h1 className="hero-h1">
-          A powerful, feature-rich
+          Email for people who
           <br />
-          email client <em>for your terminal.</em>
+          live in the <em>terminal.</em>
         </h1>
         <p className="hero-sub">
-          Matcha is a modern TUI email client for people who live in the shell.
-          Vim keybindings, PGP, IMAP multi-account, markdown composing,
-          visual-mode batch ops, and a CLI that speaks your language.
+          Matcha is a keyboard-native email client built for the shell.
+          Multi-account IMAP, PGP encryption, markdown composing, and a CLI
+          that pipes. One static binary. No cloud. No trackers.
         </p>
         <div className="hero-cta">
-          <a href="#install" className="btn btn-primary">
-            <span>Install</span>
-            <span className="btn-k">โ†ต</span>
+          <a href="#install" className="btn btn-primary btn-lg">
+            Install now
           </a>
-          <a href="https://docs.matcha.email" className="btn btn-ghost">
-            <span>Read the docs</span>
-            <span className="btn-k">โ†’</span>
+          <a href="https://docs.matcha.email" className="btn btn-ghost btn-lg">
+            Read the docs <span className="btn-k">โ†’</span>
           </a>
         </div>
+        <QuickStart />
         <div className="hero-meta">
           <div>
             <span className="dim">license</span> MIT
@@ -120,45 +211,6 @@ function Hero({ datasetKey, setDatasetKey }) {
           </div>
         </div>
       </div>
-
-      <div className="hero-demo">
-        <div className="hero-demo-chrome">
-          <div className="hero-demo-label">
-            <span className="dim">demo ยท</span>
-            <button
-              className={"demo-swap " + (datasetKey === "default" ? "on" : "")}
-              onClick={() => setDatasetKey("default")}
-            >
-              drew's inbox
-            </button>
-            <button
-              className={"demo-swap " + (datasetKey === "dev" ? "on" : "")}
-              onClick={() => setDatasetKey("dev")}
-            >
-              floatpane dev
-            </button>
-            <button
-              className={"demo-swap " + (datasetKey === "personal" ? "on" : "")}
-              onClick={() => setDatasetKey("personal")}
-            >
-              personal
-            </button>
-          </div>
-          <div className="hero-demo-hint">
-            {pressed ? (
-              <span>โœ“ keyboard live</span>
-            ) : (
-              <span className="dim">
-                click ยท <kbd>j</kbd>
-                <kbd>k</kbd> ยท <kbd>tab</kbd> ยท <kbd>โ†ต</kbd>
-              </span>
-            )}
-          </div>
-        </div>
-        {TUI && (
-          <TUI datasetKey={datasetKey} onKeyPressed={() => setPressed(true)} />
-        )}
-      </div>
     </section>
   );
 }
@@ -166,14 +218,14 @@ function Hero({ datasetKey, setDatasetKey }) {
 const FEATURES = [
   {
     k: "01",
-    title: "Email, the way you move",
-    body: "Read, reply, delete, archive โ€” all from the home row. j/k to move, h/l between accounts, tab between folders. No mouse, no modals.",
-    mono: "j  k  h  l  r  d  a  โ†ต",
+    title: "Keyboard-native",
+    body: "Read, reply, delete, archive โ€” all from the keyboard. Navigate messages, switch accounts, and jump between folders without touching the mouse.",
+    mono: "j  k  r  d  a  โ†ต  esc",
   },
   {
     k: "02",
-    title: "Visual mode, for real",
-    body: "Press v to enter Vim-style multi-select. Expand with j/k, then d, a, or m to run batch ops as a single IMAP command.",
+    title: "Visual mode batch ops",
+    body: "Enter visual mode to select a range of messages, then delete, archive, or move them all as a single IMAP command.",
     mono: "v  j j j  d\nโ†’ deleted 4 messages",
   },
   {
@@ -185,19 +237,19 @@ const FEATURES = [
   {
     k: "04",
     title: "Multi-account, tabbed",
-    body: "IMAP, Gmail, Fastmail, Proton Bridge โ€” all tabbed in one window. h and l switch between them so you never reply from the wrong address.",
+    body: "IMAP, Gmail, Fastmail, Proton Bridge โ€” all in one window. Switch between them instantly so you never reply from the wrong address.",
     mono: "โ† me@andrinoff\nโ†’ drew@floatpane",
   },
   {
     k: "05",
     title: "Fuzzy filter",
-    body: "Press / to fuzzy-filter across senders, subjects, and bodies in the active view. Results stream in as you type.",
+    body: "Filter across senders, subjects, and bodies in the active view. Results stream in as you type.",
     mono: "/lena  โ†’  3 hits",
   },
   {
     k: "06",
     title: "Local-first drafts",
-    body: "Every keystroke hits disk before it hits the wire. Close the laptop, open it anywhere, pick up mid-sentence. Esc saves.",
+    body: "Every keystroke hits disk before it hits the wire. Close the laptop, open it anywhere, pick up mid-sentence.",
     mono: "~/.cache/matcha/drafts",
   },
   {
@@ -208,22 +260,22 @@ const FEATURES = [
   },
   {
     k: "08",
-    title: "AI, on your terms",
-    body: "Rewrite drafts with the model of your choice. Let agents send on your behalf โ€” with strict scopes and an audit log.",
-    mono: "alt + r: make it more formal",
+    title: "Inline image rendering",
+    body: "Images render inline via iTerm2 or kitty graphics where supported. Toggle with a key. Off by default, always.",
+    mono: "โ†’ โ—ง images on",
   },
   {
     k: "09",
-    title: "Smart image rendering",
-    body: "Images render inline via iterm2 or kitty-graphics where supported. Toggle with i. Off by default, always.",
-    mono: "i  โ†’  โ—ง images on",
+    title: "Full-disk encryption",
+    body: "Encrypt all local data with a password that is never stored โ€” not on disk, not in the keyring. Matcha shows a lock screen on startup. Forget the password and there is no reset.",
+    mono: "matcha is locked\n> โ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ขโ€ข\nenter: unlock",
   },
 ];
 
 function Features() {
   return (
     <section className="features" id="features">
-      <div className="section-head">
+      <div className="section-head reveal">
         <div className="section-head-l">
           <div className="section-eyebrow">ยง features</div>
           <h2 className="section-h2">
@@ -234,13 +286,17 @@ function Features() {
         </div>
         <p className="section-head-r">
           Matcha is opinionated. It won't follow you around the web, won't
-          upsell you on AI credits, and won't sync your signatures to a SaaS. It
+          upsell you on credits, and won't sync your signatures to a SaaS. It
           reads mail. It writes mail. It stays out of the way.
         </p>
       </div>
       <div className="feature-grid">
-        {FEATURES.map((f) => (
-          <article key={f.k} className="feature">
+        {FEATURES.map((f, i) => (
+          <article
+            key={f.k}
+            className="feature reveal"
+            style={{ transitionDelay: `${i * 55}ms` }}
+          >
             <div className="feature-head">
               <span className="feature-k">{f.k}</span>
               <span className="feature-dash">โ€”โ€”</span>
@@ -255,113 +311,6 @@ function Features() {
   );
 }
 
-function Keybinds() {
-  const rows = [
-    {
-      g: "motion",
-      items: [
-        ["j / k", "next / prev message"],
-        ["โ†‘ / โ†“", "next / prev message"],
-        ["h / l", "prev / next account"],
-        ["โ† / โ†’", "prev / next account"],
-        ["tab", "next folder"],
-        ["shift-tab", "prev folder"],
-      ],
-    },
-    {
-      g: "inbox",
-      items: [
-        ["โ†ต", "open email"],
-        ["r", "refresh"],
-        ["d", "delete"],
-        ["a", "archive"],
-        ["/", "filter"],
-        ["v", "visual mode"],
-        ["esc", "back / main menu"],
-      ],
-    },
-    {
-      g: "visual mode",
-      items: [
-        ["v", "enter visual mode"],
-        ["j / k", "expand selection"],
-        ["d", "delete all selected"],
-        ["a", "archive all selected"],
-        ["m", "move to folder"],
-        ["v / esc", "exit visual mode"],
-      ],
-    },
-    {
-      g: "email view",
-      items: [
-        ["j / k", "scroll body"],
-        ["r", "reply"],
-        ["d", "delete"],
-        ["a", "archive"],
-        ["tab", "focus attachments"],
-        ["i", "toggle images"],
-        ["esc", "back to inbox"],
-      ],
-    },
-    {
-      g: "attachments",
-      items: [
-        ["j / k", "navigate"],
-        ["โ†ต", "download & open"],
-        ["tab / esc", "back to body"],
-      ],
-    },
-    {
-      g: "composer",
-      items: [
-        ["tab / shift-tab", "navigate fields"],
-        ["โ†ต on From", "select account"],
-        ["โ†ต on Attachment", "open file picker"],
-        ["โ†ต on Send", "send email"],
-        ["โ†‘ / โ†“", "contact suggestions"],
-        ["esc", "save draft & exit"],
-      ],
-    },
-  ];
-  return (
-    <section className="keybinds" id="keys">
-      <div className="section-head">
-        <div className="section-head-l">
-          <div className="section-eyebrow">ยง keybinds</div>
-          <h2 className="section-h2">
-            Vim-native.
-            <br />
-            <span className="dim">Home row to inbox zero.</span>
-          </h2>
-        </div>
-        <p className="section-head-r">
-          Every binding is documented at{" "}
-          <a href="https://docs.matcha.email" className="underline-link">
-            docs.matcha.email
-          </a>
-          . Muscle-memory for vimmers, learnable for everyone else.
-        </p>
-      </div>
-      <div className="keybinds-grid">
-        {rows.map((row) => (
-          <div key={row.g} className="keybinds-col">
-            <div className="keybinds-col-head">โ”€โ”€ {row.g} โ”€โ”€</div>
-            {row.items.map(([k, label]) => (
-              <div key={k} className="keybind-row">
-                <span className="keybind-k">
-                  <kbd>{k}</kbd>
-                </span>
-                <span className="keybind-dots">{"ยท".repeat(26)}</span>
-                <span className="keybind-label">{label}</span>
-              </div>
-            ))}
-          </div>
-        ))}
-      </div>
-    </section>
-  );
-}
-
 const INSTALL_TABS = {
   brew: {
     plat: "macOS ยท Linux",
@@ -371,6 +320,10 @@ const INSTALL_TABS = {
     plat: "Windows 10 / 11",
     cmd: "$ winget install --id=floatpane.matcha\n$ matcha",
   },
+  scoop: {
+    plat: "Windows",
+    cmd: "$ scoop install matcha\n$ matcha",
+  },
   snap: { plat: "Ubuntu ยท Linux", cmd: "$ sudo snap install matcha\n$ matcha" },
   flatpak: {
     plat: "Linux",
@@ -379,7 +332,11 @@ const INSTALL_TABS = {
   aur: { plat: "Arch Linux", cmd: "$ yay -S matcha-client-bin\n$ matcha" },
   nix: {
     plat: "NixOS ยท any Nix",
-    cmd: "$ nix profile install github:floatpane/matcha\n$ matcha",
+    cmd: "$ nix profile install github:floatpane/nix-matcha\n$ matcha",
+  },
+  nixpkgs: {
+    plat: "NixOS ยท nixpkgs",
+    cmd: "$ nix profile install nixpkgs#matcha\n$ matcha",
   },
 };
 
@@ -416,7 +373,7 @@ function Install() {
   const t = INSTALL_TABS[tab];
   return (
     <section className="install" id="install">
-      <div className="section-head">
+      <div className="section-head reveal">
         <div className="section-head-l">
           <div className="section-eyebrow">ยง install</div>
           <h2 className="section-h2">
@@ -437,7 +394,7 @@ function Install() {
           .
         </p>
       </div>
-      <div className="install-card">
+      <div className="install-card reveal" style={{ transitionDelay: "0.15s" }}>
         <div className="install-tabs">
           {Object.keys(INSTALL_TABS).map((k) => (
             <button
@@ -469,15 +426,21 @@ function CTA() {
   return (
     <section className="cta">
       <div className="cta-inner">
-        <div className="cta-pre">$ _</div>
-        <h2 className="cta-h2">
+        <div className="cta-pre reveal">$ _</div>
+        <h2
+          className="cta-h2 reveal"
+          style={{ transitionDelay: "0.12s" }}
+        >
           Your inbox is waiting
           <br />
           in the terminal.
         </h2>
-        <div className="cta-row">
+        <div
+          className="cta-row reveal"
+          style={{ transitionDelay: "0.24s" }}
+        >
           <a href="#install" className="btn btn-primary btn-lg">
-            make your emails secure
+            install matcha
           </a>
           <a href="https://docs.matcha.email" className="btn btn-ghost btn-lg">
             read the docs โ†’
@@ -495,7 +458,7 @@ function Footer() {
         <div className="footer-brand">
           <MatchaWordmark />
           <p className="footer-tag">
-            a modern TUI email client.
+            a keyboard-native email client.
             <br />
             made with care by floatpane.
           </p>
@@ -504,7 +467,6 @@ function Footer() {
           <div>
             <div className="footer-h">product</div>
             <a href="#features">features</a>
-            <a href="#keys">keybinds</a>
             <a href="#install">install</a>
             <a href="https://github.com/floatpane/matcha/releases">releases</a>
           </div>
@@ -544,79 +506,17 @@ function Footer() {
   );
 }
 
-// ---------- Tweaks ----------
-function Tweaks({ datasetKey, setDatasetKey, visible }) {
-  if (!visible) return null;
-  return (
-    <div className="tweaks">
-      <div className="tweaks-head">Tweaks</div>
-      <div className="tweaks-sub">Demo content</div>
-      {Object.entries({
-        default: "drew's inbox",
-        dev: "floatpane dev",
-        personal: "personal",
-      }).map(([k, label]) => (
-        <button
-          key={k}
-          onClick={() => setDatasetKey(k)}
-          className={"tweaks-opt " + (datasetKey === k ? "on" : "")}
-        >
-          <span className="tweaks-dot">{datasetKey === k ? "โ—" : "โ—‹"}</span>
-          <span>{label}</span>
-        </button>
-      ))}
-    </div>
-  );
-}
-
 function App() {
-  const [datasetKey, setDatasetKey] = useState(() => {
-    try {
-      return localStorage.getItem("matcha-dataset") || "default";
-    } catch {
-      return "default";
-    }
-  });
-  const [tweaksVisible, setTweaksVisible] = useState(false);
-
-  useEffect(() => {
-    try {
-      localStorage.setItem("matcha-dataset", datasetKey);
-    } catch {}
-  }, [datasetKey]);
-
-  useEffect(() => {
-    const onMsg = (e) => {
-      const d = e.data || {};
-      if (d.type === "__activate_edit_mode") setTweaksVisible(true);
-      if (d.type === "__deactivate_edit_mode") setTweaksVisible(false);
-    };
-    window.addEventListener("message", onMsg);
-    window.parent.postMessage({ type: "__edit_mode_available" }, "*");
-    return () => window.removeEventListener("message", onMsg);
-  }, []);
-
-  useEffect(() => {
-    window.parent.postMessage(
-      { type: "__edit_mode_set_keys", edits: { datasetKey } },
-      "*",
-    );
-  }, [datasetKey]);
-
+  useScrollReveal();
+  useMouseGlow();
   return (
     <div className="site">
       <TopNav />
-      <Hero datasetKey={datasetKey} setDatasetKey={setDatasetKey} />
+      <Hero />
       <Features />
-      <Keybinds />
       <Install />
       <CTA />
       <Footer />
-      <Tweaks
-        datasetKey={datasetKey}
-        setDatasetKey={setDatasetKey}
-        visible={tweaksVisible}
-      />
     </div>
   );
 }

public/index.html ๐Ÿ”—

@@ -3,10 +3,10 @@
     <head>
         <meta charset="utf-8" />
         <meta name="viewport" content="width=device-width, initial-scale=1" />
-        <title>Matcha โ€” a TUI email client, by Floatpane</title>
+        <title>Matcha โ€” email for your terminal, by Floatpane</title>
         <meta
             name="description"
-            content="Matcha is a modern TUI email client by Floatpane. Vim keybindings, PGP, IMAP multi-account, markdown composing, Lua plugins."
+            content="Matcha is a keyboard-native email client for the terminal. Multi-account IMAP, PGP, markdown composing, and a CLI that pipes. One binary. No cloud."
         />
 
         <link rel="preconnect" href="https://fonts.googleapis.com" />
@@ -16,15 +16,7 @@
             rel="stylesheet"
         />
         <link rel="icon" type="image/png" href="assets/favicon.png" />
-        <link rel="stylesheet" href="styles.css?v=3" />
-
-        <script>
-            /*EDITMODE-BEGIN*/
-            const TWEAK_DEFAULTS = {
-                datasetKey: "default",
-            };
-            /*EDITMODE-END*/
-        </script>
+        <link rel="stylesheet" href="styles.css?v=4" />
     </head>
     <body>
         <div id="root"></div>
@@ -45,8 +37,7 @@
             crossorigin="anonymous"
         ></script>
 
-        <script type="text/babel" src="components/tui.jsx?v=3"></script>
-        <script type="text/babel" src="components/site.jsx?v=3"></script>
+        <script type="text/babel" src="components/site.jsx?v=4"></script>
 
         <script type="text/babel">
             const root = ReactDOM.createRoot(document.getElementById("root"));

public/styles.css ๐Ÿ”—

@@ -1,6 +1,5 @@
 /* ============ Matcha site styles ============ */
 :root {
-    /* Dark blue-green terminal, like the real Matcha screenshots */
     --bg: #05080a;
     --bg-2: #060a0d;
     --panel: #0a1013;
@@ -11,7 +10,6 @@
     --ink-dim: #7d9099;
     --ink-dim2: #52666e;
 
-    /* green accent โ€” slightly darker, per request */
     --accent: #6aa84f;
     --accent-2: #85c26a;
     --accent-deep: #3b6b28;
@@ -24,11 +22,20 @@
     --sans:
         "IBM Plex Sans", ui-sans-serif, -apple-system, system-ui, sans-serif;
     --serif: "IBM Plex Serif", ui-serif, Georgia, serif;
+
+    /* mouse glow position โ€” updated by JS */
+    --mx: 50%;
+    --my: 30%;
 }
 
 * {
     box-sizing: border-box;
 }
+
+html {
+    scroll-behavior: smooth;
+}
+
 html,
 body {
     margin: 0;
@@ -41,6 +48,8 @@ body {
     -webkit-font-smoothing: antialiased;
     text-rendering: optimizeLegibility;
 }
+
+/* Static ambient gradient */
 body::before {
     content: "";
     position: fixed;
@@ -59,28 +68,48 @@ body::before {
             transparent 60%
         );
 }
+
+/* Mouse-follow glow overlay */
+body::after {
+    content: "";
+    position: fixed;
+    inset: 0;
+    pointer-events: none;
+    z-index: 0;
+    background: radial-gradient(
+        560px circle at var(--mx) var(--my),
+        rgba(106, 168, 79, 0.055),
+        transparent 70%
+    );
+}
+
 a {
     color: inherit;
     text-decoration: none;
 }
+
 code,
 kbd,
 pre {
     font-family: var(--mono);
 }
+
 em {
     font-style: italic;
     color: var(--accent);
     font-family: var(--serif);
     font-weight: 500;
 }
+
 .dim {
     color: var(--ink-dim);
 }
+
 .underline-link {
     color: var(--accent);
     border-bottom: 1px dashed var(--accent-deep);
     padding-bottom: 1px;
+    transition: color 0.15s;
 }
 .underline-link:hover {
     color: var(--accent-2);
@@ -107,10 +136,12 @@ em {
     backdrop-filter: blur(12px);
     z-index: 50;
 }
+
 .nav-brand {
     display: flex;
     align-items: center;
 }
+
 .wordmark {
     display: inline-flex;
     align-items: center;
@@ -119,6 +150,7 @@ em {
     font-size: 15px;
     letter-spacing: -0.01em;
 }
+
 .wm-bracket {
     color: var(--accent);
     font-size: 12px;
@@ -132,7 +164,6 @@ em {
     color: var(--ink-dim2);
     font-size: 12px;
 }
-
 .wm-logo {
     display: flex;
     align-items: center;
@@ -148,11 +179,26 @@ em {
 }
 .nav-links a {
     color: var(--ink-dim);
-    transition: color 0.12s;
+    transition: color 0.15s;
+    position: relative;
+}
+.nav-links a::after {
+    content: "";
+    position: absolute;
+    bottom: -2px;
+    left: 0;
+    width: 0;
+    height: 1px;
+    background: var(--accent);
+    transition: width 0.25s ease;
 }
 .nav-links a:hover {
     color: var(--ink);
 }
+.nav-links a:hover::after {
+    width: 100%;
+}
+
 .nav-github {
     display: inline-flex;
     align-items: center;
@@ -180,7 +226,10 @@ em {
     color: var(--ink);
     background: transparent;
     cursor: pointer;
-    transition: all 0.12s;
+    transition:
+        border-color 0.15s,
+        color 0.15s,
+        box-shadow 0.25s;
 }
 .btn:hover {
     border-color: var(--accent);
@@ -189,6 +238,7 @@ em {
 .btn-k {
     font-size: 11px;
     color: var(--ink-dim);
+    transition: color 0.15s;
 }
 .btn:hover .btn-k {
     color: var(--accent);
@@ -203,6 +253,7 @@ em {
     background: var(--accent-2);
     border-color: var(--accent-2);
     color: #05080a;
+    box-shadow: 0 0 28px rgba(106, 168, 79, 0.35);
 }
 .btn-primary .btn-k {
     color: #05080a;
@@ -216,16 +267,45 @@ em {
     font-size: 14px;
 }
 
+/* ---------- Hero load animations ---------- */
+@keyframes heroFadeUp {
+    from {
+        opacity: 0;
+        transform: translateY(18px);
+    }
+    to {
+        opacity: 1;
+        transform: translateY(0);
+    }
+}
+
+.hero-eyebrow {
+    animation: heroFadeUp 0.55s cubic-bezier(0.22, 1, 0.36, 1) both 0.05s;
+}
+.hero-h1 {
+    animation: heroFadeUp 0.65s cubic-bezier(0.22, 1, 0.36, 1) both 0.18s;
+}
+.hero-sub {
+    animation: heroFadeUp 0.65s cubic-bezier(0.22, 1, 0.36, 1) both 0.32s;
+}
+.hero-cta {
+    animation: heroFadeUp 0.65s cubic-bezier(0.22, 1, 0.36, 1) both 0.46s;
+}
+.quickstart {
+    animation: heroFadeUp 0.65s cubic-bezier(0.22, 1, 0.36, 1) both 0.58s;
+}
+.hero-meta {
+    animation: heroFadeUp 0.65s cubic-bezier(0.22, 1, 0.36, 1) both 0.72s;
+}
+
 /* ---------- Hero ---------- */
 .hero {
-    display: grid;
-    grid-template-columns: 1fr 1.35fr;
-    gap: 56px;
-    padding: 72px 0 80px;
-    align-items: start;
+    padding: 96px 0 72px;
+    text-align: center;
 }
 .hero-copy {
-    padding-top: 24px;
+    max-width: 720px;
+    margin: 0 auto;
 }
 .hero-eyebrow {
     display: inline-flex;
@@ -253,37 +333,44 @@ em {
         opacity: 1;
     }
     50% {
-        opacity: 0.4;
+        opacity: 0.35;
     }
 }
 .hero-h1 {
     font-family: var(--sans);
-    font-size: 56px;
+    font-size: 62px;
     font-weight: 500;
     letter-spacing: -0.025em;
     line-height: 1.04;
     margin: 0 0 24px;
     text-wrap: pretty;
 }
+.hero-h1 em {
+    text-shadow: 0 0 56px rgba(106, 168, 79, 0.4);
+}
 .hero-sub {
     font-size: 17px;
     color: var(--ink-dim);
-    max-width: 46ch;
-    margin: 0 0 36px;
-    line-height: 1.55;
+    max-width: 50ch;
+    margin: 0 auto 36px;
+    line-height: 1.6;
 }
 .hero-cta {
     display: flex;
     gap: 12px;
-    margin-bottom: 44px;
+    justify-content: center;
+    margin-bottom: 36px;
+    flex-wrap: wrap;
 }
 .hero-meta {
-    display: grid;
-    grid-template-columns: repeat(3, auto);
+    display: flex;
     gap: 32px;
+    justify-content: center;
+    flex-wrap: wrap;
     font-family: var(--mono);
     font-size: 12px;
     color: var(--ink);
+    margin-top: 32px;
 }
 .hero-meta .dim {
     display: block;
@@ -294,406 +381,75 @@ em {
     letter-spacing: 0.08em;
 }
 
-.hero-demo {
-    position: relative;
-}
-.hero-demo-chrome {
-    display: flex;
-    align-items: center;
-    justify-content: space-between;
-    padding: 0 0 10px;
-    font-family: var(--mono);
-    font-size: 12px;
-}
-.hero-demo-label {
-    display: flex;
-    align-items: center;
-    gap: 8px;
-    color: var(--ink-dim);
-}
-.demo-swap {
-    background: transparent;
-    border: 1px solid var(--line);
-    color: var(--ink-dim);
-    padding: 3px 8px;
-    font-family: var(--mono);
-    font-size: 11px;
-    border-radius: 2px;
-    cursor: pointer;
-    transition: all 0.12s;
-}
-.demo-swap:hover {
-    color: var(--ink);
-    border-color: var(--line-2);
-}
-.demo-swap.on {
-    color: var(--accent);
-    border-color: var(--accent);
-}
-.hero-demo-hint {
-    color: var(--ink-dim);
-}
-.hero-demo-hint kbd {
-    display: inline-block;
-    margin: 0 2px;
-    padding: 1px 5px;
-    border: 1px solid var(--line-2);
-    border-radius: 2px;
-    font-size: 10px;
-    color: var(--ink);
-}
-
-@media (max-width: 960px) {
-    .hero {
-        grid-template-columns: 1fr;
-    }
-    .hero-h1 {
-        font-size: 40px;
-    }
-}
-
-/* ---------- TUI โ€” matches real Matcha layout ---------- */
-.tui-root {
-    position: relative;
-    background: var(--bg);
+/* ---------- QuickStart ---------- */
+.quickstart {
+    background: var(--panel-2);
     border: 1px solid var(--line-2);
     border-radius: 6px;
     overflow: hidden;
-    font-family: var(--mono);
-    font-size: 12px;
-    line-height: 1.5;
-    color: var(--ink);
-    outline: none;
+    margin: 0 auto 8px;
+    max-width: 480px;
+    text-align: left;
     box-shadow:
-        0 1px 0 rgba(255, 255, 255, 0.02) inset,
-        0 30px 80px -20px rgba(0, 0, 0, 0.7),
-        0 10px 30px -10px rgba(106, 168, 79, 0.08);
-    user-select: none;
+        0 20px 60px -20px rgba(0, 0, 0, 0.5),
+        0 8px 20px -8px rgba(106, 168, 79, 0.06);
     transition:
-        box-shadow 0.2s,
-        border-color 0.2s;
-    min-height: 520px;
+        border-color 0.25s,
+        box-shadow 0.25s;
 }
-.tui-root.is-focused {
-    border-color: var(--accent);
+.quickstart:hover {
+    border-color: var(--accent-deep);
     box-shadow:
-        0 0 0 1px var(--accent),
-        0 30px 80px -20px rgba(0, 0, 0, 0.7),
-        0 10px 30px -10px rgba(106, 168, 79, 0.2);
-}
-
-.tui-titlebar {
-    display: flex;
-    justify-content: space-between;
-    align-items: center;
-    padding: 8px 12px;
-    background: #02050a;
-    border-bottom: 1px solid var(--line);
-    font-size: 11px;
-    color: var(--ink-dim);
+        0 24px 70px -20px rgba(0, 0, 0, 0.55),
+        0 8px 28px -8px rgba(106, 168, 79, 0.16);
 }
-.tui-titlebar-left {
+.qs-bar {
     display: flex;
     align-items: center;
-    gap: 10px;
-}
-.tui-traffic {
-    display: inline-flex;
     gap: 6px;
+    padding: 9px 14px;
+    background: var(--bg-2);
+    border-bottom: 1px solid var(--line);
 }
-.tl {
-    width: 11px;
-    height: 11px;
+.qs-dot {
+    width: 10px;
+    height: 10px;
     border-radius: 50%;
-    display: inline-block;
 }
-.tl-r {
+.qs-r {
     background: #2a1414;
 }
-.tl-y {
+.qs-y {
     background: #2a2514;
 }
-.tl-g {
+.qs-g {
     background: #0f2a16;
 }
-.tui-title-text {
-    color: var(--ink-dim);
-}
-.tui-titlebar-right {
-    display: flex;
-    gap: 8px;
-}
-.tui-titlebar-right .sep {
-    color: var(--ink-dim2);
-}
-
-.tui-shell {
-    display: grid;
-    grid-template-columns: 160px 1fr;
-    min-height: 460px;
-}
-
-/* Sidebar โ€” folders */
-.tui-sidebar {
-    background: var(--bg-2);
-    padding: 10px 0;
-    border-right: 1px solid var(--line);
-    font-size: 12px;
-}
-.tui-sidebar-head {
-    padding: 2px 14px 10px;
-    color: var(--accent);
-    font-weight: 500;
-}
-.tui-folder {
-    padding: 2px 14px;
-    color: var(--ink-dim);
-    cursor: pointer;
-    white-space: nowrap;
-}
-.tui-folder:hover {
-    color: var(--ink);
-}
-.tui-folder.active {
-    background: var(--accent);
-    color: #05080a;
-    font-weight: 500;
-}
-
-/* Main pane */
-.tui-main {
-    position: relative;
-    padding: 0;
-    display: flex;
-    flex-direction: column;
-    overflow: hidden;
-}
-
-/* Account tabs row */
-.tui-acct-row {
-    display: flex;
-    gap: 22px;
-    padding: 8px 16px;
-    border-bottom: 1px solid var(--line);
-    font-size: 12px;
-}
-.tui-acct {
-    color: var(--ink-dim);
-    cursor: pointer;
-    padding: 2px 0;
-}
-.tui-acct:hover {
-    color: var(--ink);
-}
-.tui-acct.active {
-    color: var(--accent);
-    border-bottom: 1px solid var(--accent);
-    padding-bottom: 1px;
-}
-
-.tui-heading {
-    padding: 14px 16px 2px;
-    color: var(--accent);
-    font-weight: 500;
-    font-size: 12.5px;
-}
-.tui-subhead {
-    padding: 0 16px 8px;
-    color: var(--ink-dim);
-    font-size: 11.5px;
-}
-.tui-filter-chip {
-    color: var(--yellow);
-    margin-left: 10px;
-}
-
-.tui-list {
-    flex: 1;
-    padding: 2px 16px 40px;
-    position: relative;
-    z-index: 2;
-}
-
-.tui-row {
-    display: grid;
-    grid-template-columns: 18px 26px auto 14px auto 14px 1fr auto;
-    gap: 6px;
-    align-items: baseline;
-    padding: 1px 0;
-    cursor: pointer;
-    color: var(--ink-dim);
-    white-space: nowrap;
-    font-size: 12px;
-}
-.tui-row:hover {
-    color: var(--ink);
-}
-.tui-row.cur {
-    color: var(--accent);
-    background: rgba(106, 168, 79, 0.04);
-}
-.tui-row.visual.cur {
-    color: var(--yellow);
-}
-.tui-row-cursor {
-    color: var(--accent);
-    white-space: pre;
-    font-weight: 600;
-}
-.tui-row.visual .tui-row-cursor {
-    color: var(--yellow);
-}
-.tui-row-num {
-    color: var(--ink-dim2);
-}
-.tui-row-acct {
-    color: var(--blue);
-}
-.tui-row.cur .tui-row-acct {
-    color: var(--blue);
-}
-.tui-row-sep {
-    color: var(--ink-dim2);
-}
-.tui-row-from {
-    color: var(--ink);
-}
-.tui-row.cur .tui-row-from {
-    color: var(--accent);
-}
-.tui-row-subj {
-    overflow: hidden;
-    text-overflow: ellipsis;
-    color: var(--ink-dim);
-}
-.tui-row.cur .tui-row-subj {
-    color: var(--accent-2);
-}
-.tui-row-date {
-    color: var(--ink-dim2);
-    padding-left: 12px;
-}
-.tui-row.cur .tui-row-date {
-    color: var(--green-soft);
-}
-.tui-row-dots {
-    color: var(--ink-dim2);
-    padding-top: 12px;
-    letter-spacing: 4px;
-}
-.tui-row-empty {
-    color: var(--ink-dim2);
-    padding: 32px 0;
-    text-align: center;
-}
-
-/* Email view */
-.tui-email {
-    position: relative;
-    flex: 1;
-    padding: 0;
-    display: flex;
-    flex-direction: column;
-}
-.tui-email-head {
-    padding: 10px 16px;
-    color: var(--ink);
-    border-bottom: 1px solid var(--line);
-    font-size: 12px;
-}
-.tui-email-body {
-    padding: 16px;
-    flex: 1;
-    color: var(--ink);
-    position: relative;
-    z-index: 2;
-}
-.tui-email-line {
-    white-space: pre-wrap;
-}
-.tui-link {
-    color: var(--blue);
-    border-bottom: 1px solid var(--blue);
-    cursor: pointer;
-}
-
-/* Watermark */
-.tui-watermark {
-    position: absolute;
-    left: 0;
-    right: 0;
-    bottom: 30px;
-    width: 100%;
-    height: 60%;
-    color: var(--ink-dim2);
-    pointer-events: none;
-    z-index: 1;
-    opacity: 0.55;
-}
-
-/* Flash */
-.tui-flash {
-    position: absolute;
-    top: 52px;
-    right: 16px;
-    background: rgba(5, 8, 10, 0.94);
-    border: 1px solid var(--accent);
-    color: var(--accent);
-    padding: 4px 10px;
+.qs-bar-label {
+    font-family: var(--mono);
     font-size: 11px;
-    border-radius: 2px;
-    z-index: 6;
-}
-
-/* Status / legend line โ€” dim like the real TUI */
-.tui-status {
-    display: flex;
-    flex-wrap: wrap;
-    gap: 0 14px;
-    padding: 6px 16px;
-    border-top: 1px solid var(--line);
-    background: var(--bg-2);
     color: var(--ink-dim2);
-    font-size: 11px;
-    min-height: 26px;
-}
-.tui-status .seg {
-    color: var(--ink-dim);
-    white-space: nowrap;
-}
-.tui-status .seg.hi {
-    color: var(--yellow);
-}
-
-/* Filter command line */
-.tui-cmdline {
-    position: absolute;
-    bottom: 26px;
-    left: 0;
-    right: 0;
-    padding: 5px 12px;
-    background: #02050a;
-    color: var(--accent);
-    border-top: 1px solid var(--accent);
-    font-size: 12px;
-    display: flex;
-    align-items: center;
-    gap: 4px;
-    z-index: 5;
+    margin-left: 6px;
 }
-.tui-cmdline-prompt {
+.qs-code {
+    margin: 0;
+    padding: 20px 24px;
+    font-family: var(--mono);
+    font-size: 14px;
+    line-height: 2;
     color: var(--accent);
+    min-height: 80px;
 }
-.tui-cmdline-buf {
-    color: var(--ink);
+.qs-prompt {
+    color: var(--ink-dim2);
 }
-.tui-caret {
+.qs-caret {
     display: inline-block;
-    width: 7px;
-    height: 13px;
+    width: 8px;
+    height: 15px;
     background: var(--accent);
-    margin-left: 2px;
-    transform: translateY(2px);
+    vertical-align: text-bottom;
+    margin-left: 1px;
     animation: blink 1s step-end infinite;
 }
 @keyframes blink {
@@ -702,27 +458,17 @@ em {
     }
 }
 
-.tui-focus-hint {
-    position: absolute;
-    top: 48px;
-    right: 16px;
-    padding: 5px 10px;
-    background: rgba(5, 8, 10, 0.9);
-    border: 1px solid var(--line-2);
-    border-radius: 2px;
-    font-size: 11px;
-    color: var(--ink-dim);
-    pointer-events: none;
-    z-index: 3;
+/* ---------- Scroll reveal ---------- */
+.reveal {
+    opacity: 0;
+    transform: translateY(16px);
+    transition:
+        opacity 0.7s ease,
+        transform 0.7s ease;
 }
-.tui-focus-hint kbd {
-    display: inline-block;
-    margin: 0 1px;
-    padding: 0 4px;
-    border: 1px solid var(--line-2);
-    background: var(--panel);
-    border-radius: 2px;
-    color: var(--ink);
+.reveal.visible {
+    opacity: 1;
+    transform: translateY(0);
 }
 
 /* ---------- Sections ---------- */
@@ -776,12 +522,32 @@ em {
     margin-top: 24px;
 }
 .feature {
+    position: relative;
+    overflow: hidden;
     background: var(--bg);
     padding: 32px 28px;
     min-height: 240px;
     display: flex;
     flex-direction: column;
-    transition: background 0.2s;
+    transition:
+        background 0.2s,
+        opacity 0.7s ease,
+        transform 0.7s ease;
+}
+.feature::before {
+    content: "";
+    position: absolute;
+    top: 0;
+    left: 0;
+    right: 0;
+    height: 2px;
+    background: linear-gradient(90deg, var(--accent), var(--accent-2));
+    transform: scaleX(0);
+    transform-origin: left;
+    transition: transform 0.35s cubic-bezier(0.22, 1, 0.36, 1);
+}
+.feature:hover::before {
+    transform: scaleX(1);
 }
 .feature:hover {
     background: var(--bg-2);
@@ -822,66 +588,10 @@ em {
     color: var(--accent);
     border-radius: 2px;
     white-space: pre-wrap;
+    transition: border-color 0.2s;
 }
-@media (max-width: 960px) {
-    .feature-grid {
-        grid-template-columns: 1fr;
-    }
-    .section-head {
-        grid-template-columns: 1fr;
-        gap: 20px;
-    }
-    .section-h2 {
-        font-size: 32px;
-    }
-}
-
-/* Keybinds */
-.keybinds-grid {
-    display: grid;
-    grid-template-columns: repeat(3, 1fr);
-    gap: 40px 32px;
-    margin-top: 24px;
-    padding: 32px 0 80px;
-}
-.keybinds-col-head {
-    font-family: var(--mono);
-    font-size: 12px;
-    color: var(--accent);
-    margin-bottom: 16px;
-    letter-spacing: 0.04em;
-}
-.keybind-row {
-    display: flex;
-    align-items: baseline;
-    gap: 8px;
-    margin-bottom: 8px;
-    font-family: var(--mono);
-    font-size: 12.5px;
-}
-.keybind-k kbd {
-    display: inline-block;
-    padding: 1px 8px;
-    border: 1px solid var(--line-2);
-    background: var(--panel);
-    color: var(--ink);
-    border-radius: 2px;
-    font-size: 11.5px;
-    white-space: nowrap;
-}
-.keybind-dots {
-    color: var(--line-2);
-    flex: 1;
-    overflow: hidden;
-    white-space: nowrap;
-}
-.keybind-label {
-    color: var(--ink-dim);
-}
-@media (max-width: 960px) {
-    .keybinds-grid {
-        grid-template-columns: repeat(2, 1fr);
-    }
+.feature:hover .feature-mono {
+    border-color: var(--accent-deep);
 }
 
 /* Install */
@@ -891,6 +601,10 @@ em {
     border-radius: 4px;
     overflow: hidden;
     margin-bottom: 80px;
+    transition: border-color 0.25s;
+}
+.install-card:hover {
+    border-color: var(--line-2);
 }
 .install-tabs {
     display: flex;
@@ -909,7 +623,9 @@ em {
     font-size: 13px;
     cursor: pointer;
     border-bottom: 2px solid transparent;
-    transition: all 0.12s;
+    transition:
+        color 0.15s,
+        border-color 0.15s;
     white-space: nowrap;
 }
 .install-tab:hover {
@@ -990,11 +706,6 @@ em {
     justify-content: center;
     flex-wrap: wrap;
 }
-@media (max-width: 960px) {
-    .cta-h2 {
-        font-size: 36px;
-    }
-}
 
 /* Footer */
 .footer {
@@ -1031,6 +742,7 @@ em {
     font-size: 13px;
     padding: 4px 0;
     cursor: pointer;
+    transition: color 0.15s;
 }
 .footer-cols a:hover {
     color: var(--accent);
@@ -1057,70 +769,111 @@ em {
 .footer-meta .sep {
     color: var(--line-2);
 }
+
+/* ---------- Responsive ---------- */
 @media (max-width: 960px) {
+    .hero-h1 {
+        font-size: 46px;
+    }
+    .feature-grid {
+        grid-template-columns: repeat(2, 1fr);
+    }
+    .section-head {
+        grid-template-columns: 1fr;
+        gap: 20px;
+    }
+    .section-h2 {
+        font-size: 34px;
+    }
     .footer-top {
         grid-template-columns: 1fr;
     }
     .footer-cols {
         grid-template-columns: repeat(2, 1fr);
     }
+    .cta-h2 {
+        font-size: 40px;
+    }
 }
 
-/* Tweaks */
-.tweaks {
-    position: fixed;
-    bottom: 20px;
-    right: 20px;
-    background: var(--bg-2);
-    border: 1px solid var(--accent);
-    padding: 14px;
-    border-radius: 4px;
-    min-width: 220px;
-    font-family: var(--mono);
-    box-shadow:
-        0 20px 40px -10px rgba(0, 0, 0, 0.6),
-        0 0 0 4px rgba(106, 168, 79, 0.06);
-    z-index: 100;
-}
-.tweaks-head {
-    font-size: 11px;
-    color: var(--accent);
-    text-transform: uppercase;
-    letter-spacing: 0.12em;
-    margin-bottom: 12px;
-    padding-bottom: 8px;
-    border-bottom: 1px dashed var(--line-2);
-}
-.tweaks-sub {
-    font-size: 10px;
-    color: var(--ink-dim2);
-    text-transform: uppercase;
-    letter-spacing: 0.08em;
-    margin-bottom: 8px;
-}
-.tweaks-opt {
-    display: flex;
-    align-items: center;
-    gap: 8px;
-    width: 100%;
-    padding: 6px 8px;
-    background: transparent;
-    border: none;
-    color: var(--ink-dim);
-    font-family: var(--mono);
-    font-size: 12px;
-    text-align: left;
-    cursor: pointer;
-    border-radius: 2px;
-}
-.tweaks-opt:hover {
-    background: var(--panel);
-    color: var(--ink);
-}
-.tweaks-opt.on {
-    color: var(--accent);
+@media (max-width: 640px) {
+    .site {
+        padding: 0 20px;
+    }
+    .nav-links {
+        display: none;
+    }
+    .nav-right {
+        display: none;
+    }
+    .hero {
+        padding: 64px 0 48px;
+    }
+    .hero-h1 {
+        font-size: 36px;
+    }
+    .hero-sub {
+        font-size: 15px;
+    }
+    .hero-cta .btn-lg {
+        padding: 12px 18px;
+        font-size: 13px;
+    }
+    .quickstart {
+        max-width: 100%;
+    }
+    .qs-code {
+        font-size: 12px;
+        padding: 16px 18px;
+    }
+    .hero-meta {
+        flex-direction: column;
+        gap: 12px;
+        align-items: center;
+    }
+    .feature-grid {
+        grid-template-columns: 1fr;
+    }
+    .section-h2 {
+        font-size: 28px;
+    }
+    .cta-h2 {
+        font-size: 30px;
+    }
+    .install-code {
+        font-size: 13px;
+        padding: 24px 18px;
+    }
+    .footer-cols {
+        grid-template-columns: repeat(2, 1fr);
+    }
 }
-.tweaks-dot {
-    color: var(--accent);
-    width: 10px;
+
+/* ---------- Reduced motion ---------- */
+@media (prefers-reduced-motion: reduce) {
+    .hero-eyebrow,
+    .hero-h1,
+    .hero-sub,
+    .hero-cta,
+    .quickstart,
+    .hero-meta {
+        animation: none;
+    }
+    .reveal {
+        opacity: 1;
+        transform: none;
+        transition: none;
+    }
+    .feature {
+        opacity: 1;
+        transform: none;
+    }
+    .dot-live,
+    .qs-caret {
+        animation: none;
+        opacity: 1;
+    }
+    .feature::before {
+        transition: none;
+    }
 }