+
+
+
Our manifesto
+
+
+
+ We believe in the power of notetaking, not notetakers. Meetings should be moments of presence, not
+ passive attendance. If you are not adding value, your time is better spent elsewhere for you and your
+ team.
+
+
+ Hyprnote exists to preserve what makes us human: conversations that spark ideas, collaborations that
+ move work forward. We build tools that amplify human agency, not replace it. No ghost bots. No silent
+ note lurkers. Just people, thinking together.
+
+
We stand with those who value real connection and purposeful collaboration.
+
-
-
-
+
+
+
+
+
+
+
+
Hyprnote
+
John Jeong, Yujong Lee
+
+
+
-
- Where conversations stay yours
-
-
- Start using Hyprnote today and bring clarity to your back-to-back meetings
-
-
-
-
-
-
+
-
-
+
+
+ );
+}
+
+function CTASection() {
+ return (
+
+
+
+
+
+
+ Where conversations stay yours
+
+
+ Start using Hyprnote today and bring clarity to your back-to-back meetings
+
+
+
+
+
+
+
);
}
diff --git a/apps/web/src/routes/_view/pricing.tsx b/apps/web/src/routes/_view/pricing.tsx
index 0b5e6d8bc..bfa978194 100644
--- a/apps/web/src/routes/_view/pricing.tsx
+++ b/apps/web/src/routes/_view/pricing.tsx
@@ -1,9 +1,332 @@
+import { cn } from "@hypr/utils";
+
+import { Icon } from "@iconify-icon/react";
import { createFileRoute } from "@tanstack/react-router";
export const Route = createFileRoute("/_view/pricing")({
component: Component,
});
+interface PricingPlan {
+ name: string;
+ price: { monthly: number; yearly: number } | null;
+ description: string;
+ popular?: boolean;
+ features: Array<{
+ label: string;
+ included: boolean | "partial";
+ tooltip?: string;
+ comingSoon?: boolean;
+ }>;
+}
+
+const pricingPlans: PricingPlan[] = [
+ {
+ name: "Free",
+ price: null,
+ description: "Fully functional with your own API keys. Perfect for individuals who want complete control.",
+ features: [
+ { label: "Local Transcription", included: true },
+ { label: "Speaker Identification", included: true },
+ { label: "Bring Your Own Key (STT & LLM)", included: true },
+ { label: "Basic Sharing (Copy, PDF)", included: true },
+ { label: "All Data Local", included: true },
+ { label: "Integrations", included: "partial", tooltip: "Available with free account signup" },
+ { label: "Templates & Chat", included: false },
+ { label: "Cloud Services (STT & LLM)", included: false },
+ { label: "Cloud Sync", included: false },
+ { label: "Shareable Links", included: false },
+ { label: "Unified Billing & Access Management", included: false },
+ ],
+ },
+ {
+ name: "Pro",
+ price: {
+ monthly: 35,
+ yearly: 295,
+ },
+ description: "No API keys needed. Get cloud services, advanced sharing, and team features out of the box.",
+ popular: true,
+ features: [
+ { label: "Everything in Free", included: true },
+ { label: "Integrations", included: true },
+ { label: "Templates & Chat", included: true },
+ { label: "Cloud Services (STT & LLM)", included: true },
+ { label: "Cloud Sync", included: true, tooltip: "Select which notes to sync", comingSoon: true },
+ {
+ label: "Shareable Links",
+ included: true,
+ tooltip: "DocSend-like: view tracking, expiration, revocation",
+ comingSoon: true,
+ },
+ { label: "Unified Billing & Access Management", included: true, comingSoon: true },
+ ],
+ },
+];
+
+const infoCards = [
+ {
+ icon: "mdi:account-check-outline",
+ title: "Speaker identification is included in all plans",
+ },
+ {
+ icon: "mdi:link-variant",
+ title: "Shareable link includes DocsSend-like controls",
+ description: "viewer tracking, expiration, revocation",
+ },
+ {
+ icon: "mdi:key-outline",
+ title: "BYOK lets you connect your own LLM",
+ description: "for full data control",
+ },
+];
+
function Component() {
- return
Hello "/_view/_layout/pricing"!
;
+ return (
+
+
+
+ );
+}
+
+function HeroSection() {
+ return (
+
+
+
+ Hyprnote Pricing
+
+
+ Choose the plan that fits your needs. Start for free, upgrade when you need cloud features.
+
+
+
+ );
+}
+
+function PricingCardsSection() {
+ return (
+
+
+ {pricingPlans.map((plan) =>
)}
+
+
+ );
+}
+
+function PricingCard({ plan }: { plan: PricingPlan }) {
+ return (
+
+ {plan.popular && (
+
+ Most Popular
+
+ )}
+
+
+
+
{plan.name}
+
{plan.description}
+
+ {plan.price
+ ? (
+
+
+
+ ${plan.price.monthly}
+
+ /seat/month
+
+
+ or ${plan.price.yearly}/seat/year (save 30%)
+
+
+ )
+ :
Free
}
+
+
+
+ {plan.features.map((feature, idx) => (
+
+
+
+
+
+ {feature.label}
+
+ {feature.comingSoon && (
+
+ Coming Soon
+
+ )}
+
+ {feature.tooltip && (
+
+ {feature.tooltip}
+
+ )}
+
+
+ ))}
+
+
+
+ {plan.price ? "Get Started" : "Download for Free"}
+
+
+
+ );
+}
+
+function InfoCardsSection() {
+ return (
+
+
+
+ {infoCards.map((card, idx) => (
+
+
+
+ {card.title}
+
+ {card.description &&
{card.description}
}
+
+ ))}
+
+
+
+ );
+}
+
+function FAQSection() {
+ const faqs = [
+ {
+ question: "What does 'Local only' transcription mean?",
+ answer:
+ "All transcription happens on your device. Your audio never leaves your computer, ensuring complete privacy.",
+ },
+ {
+ question: "What is BYOK (Bring Your Own Key)?",
+ answer:
+ "BYOK allows you to connect your own LLM provider (like OpenAI, Anthropic, or self-hosted models) for AI features while maintaining full control over your data.",
+ },
+ {
+ question: "What's included in shareable links?",
+ answer:
+ "Pro users get DocsSend-like controls: track who views your notes, set expiration dates, and revoke access anytime.",
+ },
+ {
+ question: "Is there a team discount?",
+ answer: "Yes! Teams on annual plans save 30%. Contact us for larger team pricing.",
+ },
+ ];
+
+ return (
+
+
+
+ Frequently Asked Questions
+
+
+ {faqs.map((faq, idx) => (
+
+
+ {faq.question}
+
+
{faq.answer}
+
+ ))}
+
+
+
+ );
+}
+
+function CTASection() {
+ return (
+
+
+
+
+
+
+ Ready to get started?
+
+
+ Download Hyprnote for free and upgrade when you need cloud features
+
+
+
+
+ );
}
diff --git a/apps/web/src/routes/_view/route.tsx b/apps/web/src/routes/_view/route.tsx
index ea03f2a7c..c92914144 100644
--- a/apps/web/src/routes/_view/route.tsx
+++ b/apps/web/src/routes/_view/route.tsx
@@ -7,7 +7,7 @@ export const Route = createFileRoute("/_view")({
function Component() {
return (
-
+
@@ -20,43 +20,44 @@ function Component() {
function Header() {
return (
-
+
- Hyprnote
+
Docs
Blog
Pricing
-
-
- Download
-
+ Join waitlist
+
@@ -65,37 +66,12 @@ function Header() {
);
}
-function HeaderUser() {
- const { user } = Route.useLoaderData();
-
- if (user) {
- return (
-
- Account
-
- );
- }
-
- return (
-
- Get Started
-
- );
-}
-
function Footer() {
const currentYear = new Date().getFullYear();
return (
-
+
© {currentYear} Fastrepl, Inc.
diff --git a/apps/web/src/routes/auth.tsx b/apps/web/src/routes/auth.tsx
index 54bbd4ee5..876ac9e3f 100644
--- a/apps/web/src/routes/auth.tsx
+++ b/apps/web/src/routes/auth.tsx
@@ -1,3 +1,5 @@
+import { cn } from "@hypr/utils";
+
import { Icon } from "@iconify-icon/react";
import { useForm } from "@tanstack/react-form";
import { useMutation } from "@tanstack/react-query";
@@ -5,7 +7,6 @@ import { createFileRoute } from "@tanstack/react-router";
import { z } from "zod";
import { doAuth } from "@/functions/auth";
-import { cn } from "@hypr/utils";
const validateSearch = z.object({
flow: z.enum(["desktop", "web"]).default("web"),
@@ -37,14 +38,12 @@ function Container({ children }: { children: React.ReactNode }) {
return (
-
-
- {children}
-
+
+ {children}
);
@@ -62,7 +61,7 @@ function Header() {
])}
>
oauthMutation.mutate(provider)}
diff --git a/apps/web/src/routes/cal.tsx b/apps/web/src/routes/cal.tsx
new file mode 100644
index 000000000..045c34784
--- /dev/null
+++ b/apps/web/src/routes/cal.tsx
@@ -0,0 +1,114 @@
+import { cn } from "@hypr/utils";
+import { createFileRoute } from "@tanstack/react-router";
+
+export const Route = createFileRoute("/cal")({
+ component: Component,
+});
+
+function Component() {
+ return (
+
+
+
+
+
+ );
+}
+
+function Container({ children }: { children: React.ReactNode }) {
+ return (
+
+ );
+}
+
+function Header() {
+ return (
+ <>
+
+ Skip the Waitlist
+
+
+ Book a quick 20-minute chat with the founders and get guaranteed early access to Hyprnote
+
+ >
+ );
+}
+
+function BookingButton() {
+ return (
+
+ );
+}
+
+function FAQ() {
+ return (
+
+ );
+}
+
+function FAQItem({ question, answer }: { question: string; answer: string }) {
+ return (
+
+
{question}
+
{answer}
+
+ );
+}
diff --git a/apps/web/src/styles.css b/apps/web/src/styles.css
index 82d9eb194..77c34ab58 100644
--- a/apps/web/src/styles.css
+++ b/apps/web/src/styles.css
@@ -4,4 +4,16 @@
@theme {
--font-sans: "Inter", system-ui, -apple-system, sans-serif;
--font-serif: "Lora", Georgia, serif;
+ --breakpoint-laptop: 72rem;
+}
+
+@layer utilities {
+ .scrollbar-none {
+ -ms-overflow-style: none;
+ scrollbar-width: none;
+ }
+
+ .scrollbar-none::-webkit-scrollbar {
+ display: none;
+ }
}
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 0c6cf649f..8396a3dad 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -322,6 +322,9 @@ importers:
'@iconify-icon/react':
specifier: ^3.0.1
version: 3.0.1(react@19.2.0)
+ '@mux/mux-player-react':
+ specifier: ^3.7.0
+ version: 3.7.0(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)
'@nangohq/frontend':
specifier: ^0.69.5
version: 0.69.5
@@ -2018,6 +2021,31 @@ packages:
resolution: {integrity: sha512-6rqTdFt67AAAzln3NOKsXRmv5ZzPkgbfaebKBqUbts7vK1GZudqnrun5a8d3M/h955cam9RHZ6Jb4Y1XhnmFPg==}
engines: {node: '>=18'}
+ '@mux/mux-data-google-ima@0.2.8':
+ resolution: {integrity: sha512-0ZEkHdcZ6bS8QtcjFcoJeZxJTpX7qRIledf4q1trMWPznugvtajCjCM2kieK/pzkZj1JM6liDRFs1PJSfVUs2A==}
+
+ '@mux/mux-player-react@3.7.0':
+ resolution: {integrity: sha512-STBpvrJpZrPnlX7Ww2X6r4E663wynSHdqk3c8T37Z8Pvz706oCqaylCHln7T7GCf9R5YOKk70rT3OuI5M5ezMA==}
+ peerDependencies:
+ '@types/react': ^17.0.0 || ^17.0.0-0 || ^18 || ^18.0.0-0 || ^19 || ^19.0.0-0
+ '@types/react-dom': '*'
+ react: ^17.0.2 || ^17.0.0-0 || ^18 || ^18.0.0-0 || ^19 || ^19.0.0-0
+ react-dom: ^17.0.2 || ^17.0.2-0 || ^18 || ^18.0.0-0 || ^19 || ^19.0.0-0
+ peerDependenciesMeta:
+ '@types/react':
+ optional: true
+ '@types/react-dom':
+ optional: true
+
+ '@mux/mux-player@3.7.0':
+ resolution: {integrity: sha512-p4mlxhcfUP3xXpvZWkD5y8aJKrKc3Ck+W9VValqedxHOkYSI0lJ0AQy9kCjx0HfMj5M33CKIQvon9vJQoA1dIw==}
+
+ '@mux/mux-video@0.27.1':
+ resolution: {integrity: sha512-pUunuxbwcoO5Z2QtxkDlfLJLphpWEwlBa9k9r2542q7hwUw8q54BlxEjGfNkyv6sQFeYLpTvxKmDbI+lAIfKbg==}
+
+ '@mux/playback-core@0.31.1':
+ resolution: {integrity: sha512-fd+zVyzuDDiOJjS+4k7gP3sMJIZmAXsxGtDVHFeTCOz2gM6oqJbxNKsZrEealkVr32gKyrNLQR6mVEjJZN6Olw==}
+
'@nangohq/frontend@0.69.5':
resolution: {integrity: sha512-Df0GYFHoCIkd3R73UmbkfdOC7eQFaGj+MNXjjLuLjHPFZVNaEKxUg2wTbYs63mOofcY7uiTg762nD16ofDPQDQ==}
@@ -4718,9 +4746,17 @@ packages:
caniuse-lite@1.0.30001751:
resolution: {integrity: sha512-A0QJhug0Ly64Ii3eIqHu5X51ebln3k4yTUkY1j8drqpWHVreg/VLijN48cZ1bYPiqOQuqpkIKnzr/Ul8V+p6Cw==}
+ castable-video@1.1.11:
+ resolution: {integrity: sha512-LCRTK6oe7SB1SiUQFzZCo6D6gcEzijqBTVIuj3smKpQdesXM18QTbCVqWgh9MfOeQgTx/i9ji5jGcdqNPeWg2g==}
+
ccount@2.0.1:
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
+ ce-la-react@0.3.1:
+ resolution: {integrity: sha512-g0YwpZDPIwTwFumGTzNHcgJA6VhFfFCJkSNdUdC04br2UfU+56JDrJrJva3FZ7MToB4NDHAFBiPE/PZdNl1mQA==}
+ peerDependencies:
+ react: '>=17.0.0'
+
chai@5.3.3:
resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==}
engines: {node: '>=18'}
@@ -4998,6 +5034,9 @@ packages:
csstype@3.1.3:
resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==}
+ custom-media-element@1.4.5:
+ resolution: {integrity: sha512-cjrsQufETwxjvwZbYbKBCJNvmQ2++G9AvT45zDi7NXL9k2PdVcs2h0jQz96J6G4TMKRCcEsoJ+QTgQD00Igtjw==}
+
cytoscape-cose-bilkent@4.1.0:
resolution: {integrity: sha512-wgQlVIUJF13Quxiv5e1gstZ08rnZj2XaLHGoFMYXz7SkNfCDOOteKBE6SYRfA9WxxI/iBc3ajfDoc6hb/MRAHQ==}
peerDependencies:
@@ -5958,6 +5997,9 @@ packages:
hastscript@9.0.1:
resolution: {integrity: sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==}
+ hls.js@1.6.13:
+ resolution: {integrity: sha512-hNEzjZNHf5bFrUNvdS4/1RjIanuJ6szpWNfTaX5I6WfGynWXGT7K/YQLYtemSvFExzeMdgdE4SsyVLJbd5PcZA==}
+
hoist-non-react-statics@3.3.2:
resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==}
@@ -6582,6 +6624,15 @@ packages:
peerDependencies:
esbuild: 0.25.11
+ media-chrome@4.13.1:
+ resolution: {integrity: sha512-jPPwYrFkM4ky27/xNYEeyRPOBC7qvru4Oydy7vQHMHplXLQJmjtcauhlLPvG0O5kkYFEaOBXv5zGYes/UxOoVw==}
+
+ media-chrome@4.14.0:
+ resolution: {integrity: sha512-IEdFb4blyF15vLvQzLIn6USJBv7Kf2ne+TfLQKBYI5Z0f9VEBVZz5MKy4Uhi0iA9lStl2S9ENIujJRuJIa5OiA==}
+
+ media-tracks@0.3.3:
+ resolution: {integrity: sha512-9P2FuUHnZZ3iji+2RQk7Zkh5AmZTnOG5fODACnjhCVveX1McY3jmCRHofIEI+yTBqplz7LXy48c7fQ3Uigp88w==}
+
media-typer@1.1.0:
resolution: {integrity: sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==}
engines: {node: '>= 0.8'}
@@ -6858,6 +6909,12 @@ packages:
mute-stream@0.0.8:
resolution: {integrity: sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==}
+ mux-embed@5.13.0:
+ resolution: {integrity: sha512-voN+WCsA8S0lRTeu14v7WaWnQMr4f1D6rKzT7Bq1ZtETjnOaIG9EreG7p/SJrHLHHd1WTt0RYtTr4ZqJICcL6Q==}
+
+ mux-embed@5.9.0:
+ resolution: {integrity: sha512-wmunL3uoPhma/tWy8PrDPZkvJpXvSFBwbD3KkC4PG8Ztjfb1X3hRJwGUAQyRz7z99b/ovLm2UTTitrkvStjH4w==}
+
mz@2.7.0:
resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==}
@@ -7166,6 +7223,9 @@ packages:
pkg-types@2.3.0:
resolution: {integrity: sha512-SIqCzDRg0s9npO5XQ3tNZioRY1uK06lA41ynBC1YmFTmnY6FjUjVt6s4LoADmwoig1qqD0oK8h1p/8mlMx8Oig==}
+ player.style@0.2.0:
+ resolution: {integrity: sha512-Ngoaz49TClptMr8HDA2IFmjT3Iq6R27QEUH/C+On33L59RSF3dCLefBYB1Au2RDZQJ6oVFpc1sXaPVpp7fEzzA==}
+
pluralize@8.0.0:
resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==}
engines: {node: '>=4'}
@@ -10493,6 +10553,43 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ '@mux/mux-data-google-ima@0.2.8':
+ dependencies:
+ mux-embed: 5.9.0
+
+ '@mux/mux-player-react@3.7.0(@types/react-dom@19.2.2(@types/react@19.2.2))(@types/react@19.2.2)(react-dom@19.2.0(react@19.2.0))(react@19.2.0)':
+ dependencies:
+ '@mux/mux-player': 3.7.0(react@19.2.0)
+ '@mux/playback-core': 0.31.1
+ prop-types: 15.8.1
+ react: 19.2.0
+ react-dom: 19.2.0(react@19.2.0)
+ optionalDependencies:
+ '@types/react': 19.2.2
+ '@types/react-dom': 19.2.2(@types/react@19.2.2)
+
+ '@mux/mux-player@3.7.0(react@19.2.0)':
+ dependencies:
+ '@mux/mux-video': 0.27.1
+ '@mux/playback-core': 0.31.1
+ media-chrome: 4.14.0(react@19.2.0)
+ player.style: 0.2.0(react@19.2.0)
+ transitivePeerDependencies:
+ - react
+
+ '@mux/mux-video@0.27.1':
+ dependencies:
+ '@mux/mux-data-google-ima': 0.2.8
+ '@mux/playback-core': 0.31.1
+ castable-video: 1.1.11
+ custom-media-element: 1.4.5
+ media-tracks: 0.3.3
+
+ '@mux/playback-core@0.31.1':
+ dependencies:
+ hls.js: 1.6.13
+ mux-embed: 5.13.0
+
'@nangohq/frontend@0.69.5':
dependencies:
'@nangohq/types': 0.69.5
@@ -13816,8 +13913,16 @@ snapshots:
caniuse-lite@1.0.30001751: {}
+ castable-video@1.1.11:
+ dependencies:
+ custom-media-element: 1.4.5
+
ccount@2.0.1: {}
+ ce-la-react@0.3.1(react@19.2.0):
+ dependencies:
+ react: 19.2.0
+
chai@5.3.3:
dependencies:
assertion-error: 2.0.1
@@ -14126,6 +14231,8 @@ snapshots:
csstype@3.1.3: {}
+ custom-media-element@1.4.5: {}
+
cytoscape-cose-bilkent@4.1.0(cytoscape@3.33.1):
dependencies:
cose-base: 1.0.3
@@ -15170,6 +15277,8 @@ snapshots:
property-information: 7.1.0
space-separated-tokens: 2.0.2
+ hls.js@1.6.13: {}
+
hoist-non-react-statics@3.3.2:
dependencies:
react-is: 16.13.1
@@ -15907,6 +16016,20 @@ snapshots:
transitivePeerDependencies:
- supports-color
+ media-chrome@4.13.1(react@19.2.0):
+ dependencies:
+ ce-la-react: 0.3.1(react@19.2.0)
+ transitivePeerDependencies:
+ - react
+
+ media-chrome@4.14.0(react@19.2.0):
+ dependencies:
+ ce-la-react: 0.3.1(react@19.2.0)
+ transitivePeerDependencies:
+ - react
+
+ media-tracks@0.3.3: {}
+
media-typer@1.1.0: {}
merge-descriptors@2.0.0: {}
@@ -16361,6 +16484,10 @@ snapshots:
mute-stream@0.0.8: {}
+ mux-embed@5.13.0: {}
+
+ mux-embed@5.9.0: {}
+
mz@2.7.0:
dependencies:
any-promise: 1.3.0
@@ -16694,6 +16821,12 @@ snapshots:
exsolve: 1.0.7
pathe: 2.0.3
+ player.style@0.2.0(react@19.2.0):
+ dependencies:
+ media-chrome: 4.13.1(react@19.2.0)
+ transitivePeerDependencies:
+ - react
+
pluralize@8.0.0: {}
points-on-curve@0.2.0: {}