Merge pull request #1257 from useautumn/feat/site/logged_in_hint

feat: 🎸 show dashboard on landing if logged in
This commit is contained in:
Charlie Lamb
2026-04-16 16:18:13 +01:00
committed by GitHub
11 changed files with 2589 additions and 515 deletions

View File

@@ -1,5 +1,6 @@
{
"lockfileVersion": 1,
"configVersion": 0,
"workspaces": {
"": {
"name": "autumndev",
@@ -10,7 +11,7 @@
"lottie-react": "^2.4.1",
"matter-js": "^0.20.0",
"motion": "^12.38.0",
"next": "16.2.1",
"next": "16.2.4",
"next-mdx-remote": "^6.0.0",
"ngrok": "^5.0.0-beta.2",
"react": "19.2.4",
@@ -163,25 +164,25 @@
"@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@0.2.12", "", { "dependencies": { "@emnapi/core": "^1.4.3", "@emnapi/runtime": "^1.4.3", "@tybys/wasm-util": "^0.10.0" } }, "sha512-ZVWUcfwY4E/yPitQJl481FjFo3K22D6qF0DuFH6Y/nbnE11GY5uguDxZMGXPQ8WQ0128MXQD7TnfHyK4oWoIJQ=="],
"@next/env": ["@next/env@16.2.1", "", {}, "sha512-n8P/HCkIWW+gVal2Z8XqXJ6aB3J0tuM29OcHpCsobWlChH/SITBs1DFBk/HajgrwDkqqBXPbuUuzgDvUekREPg=="],
"@next/env": ["@next/env@16.2.4", "", {}, "sha512-dKkkOzOSwFYe5RX6y26fZgkSpVAlIOJKQHIiydQcrWH6y/97+RceSOAdjZ14Qa3zLduVUy0TXcn+EiM6t4rPgw=="],
"@next/eslint-plugin-next": ["@next/eslint-plugin-next@16.2.1", "", { "dependencies": { "fast-glob": "3.3.1" } }, "sha512-r0epZGo24eT4g08jJlg2OEryBphXqO8aL18oajoTKLzHJ6jVr6P6FI58DLMug04MwD3j8Fj0YK0slyzneKVyzA=="],
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.2.1", "", { "os": "darwin", "cpu": "arm64" }, "sha512-BwZ8w8YTaSEr2HIuXLMLxIdElNMPvY9fLqb20LX9A9OMGtJilhHLbCL3ggyd0TwjmMcTxi0XXt+ur1vWUoxj2Q=="],
"@next/swc-darwin-arm64": ["@next/swc-darwin-arm64@16.2.4", "", { "os": "darwin", "cpu": "arm64" }, "sha512-OXTFFox5EKN1Ym08vfrz+OXxmCcEjT4SFMbNRsWZE99dMqt2Kcusl5MqPXcW232RYkMLQTy0hqgAMEsfEd/l2A=="],
"@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.2.1", "", { "os": "darwin", "cpu": "x64" }, "sha512-/vrcE6iQSJq3uL3VGVHiXeaKbn8Es10DGTGRJnRZlkNQQk3kaNtAJg8Y6xuAlrx/6INKVjkfi5rY0iEXorZ6uA=="],
"@next/swc-darwin-x64": ["@next/swc-darwin-x64@16.2.4", "", { "os": "darwin", "cpu": "x64" }, "sha512-XhpVnUfmYWvD3YrXu55XdcAkQtOnvaI6wtQa8fuF5fGoKoxIUZ0kWPtcOfqJEWngFF/lOS9l3+O9CcownhiQxQ=="],
"@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-uLn+0BK+C31LTVbQ/QU+UaVrV0rRSJQ8RfniQAHPghDdgE+SlroYqcmFnO5iNjNfVWCyKZHYrs3Nl0mUzWxbBw=="],
"@next/swc-linux-arm64-gnu": ["@next/swc-linux-arm64-gnu@16.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-Mx/tjlNA3G8kg14QvuGAJ4xBwPk1tUHq56JxZ8CXnZwz1Etz714soCEzGQQzVMz4bEnGPowzkV6Xrp6wAkEWOQ=="],
"@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.2.1", "", { "os": "linux", "cpu": "arm64" }, "sha512-ssKq6iMRnHdnycGp9hCuGnXJZ0YPr4/wNwrfE5DbmvEcgl9+yv97/Kq3TPVDfYome1SW5geciLB9aiEqKXQjlQ=="],
"@next/swc-linux-arm64-musl": ["@next/swc-linux-arm64-musl@16.2.4", "", { "os": "linux", "cpu": "arm64" }, "sha512-iVMMp14514u7Nup2umQS03nT/bN9HurK8ufylC3FZNykrwjtx7V1A7+4kvhbDSCeonTVqV3Txnv0Lu+m2oDXNg=="],
"@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-HQm7SrHRELJ30T1TSmT706IWovFFSRGxfgUkyWJZF/RKBMdbdRWJuFrcpDdE5vy9UXjFOx6L3mRdqH04Mmx0hg=="],
"@next/swc-linux-x64-gnu": ["@next/swc-linux-x64-gnu@16.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-EZOvm1aQWgnI/N/xcWOlnS3RQBk0VtVav5Zo7n4p0A7UKyTDx047k8opDbXgBpHl4CulRqRfbw3QrX2w5UOXMQ=="],
"@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.2.1", "", { "os": "linux", "cpu": "x64" }, "sha512-aV2iUaC/5HGEpbBkE+4B8aHIudoOy5DYekAKOMSHoIYQ66y/wIVeaRx8MS2ZMdxe/HIXlMho4ubdZs/J8441Tg=="],
"@next/swc-linux-x64-musl": ["@next/swc-linux-x64-musl@16.2.4", "", { "os": "linux", "cpu": "x64" }, "sha512-h9FxsngCm9cTBf71AR4fGznDEDx1hS7+kSEiIRjq5kO1oXWm07DxVGZjCvk0SGx7TSjlUqhI8oOyz7NfwAdPoA=="],
"@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.2.1", "", { "os": "win32", "cpu": "arm64" }, "sha512-IXdNgiDHaSk0ZUJ+xp0OQTdTgnpx1RCfRTalhn3cjOP+IddTMINwA7DXZrwTmGDO8SUr5q2hdP/du4DcrB1GxA=="],
"@next/swc-win32-arm64-msvc": ["@next/swc-win32-arm64-msvc@16.2.4", "", { "os": "win32", "cpu": "arm64" }, "sha512-3NdJV5OXMSOeJYijX+bjaLge3mJBlh4ybydbT4GFoB/2hAojWHtMhl3CYlYoMrjPuodp0nzFVi4Tj2+WaMg+Ow=="],
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.2.1", "", { "os": "win32", "cpu": "x64" }, "sha512-qvU+3a39Hay+ieIztkGSbF7+mccbbg1Tk25hc4JDylf8IHjYmY/Zm64Qq1602yPyQqvie+vf5T/uPwNxDNIoeg=="],
"@next/swc-win32-x64-msvc": ["@next/swc-win32-x64-msvc@16.2.4", "", { "os": "win32", "cpu": "x64" }, "sha512-kMVGgsqhO5YTYODD9IPGGhA6iprWidQckK3LmPeW08PIFENRmgfb4MjXHO+p//d+ts2rpjvK5gXWzXSMrPl9cw=="],
"@nodelib/fs.scandir": ["@nodelib/fs.scandir@2.1.5", "", { "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g=="],
@@ -913,7 +914,7 @@
"natural-compare": ["natural-compare@1.4.0", "", {}, "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw=="],
"next": ["next@16.2.1", "", { "dependencies": { "@next/env": "16.2.1", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.2.1", "@next/swc-darwin-x64": "16.2.1", "@next/swc-linux-arm64-gnu": "16.2.1", "@next/swc-linux-arm64-musl": "16.2.1", "@next/swc-linux-x64-gnu": "16.2.1", "@next/swc-linux-x64-musl": "16.2.1", "@next/swc-win32-arm64-msvc": "16.2.1", "@next/swc-win32-x64-msvc": "16.2.1", "sharp": "^0.34.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": "dist/bin/next" }, "sha512-VaChzNL7o9rbfdt60HUj8tev4m6d7iC1igAy157526+cJlXOQu5LzsBXNT+xaJnTP/k+utSX5vMv7m0G+zKH+Q=="],
"next": ["next@16.2.4", "", { "dependencies": { "@next/env": "16.2.4", "@swc/helpers": "0.5.15", "baseline-browser-mapping": "^2.9.19", "caniuse-lite": "^1.0.30001579", "postcss": "8.4.31", "styled-jsx": "5.1.6" }, "optionalDependencies": { "@next/swc-darwin-arm64": "16.2.4", "@next/swc-darwin-x64": "16.2.4", "@next/swc-linux-arm64-gnu": "16.2.4", "@next/swc-linux-arm64-musl": "16.2.4", "@next/swc-linux-x64-gnu": "16.2.4", "@next/swc-linux-x64-musl": "16.2.4", "@next/swc-win32-arm64-msvc": "16.2.4", "@next/swc-win32-x64-msvc": "16.2.4", "sharp": "^0.34.5" }, "peerDependencies": { "@opentelemetry/api": "^1.1.0", "@playwright/test": "^1.51.1", "babel-plugin-react-compiler": "*", "react": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "react-dom": "^18.2.0 || 19.0.0-rc-de68d2f4-20241204 || ^19.0.0", "sass": "^1.3.0" }, "optionalPeers": ["@opentelemetry/api", "@playwright/test", "babel-plugin-react-compiler", "sass"], "bin": { "next": "dist/bin/next" } }, "sha512-kPvz56wF5frc+FxlHI5qnklCzbq53HTwORaWBGdT0vNoKh1Aya9XC8aPauH4NJxqtzbWsS5mAbctm4cr+EkQ2Q=="],
"next-mdx-remote": ["next-mdx-remote@6.0.0", "", { "dependencies": { "@babel/code-frame": "^7.23.5", "@mdx-js/mdx": "^3.0.1", "@mdx-js/react": "^3.0.1", "unist-util-remove": "^4.0.0", "unist-util-visit": "^5.1.0", "vfile": "^6.0.1", "vfile-matter": "^5.0.0" }, "peerDependencies": { "react": ">=16" } }, "sha512-cJEpEZlgD6xGjB4jL8BnI8FaYdN9BzZM4NwadPe1YQr7pqoWjg9EBCMv3nXBkuHqMRfv2y33SzUsuyNh9LFAQQ=="],

View File

@@ -1,284 +1,307 @@
"use client";
import { CTALines, IconCTADocs, IconCTAStart } from "@/app/constant";
import { useGSAP } from "@gsap/react";
import gsap from "gsap";
import { motion } from "motion/react";
import Image from "next/image";
import Link from "next/link";
import { useEffect, useRef, useState } from "react";
import { CTALines, IconCTADocs, IconCTAStart } from "@/app/constant";
import AutumnConfig from "./autumn-config";
// import dynamic from "next/dynamic";
// const AutumnConfig = dynamic(() => import("./autumn-config"), { ssr: false });
const BADGE_TEXT = "// 100% open source";
const getLoggedInHintCookie = () => {
if (typeof window === "undefined") return null;
return (
document.cookie
.split("; ")
.find((row) => row.startsWith("logged_in_hint="))
?.split("=")[1] === "1"
);
};
export default function Hero() {
const containerRef = useRef(null);
const heroTlRef = useRef(null);
const [displayedText, setDisplayedText] = useState("");
const containerRef = useRef(null);
const heroTlRef = useRef(null);
const [displayedText, setDisplayedText] = useState("");
const [isLoggedIn, setIsLoggedIn] = useState(false);
// Badge typewriter
useEffect(() => {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*0123456789";
let intervalId;
let timeoutId;
// Read the hint cookie after mount to avoid SSR/CSR hydration mismatch.
useEffect(() => {
setIsLoggedIn(getLoggedInHintCookie() === true);
}, []);
const startTypewriter = () => {
timeoutId = setTimeout(() => {
let iteration = 0;
intervalId = setInterval(() => {
setDisplayedText(
BADGE_TEXT.split("")
.map((char, index) => {
if (char === " ") return " ";
if (index < Math.floor(iteration)) return char;
if (index === Math.floor(iteration))
return chars[Math.floor(Math.random() * chars.length)];
return "";
})
.join(""),
);
// Badge typewriter
useEffect(() => {
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ!@#$%^&*0123456789";
let intervalId;
let timeoutId;
iteration += 0.4;
const startTypewriter = () => {
timeoutId = setTimeout(() => {
let iteration = 0;
intervalId = setInterval(() => {
setDisplayedText(
BADGE_TEXT.split("")
.map((char, index) => {
if (char === " ") return " ";
if (index < Math.floor(iteration)) return char;
if (index === Math.floor(iteration))
return chars[Math.floor(Math.random() * chars.length)];
return "";
})
.join(""),
);
if (iteration >= BADGE_TEXT.length) {
clearInterval(intervalId);
setDisplayedText(BADGE_TEXT);
}
}, 30);
}, 150);
};
iteration += 0.4;
window.addEventListener("preloader:complete", startTypewriter, {
once: true,
});
return () => {
window.removeEventListener("preloader:complete", startTypewriter);
clearTimeout(timeoutId);
clearInterval(intervalId);
};
}, []);
if (iteration >= BADGE_TEXT.length) {
clearInterval(intervalId);
setDisplayedText(BADGE_TEXT);
}
}, 30);
}, 150);
};
useEffect(() => {
const handler = () => heroTlRef.current?.play();
window.addEventListener("preloader:complete", handler, { once: true });
return () => window.removeEventListener("preloader:complete", handler);
}, []);
window.addEventListener("preloader:complete", startTypewriter, {
once: true,
});
return () => {
window.removeEventListener("preloader:complete", startTypewriter);
clearTimeout(timeoutId);
clearInterval(intervalId);
};
}, []);
useGSAP(
() => {
gsap.set(".hero-root", { opacity: 0 });
useEffect(() => {
const handler = () => heroTlRef.current?.play();
window.addEventListener("preloader:complete", handler, { once: true });
return () => window.removeEventListener("preloader:complete", handler);
}, []);
gsap.set(".hero-bg", {
opacity: 0,
filter: "blur(6px) brightness(1)",
scale: 0.97,
transformOrigin: "center top",
});
useGSAP(
() => {
gsap.set(".hero-root", { opacity: 0 });
gsap.set(".hero-reveal", {
opacity: 0,
y: 25,
filter: "blur(12px)",
scale: 0.96,
transformOrigin: "center bottom",
});
gsap.set(".hero-bg", {
opacity: 0,
filter: "blur(6px) brightness(1)",
scale: 0.97,
transformOrigin: "center top",
});
gsap.set(".hero-cta", { opacity: 0, scale: 0.95 });
gsap.set(".hero-reveal", {
opacity: 0,
y: 25,
filter: "blur(12px)",
scale: 0.96,
transformOrigin: "center bottom",
});
const tl = gsap.timeline({
paused: true,
defaults: { overwrite: "auto" },
});
heroTlRef.current = tl;
gsap.set(".hero-cta", { opacity: 0, scale: 0.95 });
tl.to(".hero-root", { opacity: 1, duration: 0.3, ease: "none" })
const tl = gsap.timeline({
paused: true,
defaults: { overwrite: "auto" },
});
heroTlRef.current = tl;
.to(".hero-bg", {
opacity: 1,
filter: "blur(0px) brightness(1)",
scale: 1,
duration: 0.4,
ease: "power2.out",
})
tl.to(".hero-root", { opacity: 1, duration: 0.3, ease: "none" })
.to(".hero-bg", {
filter: "blur(0px) brightness(1.6)",
duration: 0.125,
ease: "power2.in",
})
.to(".hero-bg", {
opacity: 1,
filter: "blur(0px) brightness(1)",
scale: 1,
duration: 0.4,
ease: "power2.out",
})
.to(".hero-bg", {
filter: "blur(0px) brightness(1)",
duration: 0.125,
ease: "power2.out",
})
.to(".hero-bg", {
filter: "blur(0px) brightness(1.6)",
duration: 0.125,
ease: "power2.in",
})
.to(
".hero-reveal",
{
opacity: 1,
y: 0,
filter: "blur(0px)",
scale: 1,
duration: 1.1,
stagger: 0.1,
ease: "power3.out",
},
"-=0.2",
)
.to(".hero-bg", {
filter: "blur(0px) brightness(1)",
duration: 0.125,
ease: "power2.out",
})
.to(
".hero-cta",
{
opacity: 1,
scale: 1,
duration: 0.3,
stagger: 0.06,
ease: "back.out(1.5)",
},
"-=0.1",
);
},
{ scope: containerRef },
);
.to(
".hero-reveal",
{
opacity: 1,
y: 0,
filter: "blur(0px)",
scale: 1,
duration: 1.1,
stagger: 0.1,
ease: "power3.out",
},
"-=0.2",
)
return (
<div ref={containerRef}>
<div className="relative hero-root opacity-0 flex flex-col items-stretch pb-0 md:pb-12 mb-0 bg-[#0F0F0F]">
<div className="flex justify-between">
<div className="flex flex-col gap-6 px-4 xl:px-22.75 py-8 bg-[#0F0F0F] mt-26">
<h4 className="hero-reveal relative uppercase font-mono tracking-[-2%] text-[12px] md:text-sm leading-sm text-white md:text-[#FFFFFF99] bg-[#2c2c2d] w-fit p-2 min-h-[30px] md:min-h-[36px] flex items-center">
<span className="invisible select-none" aria-hidden="true">
{BADGE_TEXT}
</span>
<span className="absolute inset-0 flex items-center p-2">
{displayedText}
</span>
</h4>
<div className="flex flex-col gap-6 w-full px-0 lg:px-0">
<h1 className="hero-reveal text-[44px] md:text-[56px] w-full max-w-sm sm:max-w-[480px] md:max-w-xl leading-[44px] tracking-[-5%] md:leading-14 font-sans">
<span className="text-[#FFFFFF99] font-normal">
The drop-in billing layer for
</span>{" "}
<span className="text-white block md:inline">AI startups</span>
</h1>
<p className="hero-reveal tracking-[-2%] w-full max-w-xs sm:max-w-[480px] md:max-w-xl text-[#FFFFFF99] md:text-[16px] text-[14px] font-light leading-5 font-sans">
Stop rebuilding usage limits, credit ledgers and payment
logic.{" "}
<span className="text-white font-light">
Autumn is your customer database
</span>{" "}
that scales from your first user to your largest contract.
</p>
</div>
.to(
".hero-cta",
{
opacity: 1,
scale: 1,
duration: 0.3,
stagger: 0.06,
ease: "back.out(1.5)",
},
"-=0.1",
);
},
{ scope: containerRef },
);
</div>
<div className="hero-reveal relative w-[50vw] max-w-[720px] p-16 py-0 mx-auto hidden xl:block">
<div className="absolute inset-0 z-0 pointer-events-none">
<video
src="/images/pricing-models/pricingbg.webm"
autoPlay
loop
muted
playsInline
className="absolute inset-0 w-full h-full object-cover mix-blend-screen opacity-100"
/>
</div>
<div className="relative z-10 translate-y-16 w-full flex justify-center">
<AutumnConfig initialDelay={200} awaitEvent="preloader:complete" />
</div>
</div>
</div>
<div className="border-t border-[#292929]" />
<div className="flex flex-nowrap items-center xl:px-22.75 px-4 bg-[#0F0F0F] w-full overflow-hidden">
{/* Primary CTA */}
<div className="hero-cta w-full md:w-fit md:flex-shrink-0">
<Link href={"https://app.useautumn.com/sign-in"}>
<motion.div
initial="initial"
whileHover="hover"
whileTap="hover"
className="relative"
>
{/* Adjusted px-3 for mobile, md:px-4 for desktop */}
<div className="relative overflow-hidden flex items-center gap-1.5 md:gap-2.5 cursor-pointer justify-between py-2 px-3 md:px-4 md:py-3.5 md:w-50 font-sans bg-[#9564ff] hover:bg-[#7D46F4] transition-colors duration-300">
<CTALines />
<span className="relative z-10 tracking-[-2%] uppercase md:normal-case text-white font-medium text-[12px] md:text-base whitespace-nowrap">
Start for free
</span>
<span className="relative z-10 scale-95 md:scale-100">
<IconCTAStart />
</span>
</div>
</motion.div>
</Link>
</div>
return (
<div ref={containerRef}>
<div className="relative hero-root opacity-0 flex flex-col items-stretch pb-0 md:pb-12 mb-0 bg-[#0F0F0F]">
<div className="flex justify-between">
<div className="flex flex-col gap-6 px-4 xl:px-22.75 py-8 bg-[#0F0F0F] mt-26">
<h4 className="hero-reveal relative uppercase font-mono tracking-[-2%] text-[12px] md:text-sm leading-sm text-white md:text-[#FFFFFF99] bg-[#2c2c2d] w-fit p-2 min-h-[30px] md:min-h-[36px] flex items-center">
<span className="invisible select-none" aria-hidden="true">
{BADGE_TEXT}
</span>
<span className="absolute inset-0 flex items-center p-2">
{displayedText}
</span>
</h4>
<div className="flex flex-col gap-6 w-full px-0 lg:px-0">
<h1 className="hero-reveal text-[44px] md:text-[56px] w-full max-w-sm sm:max-w-[480px] md:max-w-xl leading-[44px] tracking-[-5%] md:leading-14 font-sans">
<span className="text-[#FFFFFF99] font-normal">
The drop-in billing layer for
</span>{" "}
<span className="text-white block md:inline">AI startups</span>
</h1>
<p className="hero-reveal tracking-[-2%] w-full max-w-xs sm:max-w-[480px] md:max-w-xl text-[#FFFFFF99] md:text-[16px] text-[14px] font-light leading-5 font-sans">
Stop rebuilding usage limits, credit ledgers and payment logic.{" "}
<span className="text-white font-light">
Autumn is your customer database
</span>{" "}
that scales from your first user to your largest contract.
</p>
</div>
</div>
<div className="hero-reveal relative w-[50vw] max-w-[720px] p-16 py-0 mx-auto hidden xl:block">
<div className="absolute inset-0 z-0 pointer-events-none">
<video
src="/images/pricing-models/pricingbg.webm"
autoPlay
loop
muted
playsInline
className="absolute inset-0 w-full h-full object-cover mix-blend-screen opacity-100"
/>
</div>
<div className="relative z-10 translate-y-16 w-full flex justify-center">
<AutumnConfig
initialDelay={200}
awaitEvent="preloader:complete"
/>
</div>
</div>
</div>
<div className="border-t border-[#292929]" />
<div className="flex flex-nowrap items-center xl:px-22.75 px-4 bg-[#0F0F0F] w-full overflow-hidden">
{/* Primary CTA */}
<div className="hero-cta w-full md:w-fit md:flex-shrink-0">
<Link
href={
isLoggedIn
? "https://app.useautumn.com"
: "https://app.useautumn.com/sign-in"
}
>
<motion.div
initial="initial"
whileHover="hover"
whileTap="hover"
className="relative"
>
{/* Adjusted px-3 for mobile, md:px-4 for desktop */}
<div className="relative overflow-hidden flex items-center gap-1.5 md:gap-2.5 cursor-pointer justify-between py-2 px-3 md:px-4 md:py-3.5 md:w-50 font-sans bg-[#9564ff] hover:bg-[#7D46F4] transition-colors duration-300">
<CTALines />
<span className="relative z-10 tracking-[-2%] uppercase md:normal-case text-white font-medium text-[12px] md:text-base whitespace-nowrap">
{isLoggedIn ? "Dashboard" : "Start for free"}
</span>
<span className="relative z-10 scale-95 md:scale-100">
<IconCTAStart />
</span>
</div>
</motion.div>
</Link>
</div>
{/* Secondary CTA */}
<div className="hero-cta w-full md:w-fit md:flex-shrink-0">
<Link href={"https://cal.com/ayrod"}>
<motion.div
initial="initial"
whileHover="hover"
whileTap="hover"
className="relative"
>
<div className="relative overflow-hidden flex items-center gap-1.5 md:gap-2.5 border-r border-[#292929] text-white cursor-pointer justify-between py-2 px-3 md:px-4 md:py-3.5 md:w-50 font-sans bg-[#0F0F0F] hover:bg-[#FFFFFF1F] transition-colors duration-300">
<CTALines />
<span className="relative z-10 tracking-[-2%] text-[12px] uppercase md:normal-case md:text-[16px] whitespace-nowrap">
Book a call
</span>
<span className="relative z-10 scale-100">
<IconCTADocs />
</span>
</div>
</motion.div>
</Link>
</div>
{/* Secondary CTA */}
<div className="hero-cta w-full md:w-fit md:flex-shrink-0">
<Link href={"https://cal.com/ayrod"}>
<motion.div
initial="initial"
whileHover="hover"
whileTap="hover"
className="relative"
>
<div className="relative overflow-hidden flex items-center gap-1.5 md:gap-2.5 border-r border-[#292929] text-white cursor-pointer justify-between py-2 px-3 md:px-4 md:py-3.5 md:w-50 font-sans bg-[#0F0F0F] hover:bg-[#FFFFFF1F] transition-colors duration-300">
<CTALines />
<span className="relative z-10 tracking-[-2%] text-[12px] uppercase md:normal-case md:text-[16px] whitespace-nowrap">
Book a call
</span>
<span className="relative z-10 scale-100">
<IconCTADocs />
</span>
</div>
</motion.div>
</Link>
</div>
<div className="hero-cta hidden md:flex flex-nowrap gap-2 md:gap-3 ml-2 md:ml-3 h-10.5 md:h-12.5 flex-1">
<div className="border-r border-[#292929] h-full hidden md:block" />
<div className="border-r border-[#292929] h-full hidden md:block" />
<div className="border-r border-[#292929] h-full hidden md:block" />
<div className="hero-cta hidden md:flex flex-nowrap gap-2 md:gap-3 ml-2 md:ml-3 h-10.5 md:h-12.5 flex-1">
<div className="border-r border-[#292929] h-full hidden md:block" />
<div className="border-r border-[#292929] h-full hidden md:block" />
<div className="border-r border-[#292929] h-full hidden md:block" />
<div className="border-r border-[#292929] h-full hidden md:block" />
<div className="border-r border-[#292929] h-full hidden md:block" />
<div className="border-r border-[#292929] h-full hidden md:block" />
<div className="border-r border-[#292929] h-full hidden md:block" />
<div className="border-r border-[#292929] h-full hidden md:block" />
<div className="border-r border-[#292929] h-full hidden md:block" />
</div>
</div>
<div className="border-b border-[#292929]" />
{/* MOBILE VIEW*/}
<div className="relative block xl:hidden w-full overflow-hidden bg-[#0F0F0F] mt-12">
<div className="relative overflow-hidden w-full p-7 flex items-center justify-center">
<video
src="/images/pricing-models/pricingbg.webm"
autoPlay
loop
muted
playsInline
className="hero-bg absolute inset-0 w-full h-full object-cover mix-blend-screen opacity-100"
/>
<div className="border-r border-[#292929] h-full hidden md:block" />
<div className="border-r border-[#292929] h-full hidden md:block" />
<div className="border-r border-[#292929] h-full hidden md:block" />
<div className="border-r border-[#292929] h-full hidden md:block" />
<div className="border-r border-[#292929] h-full hidden md:block" />
<div className="border-r border-[#292929] h-full hidden md:block" />
</div>
</div>
<div className="border-b border-[#292929]" />
{/* MOBILE VIEW*/}
<div className="relative block xl:hidden w-full overflow-hidden bg-[#0F0F0F] mt-12">
<div className="relative overflow-hidden w-full p-7 flex items-center justify-center">
<video
src="/images/pricing-models/pricingbg.webm"
autoPlay
loop
muted
playsInline
className="hero-bg absolute inset-0 w-full h-full object-cover mix-blend-screen opacity-100"
/>
<div className="hero-reveal relative z-10 w-[96%] sm:w-[90%] max-w-[520px] flex justify-center items-center">
{/* <AutumnConfig lines={16} initialDelay={1000} awaitEvent="preloader:complete" /> */}
<Image
src={"/images/hero/autumn_mobile.svg"}
width={1600}
height={1600}
alt="xyz"
priority
/>
</div>
</div>
</div>
</div>
</div>
);
<div className="hero-reveal relative z-10 w-[96%] sm:w-[90%] max-w-[520px] flex justify-center items-center">
{/* <AutumnConfig lines={16} initialDelay={1000} awaitEvent="preloader:complete" /> */}
<Image
src={"/images/hero/autumn_mobile.svg"}
width={1600}
height={1600}
alt="xyz"
priority
/>
</div>
</div>
</div>
</div>
</div>
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,32 +1,32 @@
{
"name": "autumndev",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev -p 3004",
"build": "next build",
"start": "next start",
"lint": "eslint"
},
"dependencies": {
"@gsap/react": "^2.1.2",
"gray-matter": "^4.0.3",
"gsap": "^3.14.2",
"lottie-react": "^2.4.1",
"matter-js": "^0.20.0",
"motion": "^12.38.0",
"next": "16.2.1",
"next-mdx-remote": "^6.0.0",
"ngrok": "^5.0.0-beta.2",
"react": "19.2.4",
"react-dom": "19.2.4",
"react-syntax-highlighter": "^16.1.1"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.2.2",
"@tailwindcss/typography": "^0.5.19",
"eslint": "^9",
"eslint-config-next": "16.2.1",
"tailwindcss": "^4.2.2"
}
"name": "@autumn/website",
"version": "0.1.0",
"private": true,
"scripts": {
"dev": "next dev -p 3004",
"build": "next build",
"start": "next start",
"lint": "eslint"
},
"dependencies": {
"@gsap/react": "^2.1.2",
"gray-matter": "^4.0.3",
"gsap": "^3.14.2",
"lottie-react": "^2.4.1",
"matter-js": "^0.20.0",
"motion": "^12.38.0",
"next": "16.2.4",
"next-mdx-remote": "^6.0.0",
"ngrok": "^5.0.0-beta.2",
"react": "19.2.4",
"react-dom": "19.2.4",
"react-syntax-highlighter": "^16.1.1"
},
"devDependencies": {
"@tailwindcss/postcss": "^4.2.2",
"@tailwindcss/typography": "^0.5.19",
"eslint": "^9",
"eslint-config-next": "16.2.1",
"tailwindcss": "^4.2.2"
}
}

671
bun.lock

File diff suppressed because it is too large Load Diff

View File

@@ -20,6 +20,7 @@ COPY packages/stripe-sync/package.json ./packages/stripe-sync/package.json
COPY packages/sdk/package.json ./packages/sdk/package.json
COPY apps/sdk-test/package.json ./apps/sdk-test/package.json
COPY apps/docs/package.json ./apps/docs/package.json
COPY apps/website/package.json ./apps/website/package.json
# Install dependencies
RUN bun install --ignore-scripts

View File

@@ -9,6 +9,7 @@
"scripts",
"apps/checkout",
"apps/docs",
"apps/website",
"apps/sdk-test",
"packages/atmn",
"packages/atmn-tests",
@@ -98,7 +99,7 @@
"svix:push": "infisical run --env=dev -- bun packages/openapi/scripts/svixPush.ts",
"svix:push:prod": "infisical run --env=prod -- bun packages/openapi/scripts/svixPush.ts",
"docs:pull": "bun -F @autumn/docs pull",
"site": "cd apps/website && npm install && npm run dev",
"site": "cd apps/website && bun dev && cd ../..",
"docs": "bun -F @autumn/docs dev",
"docs:build": "bun -F @autumn/docs build",
"ts": "bun -F @autumn/server ts && bun -F autumn-js ts && bun -F @autumn/openapi ts && bun -F atmn ts && bun -F checkout ts",

View File

@@ -19,6 +19,8 @@ import { sendInvitationEmail } from "@/internal/emails/sendInvitationEmail.js";
import { sendOnboardingEmail } from "@/internal/emails/sendOnboardingEmail.js";
import sendOTPEmail from "@/internal/emails/sendOTPEmail.js";
import { afterOrgCreated } from "./authUtils/afterOrgCreated.js";
import { afterSessionCreated } from "./authUtils/afterSessionCreated.js";
import { afterSessionDeleted } from "./authUtils/afterSessionDeleted.js";
import { beforeSessionCreated } from "./authUtils/beforeSessionCreated.js";
import { ADMIN_USER_IDs } from "./constants.js";
@@ -48,6 +50,14 @@ export const auth = betterAuth({
session: {
create: {
before: beforeSessionCreated,
after: (session, context) => {
return afterSessionCreated(session, context);
},
},
delete: {
after: (session, context) => {
return afterSessionDeleted(session, context);
},
},
},
},

View File

@@ -0,0 +1,28 @@
import type { GenericEndpointContext } from "@better-auth/core";
import type { BetterAuthOptions, Session } from "better-auth";
export const afterSessionCreated = async (
session: Session,
context: GenericEndpointContext<BetterAuthOptions> | null,
) => {
try {
if (!context) return;
// In dev, the Vite dashboard sets this cookie client-side (localhost
// doesn't support cross-port cookie sharing via Set-Cookie headers).
if (process.env.NODE_ENV !== "production") return;
// Set a non-httpOnly hint cookie on the root domain so the landing
// page (useautumn.com) can detect that the user is logged in on app.useautumn.com.
context.setCookie("logged_in_hint", "1", {
domain: ".useautumn.com",
path: "/",
maxAge: 60 * 60 * 24 * 7, // 7 days
secure: true,
sameSite: "none",
httpOnly: false,
});
} catch (_) {
console.error("Error running afterSessionCreated", { error: _ });
}
};

View File

@@ -0,0 +1,29 @@
import type { GenericEndpointContext } from "@better-auth/core";
import type { BetterAuthOptions, Session } from "better-auth";
export const afterSessionDeleted = async (
_session: Session,
context: GenericEndpointContext<BetterAuthOptions> | null,
) => {
try {
if (!context) return;
// Dev parity: the cookie is only set server-side in production, so
// we don't need to clear it here in other environments.
if (process.env.NODE_ENV !== "production") return;
// Clear the landing-page hint cookie so signed-out users stop seeing
// the "Dashboard" CTA on useautumn.com. Attributes must match the
// original setCookie call so the browser overwrites the cookie.
context.setCookie("logged_in_hint", "", {
domain: ".useautumn.com",
path: "/",
maxAge: 0,
secure: true,
sameSite: "none",
httpOnly: false,
});
} catch (_) {
console.error("Error running afterSessionDeleted", { error: _ });
}
};

View File

@@ -51,6 +51,17 @@ export default function App() {
Sentry.setTags({
org_id: data.session.activeOrganizationId ?? "unknown_org",
});
// Set a non-httpOnly hint cookie so the landing page can detect login state.
// In production this is handled server-side on .useautumn.com domain.
if (window.location.hostname === "localhost") {
if (data?.user) {
document.cookie =
"logged_in_hint=1; path=/; max-age=604800; SameSite=Lax";
} else {
document.cookie = "logged_in_hint=; path=/; max-age=0; SameSite=Lax";
}
}
}
}, [data]);
return (