Files
freno-dev/src/routes/nook/index.tsx
Michael Freno 9508aee8e1 nook: show the full feature set on the landing page
The landing page sold eight features from around 0.6 and summed up the
rest in one line, while 0.6–0.10 shipped most of the island's modules. It
also said twelve agents (Muse made thirteen) and marked phone approvals
"coming soon" although the iPhone companion is in TestFlight beta.

Adds live demos for terminal replies, the volume mixer, Capture with its
Wings, and floating actions; a bento of module tiles behind a two-way
feature marquee; and an iPhone/Watch beta section whose request form
posts the same email-only report the app's Settings > Phone card sends.
The approval flow, remote sessions and setup, timer, temperatures and
Capture mocks are rebuilt from the app's own views, so the site shows
what the island actually draws.

Tests: report.test.ts pins the landing page's beta request against the
/report schema (red when the schema demands a machine snapshot); nook and
subdomain suites pass.

NOOK-334

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 23:07:10 -04:00

246 lines
9.7 KiB
TypeScript

import { PageHead } from "~/components/PageHead";
import { EdgeCacheHeaders } from "~/components/EdgeCacheHeaders";
import SubdomainHeader from "~/components/SubdomainHeader";
import FeatureCollage from "~/components/nook/FeatureCollage";
import FeatureBreakdowns from "~/components/nook/FeatureBreakdowns";
import IslandExtras from "~/components/nook/IslandExtras";
import PhoneBeta from "~/components/nook/PhoneBeta";
import Button from "~/components/ui/Button";
import Input from "~/components/ui/Input";
import { createSignal, Show } from "solid-js";
import { useDarkMode } from "~/context/darkMode";
import { useSite } from "~/context/SiteContext";
/*
* Downloads are logged and resolved server-side. `/api/the-nook/download`
* records the click in the analytics buffer, then 302s to the latest
* release dmg the appcast names. Pointing the button straight at the
* enclosure would skip the funnel's second step entirely.
*/
function downloadNook() {
window.location.href = "/api/the-nook/download";
}
export default function NookLanding() {
const site = useSite();
const { isDark } = useDarkMode();
const brandColor = () =>
isDark() ? (site().brandColorDark ?? site().brandColor) : site().brandColor;
const [resendEmail, setResendEmail] = createSignal("");
const [resendSending, setResendSending] = createSignal(false);
const [resendSent, setResendSent] = createSignal(false);
const [resendError, setResendError] = createSignal(false);
const resendLicense = async () => {
if (resendSending()) return;
const email = resendEmail().trim();
if (!email) return;
setResendSending(true);
setResendSent(false);
setResendError(false);
try {
const res = await fetch("/api/the-nook/resend-license", {
method: "POST",
headers: { "content-type": "application/json" },
body: JSON.stringify({ email })
});
if (!res.ok) throw new Error("resend failed");
setResendSent(true);
} catch {
setResendError(true);
} finally {
setResendSending(false);
}
};
return (
<>
<EdgeCacheHeaders maxAge={300} />
<PageHead
title="Agent control for your Mac"
ogTitle="The Nook — agent control for your Mac"
description="The Nook — a native macOS utility for coding-agent orchestration, fan and thermal control."
ogImage={`https://${site().domain}/nook/og.png`}
ogDescription="A one-time-purchase macOS app for coding-agent orchestration, fan control, and thermal insight."
/>
<SubdomainHeader />
{/* ── Hero ─────────────────────────────────────────────────────── */}
{/* -mt-14 pulls the hero under the sticky header; pt-14 re-clears
content, so the gradient fills the full viewport incl. the bar */}
<div class="relative -mt-14 flex min-h-svh flex-col overflow-hidden pt-14">
<div
class="fixed inset-0 z-0"
style={{
background: isDark()
? "radial-gradient(ellipse at top, #123945 0%, #0b0b10 70%)"
: "radial-gradient(ellipse at top, #d7eef5 0%, #f5f5f5 70%)"
}}
/>
<div class="relative z-10 flex flex-1 flex-col items-center justify-center px-4 py-12 text-center sm:py-24">
<img
src="/nook/icon.png"
alt="The Nook App Icon"
height={128}
width={128}
class="mb-4 h-24 w-24 rounded-[22%] object-cover object-center shadow-2xl sm:mb-6 sm:h-32 sm:w-32"
/>
<div
class="text-text/90 mb-6 rounded-2xl px-5 py-3 text-sm font-semibold tracking-wide backdrop-blur-sm"
style={{
border: "1px solid var(--color-overlay0)",
background: "var(--color-surface0)"
}}
>
The Nook
</div>
<h1 class="text-text mb-4 text-4xl font-bold tracking-tight sm:text-5xl">
Your Mac and Agents, under control
</h1>
<p class="text-subtext0 mb-2 max-w-xl text-xl">
Agent orchestration, fan and thermal control in a beautiful native
UI.
</p>
<p class="text-subtext1 mb-8 text-sm">
macOS 14+ · 14-day free trial · up to 3 devices
</p>
<div class="flex flex-col items-center gap-4 sm:flex-row sm:space-x-4">
<Button
variant="download"
size="lg"
color={brandColor()}
onClick={downloadNook}
>
Download trial
</Button>
<Button
variant="secondary"
size="lg"
onClick={() => (window.location.href = "/checkout")}
>
Buy
</Button>
</div>
<div class="mt-3 flex flex-col items-center gap-1.5">
<span class="border-overlay0 bg-surface0 text-subtext0 rounded-full border px-2.5 py-0.5 text-[11px] font-semibold tracking-wide">
Beta pricing · 33% off
</span>
<p class="text-subtext1 text-xs">
<span class="text-subtext0 line-through">$15</span>{" "}
<span class="text-text font-bold">$10</span> one-time
</p>
</div>
</div>
</div>
{/* ── Feature collage ─────────────────────────────────────────── */}
<FeatureCollage />
{/* ── Feature breakdowns: the features in action ─────────────── */}
<section class="bg-base relative z-20 px-4 py-16 md:px-8">
<div class="mx-auto max-w-5xl">
<p class="text-subtext1 mb-2 text-center text-xs font-semibold tracking-[0.18em] uppercase">
In action
</p>
<h2 class="text-text mb-16 text-center text-3xl font-bold">
Built for the way you actually run agents
</h2>
<FeatureBreakdowns />
</div>
</section>
{/* ── Everything else: marquee + live module bento ───────────── */}
<IslandExtras />
{/* ── iPhone + Watch companion, TestFlight beta ──────────────── */}
<PhoneBeta brandColor={brandColor()} />
{/* ── CTA ──────────────────────────────────────────────────────── */}
<section class="bg-surface0 relative z-20 px-4 py-20 md:px-8">
<div class="mx-auto max-w-4xl text-center">
<h2 class="text-text mb-4 text-3xl font-bold">
Try it free for 14 days
</h2>
<p class="text-subtext0 mx-auto mb-10 max-w-2xl leading-relaxed">
Orchestrate your coding agents, take control of your fans and
battery, give every app its own volume, capture your screen, and
keep your day in the notch. When the trial ends, unlock everything
with a single one-time payment — no subscription, ever.
</p>
<div class="flex flex-col items-center justify-center gap-4 sm:flex-row sm:space-x-4">
<Button
variant="download"
size="lg"
color={brandColor()}
onClick={downloadNook}
>
Download trial
</Button>
<a
class="text-subtext1 my-auto text-sm underline decoration-dotted hover:opacity-80"
href="/checkout"
>
Buy a license → $10{" "}
<span class="text-subtext0 line-through">$15</span>
</a>
</div>
<p class="text-subtext1 mt-6 text-xs">
Beta pricing — full price will be $15. One license covers up to 3 of
your own Macs.
</p>
</div>
</section>
{/* ── Resend license ─────────────────────────────────────────── */}
<section class="bg-base relative z-20 px-4 py-16 md:px-8">
<div class="border-overlay0 bg-surface0 mx-auto max-w-md rounded-xl border p-8">
<h2 class="text-text mb-2 text-center text-2xl font-bold">
Lost your license?
</h2>
<p class="text-subtext0 mb-6 text-center text-sm">
Enter the email you purchased with and we'll resend your key.
</p>
<form
class="flex flex-col gap-3"
onSubmit={(e) => {
e.preventDefault();
resendLicense();
}}
>
<Input
type="email"
required
label="Email"
placeholder="you@example.com"
disabled={resendSending()}
value={resendEmail()}
onInput={(e) => setResendEmail(e.currentTarget.value)}
/>
<Button
type="submit"
variant="primary"
color={brandColor()}
loading={resendSending()}
>
Resend license
</Button>
</form>
<Show when={resendSent()}>
<p class="text-subtext1 mt-4 text-center text-sm">
Check your inbox — if a license is registered to that email, your
key is on its way.
</p>
</Show>
<Show when={resendError()}>
<p class="mt-4 text-center text-sm text-red-500">
Something went wrong. Please try again.
</p>
</Show>
</div>
</section>
</>
);
}