forked from Mike/freno-dev
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>
246 lines
9.7 KiB
TypeScript
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>
|
|
</>
|
|
);
|
|
}
|