From 9c2dcc005178ad935c650bc7898152030ee6fdbd Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Fri, 5 Dec 2025 07:34:37 +0000 Subject: [PATCH 1/8] feat(web): add dropdown menus for Compare and Platforms in footer Resources column - Add hover-based dropdown for VS comparison pages (Otter, Granola, Fireflies, etc.) - Add hover-based dropdown for platform-specific download pages (macOS, Windows, Linux) - Include accessibility improvements: ARIA attributes, keyboard support, touch/click support - Follow existing header dropdown pattern for consistency Co-Authored-By: john@hyprnote.com --- apps/web/src/components/footer.tsx | 132 ++++++++++++++++++++++++++++- 1 file changed, 131 insertions(+), 1 deletion(-) diff --git a/apps/web/src/components/footer.tsx b/apps/web/src/components/footer.tsx index 11138b72e3..77c719206a 100644 --- a/apps/web/src/components/footer.tsx +++ b/apps/web/src/components/footer.tsx @@ -1,5 +1,27 @@ import { Link, useRouterState } from "@tanstack/react-router"; -import { ExternalLinkIcon, MailIcon } from "lucide-react"; +import { + ChevronDown, + ChevronUp, + ExternalLinkIcon, + MailIcon, +} from "lucide-react"; +import { useState } from "react"; + +const vsList = [ + { slug: "otter", name: "Otter.ai" }, + { slug: "granola", name: "Granola" }, + { slug: "fireflies", name: "Fireflies" }, + { slug: "fathom", name: "Fathom" }, + { slug: "notion", name: "Notion" }, + { slug: "obsidian", name: "Obsidian" }, +]; + +const platformsList = [ + { to: "/download/apple-silicon", label: "macOS (Apple Silicon)" }, + { to: "/download/apple-intel", label: "macOS (Intel)" }, + { to: "/download/windows", label: "Windows" }, + { to: "/download/linux", label: "Linux" }, +]; function getMaxWidthClass(pathname: string): string { const isBlogOrDocs = @@ -146,6 +168,9 @@ function ProductLinks() { } function ResourcesLinks() { + const [isCompareOpen, setIsCompareOpen] = useState(false); + const [isPlatformsOpen, setIsPlatformsOpen] = useState(false); + return (

@@ -180,6 +205,111 @@ function ResourcesLinks() { +
  • setIsCompareOpen(true)} + onMouseLeave={() => setIsCompareOpen(false)} + onBlur={(e) => { + if (!e.currentTarget.contains(e.relatedTarget)) { + setIsCompareOpen(false); + } + }} + > + + {isCompareOpen && ( + + )} +
  • +
  • setIsPlatformsOpen(true)} + onMouseLeave={() => setIsPlatformsOpen(false)} + onBlur={(e) => { + if (!e.currentTarget.contains(e.relatedTarget)) { + setIsPlatformsOpen(false); + } + }} + > + + {isPlatformsOpen && ( + + )} +
  • Date: Fri, 5 Dec 2025 08:20:22 +0000 Subject: [PATCH 2/8] refactor(web): simplify footer links to hover-reveal pattern - Replace dropdown menus with simpler hover-reveal links - 'Versus ???' reveals random competitor name on hover, links to VS page - 'Hyprnote for ???' reveals random use case on hover, links to solution page - Cleaner, less cluttered footer design Co-Authored-By: john@hyprnote.com --- apps/web/src/components/footer.tsx | 148 ++++++++--------------------- 1 file changed, 37 insertions(+), 111 deletions(-) diff --git a/apps/web/src/components/footer.tsx b/apps/web/src/components/footer.tsx index 77c719206a..03d09921d7 100644 --- a/apps/web/src/components/footer.tsx +++ b/apps/web/src/components/footer.tsx @@ -1,11 +1,6 @@ import { Link, useRouterState } from "@tanstack/react-router"; -import { - ChevronDown, - ChevronUp, - ExternalLinkIcon, - MailIcon, -} from "lucide-react"; -import { useState } from "react"; +import { ExternalLinkIcon, MailIcon } from "lucide-react"; +import { useMemo, useState } from "react"; const vsList = [ { slug: "otter", name: "Otter.ai" }, @@ -16,11 +11,13 @@ const vsList = [ { slug: "obsidian", name: "Obsidian" }, ]; -const platformsList = [ - { to: "/download/apple-silicon", label: "macOS (Apple Silicon)" }, - { to: "/download/apple-intel", label: "macOS (Intel)" }, - { to: "/download/windows", label: "Windows" }, - { to: "/download/linux", label: "Linux" }, +const useCasesList = [ + { to: "/solution/sales", label: "Sales" }, + { to: "/solution/recruiting", label: "Recruiting" }, + { to: "/solution/consulting", label: "Consulting" }, + { to: "/solution/coaching", label: "Coaching" }, + { to: "/solution/research", label: "Research" }, + { to: "/solution/journalism", label: "Journalism" }, ]; function getMaxWidthClass(pathname: string): string { @@ -168,8 +165,17 @@ function ProductLinks() { } function ResourcesLinks() { - const [isCompareOpen, setIsCompareOpen] = useState(false); - const [isPlatformsOpen, setIsPlatformsOpen] = useState(false); + const [isVersusHovered, setIsVersusHovered] = useState(false); + const [isUsedInHovered, setIsUsedInHovered] = useState(false); + + const randomVs = useMemo( + () => vsList[Math.floor(Math.random() * vsList.length)], + [], + ); + const randomUseCase = useMemo( + () => useCasesList[Math.floor(Math.random() * useCasesList.length)], + [], + ); return (
    @@ -206,109 +212,29 @@ function ResourcesLinks() {
  • setIsCompareOpen(true)} - onMouseLeave={() => setIsCompareOpen(false)} - onBlur={(e) => { - if (!e.currentTarget.contains(e.relatedTarget)) { - setIsCompareOpen(false); - } - }} + onMouseEnter={() => setIsVersusHovered(true)} + onMouseLeave={() => setIsVersusHovered(false)} > - - {isCompareOpen && ( - - )} + {isVersusHovered ? `Versus ${randomVs.name}` : "Versus ???"} +
  • setIsPlatformsOpen(true)} - onMouseLeave={() => setIsPlatformsOpen(false)} - onBlur={(e) => { - if (!e.currentTarget.contains(e.relatedTarget)) { - setIsPlatformsOpen(false); - } - }} + onMouseEnter={() => setIsUsedInHovered(true)} + onMouseLeave={() => setIsUsedInHovered(false)} > - - {isPlatformsOpen && ( - - )} + {isUsedInHovered + ? `Hyprnote for ${randomUseCase.label}` + : "Hyprnote for ???"} +
  • Date: Sat, 6 Dec 2025 04:38:09 +0000 Subject: [PATCH 3/8] feat(web): shuffle random item on each hover - Each hover picks a different random item from the list - Uses getNextRandomIndex helper to avoid showing same item twice in a row - Versus ??? and Hyprnote for ??? now shuffle on repeated hovers Co-Authored-By: john@hyprnote.com --- apps/web/src/components/footer.tsx | 44 +++++++++++++++++++++--------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/apps/web/src/components/footer.tsx b/apps/web/src/components/footer.tsx index 03d09921d7..98d1095181 100644 --- a/apps/web/src/components/footer.tsx +++ b/apps/web/src/components/footer.tsx @@ -1,6 +1,15 @@ import { Link, useRouterState } from "@tanstack/react-router"; import { ExternalLinkIcon, MailIcon } from "lucide-react"; -import { useMemo, useState } from "react"; +import { useState } from "react"; + +function getNextRandomIndex(length: number, prevIndex: number): number { + if (length <= 1) return 0; + let next = prevIndex; + while (next === prevIndex) { + next = Math.floor(Math.random() * length); + } + return next; +} const vsList = [ { slug: "otter", name: "Otter.ai" }, @@ -168,15 +177,16 @@ function ResourcesLinks() { const [isVersusHovered, setIsVersusHovered] = useState(false); const [isUsedInHovered, setIsUsedInHovered] = useState(false); - const randomVs = useMemo( - () => vsList[Math.floor(Math.random() * vsList.length)], - [], + const [vsIndex, setVsIndex] = useState(() => + Math.floor(Math.random() * vsList.length), ); - const randomUseCase = useMemo( - () => useCasesList[Math.floor(Math.random() * useCasesList.length)], - [], + const [useCaseIndex, setUseCaseIndex] = useState(() => + Math.floor(Math.random() * useCasesList.length), ); + const currentVs = vsList[vsIndex]; + const currentUseCase = useCasesList[useCaseIndex]; + return (

    @@ -212,27 +222,35 @@ function ResourcesLinks() {

  • setIsVersusHovered(true)} + onMouseEnter={() => { + setIsVersusHovered(true); + setVsIndex((prev) => getNextRandomIndex(vsList.length, prev)); + }} onMouseLeave={() => setIsVersusHovered(false)} > - {isVersusHovered ? `Versus ${randomVs.name}` : "Versus ???"} + {isVersusHovered ? `Versus ${currentVs.name}` : "Versus ???"}
  • setIsUsedInHovered(true)} + onMouseEnter={() => { + setIsUsedInHovered(true); + setUseCaseIndex((prev) => + getNextRandomIndex(useCasesList.length, prev), + ); + }} onMouseLeave={() => setIsUsedInHovered(false)} > {isUsedInHovered - ? `Hyprnote for ${randomUseCase.label}` + ? `Hyprnote for ${currentUseCase.label}` : "Hyprnote for ???"}
  • From 2c0b44c571547b8a20ccf1cb088cb7132492cd07 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sat, 6 Dec 2025 05:01:05 +0000 Subject: [PATCH 4/8] chore(web): change 'Hyprnote for ???' to 'For ???' Co-Authored-By: john@hyprnote.com --- apps/web/src/components/footer.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/apps/web/src/components/footer.tsx b/apps/web/src/components/footer.tsx index 98d1095181..a28ab12a77 100644 --- a/apps/web/src/components/footer.tsx +++ b/apps/web/src/components/footer.tsx @@ -249,9 +249,7 @@ function ResourcesLinks() { to={currentUseCase.to} className="text-sm text-neutral-600 hover:text-stone-600 transition-colors no-underline hover:underline hover:decoration-dotted" > - {isUsedInHovered - ? `Hyprnote for ${currentUseCase.label}` - : "Hyprnote for ???"} + {isUsedInHovered ? `For ${currentUseCase.label}` : "For ???"}
  • From fa2aa3dcd114775f4669602b5fad1cd551e2a641 Mon Sep 17 00:00:00 2001 From: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com> Date: Sat, 6 Dec 2025 05:03:32 +0000 Subject: [PATCH 5/8] feat(web): replace ??? with blurred text that reveals on hover - Remove hover boolean states (no longer needed) - Use Tailwind blur-sm and group-hover:blur-none for reveal effect - Only blur the dynamic part (competitor name / use case label) - Add smooth transition with duration-150 Co-Authored-By: john@hyprnote.com --- apps/web/src/components/footer.tsx | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/apps/web/src/components/footer.tsx b/apps/web/src/components/footer.tsx index a28ab12a77..81a5089c87 100644 --- a/apps/web/src/components/footer.tsx +++ b/apps/web/src/components/footer.tsx @@ -174,9 +174,6 @@ function ProductLinks() { } function ResourcesLinks() { - const [isVersusHovered, setIsVersusHovered] = useState(false); - const [isUsedInHovered, setIsUsedInHovered] = useState(false); - const [vsIndex, setVsIndex] = useState(() => Math.floor(Math.random() * vsList.length), ); @@ -223,33 +220,35 @@ function ResourcesLinks() {
  • { - setIsVersusHovered(true); setVsIndex((prev) => getNextRandomIndex(vsList.length, prev)); }} - onMouseLeave={() => setIsVersusHovered(false)} > - {isVersusHovered ? `Versus ${currentVs.name}` : "Versus ???"} + Versus{" "} + + {currentVs.name} +
  • { - setIsUsedInHovered(true); setUseCaseIndex((prev) => getNextRandomIndex(useCasesList.length, prev), ); }} - onMouseLeave={() => setIsUsedInHovered(false)} > - {isUsedInHovered ? `For ${currentUseCase.label}` : "For ???"} + For{" "} + + {currentUseCase.label} +
  • From d779bbc81994c36357cc3cedf0b99deab1726c2b Mon Sep 17 00:00:00 2001 From: John Jeong Date: Sat, 6 Dec 2025 21:50:14 +0900 Subject: [PATCH 6/8] feat(footer): improve accessibility and interaction for links --- apps/web/src/components/footer.tsx | 52 ++++++++++++++++++++++-------- 1 file changed, 38 insertions(+), 14 deletions(-) diff --git a/apps/web/src/components/footer.tsx b/apps/web/src/components/footer.tsx index 81a5089c87..9c90d81b26 100644 --- a/apps/web/src/components/footer.tsx +++ b/apps/web/src/components/footer.tsx @@ -218,35 +218,59 @@ function ResourcesLinks() {
  • -
  • { - setVsIndex((prev) => getNextRandomIndex(vsList.length, prev)); - }} - > +
  • { + setVsIndex((prev) => getNextRandomIndex(vsList.length, prev)); + }} + onMouseLeave={() => { + setVsIndex((prev) => getNextRandomIndex(vsList.length, prev)); + }} + onFocus={() => { + setVsIndex((prev) => getNextRandomIndex(vsList.length, prev)); + }} + onBlur={() => { + setVsIndex((prev) => getNextRandomIndex(vsList.length, prev)); + }} > Versus{" "} - + {currentVs.name}
  • -
  • { - setUseCaseIndex((prev) => - getNextRandomIndex(useCasesList.length, prev), - ); - }} - > +
  • { + setUseCaseIndex((prev) => + getNextRandomIndex(useCasesList.length, prev), + ); + }} + onMouseLeave={() => { + setUseCaseIndex((prev) => + getNextRandomIndex(useCasesList.length, prev), + ); + }} + onFocus={() => { + setUseCaseIndex((prev) => + getNextRandomIndex(useCasesList.length, prev), + ); + }} + onBlur={() => { + setUseCaseIndex((prev) => + getNextRandomIndex(useCasesList.length, prev), + ); + }} > For{" "} - + {currentUseCase.label} From c45fae05ef03adad759797c9b468ca5951e6b0d4 Mon Sep 17 00:00:00 2001 From: John Jeong Date: Sat, 6 Dec 2025 22:12:01 +0900 Subject: [PATCH 7/8] feat(web): add research solution page for Hyprnote --- apps/web/src/components/footer.tsx | 16 -- apps/web/src/routeTree.gen.ts | 84 ++++++ .../src/routes/_view/solution/coaching.tsx | 244 +++++++++++++++++ .../src/routes/_view/solution/consulting.tsx | 244 +++++++++++++++++ .../src/routes/_view/solution/journalism.tsx | 244 +++++++++++++++++ .../src/routes/_view/solution/research.tsx | 245 ++++++++++++++++++ 6 files changed, 1061 insertions(+), 16 deletions(-) create mode 100644 apps/web/src/routes/_view/solution/coaching.tsx create mode 100644 apps/web/src/routes/_view/solution/consulting.tsx create mode 100644 apps/web/src/routes/_view/solution/journalism.tsx create mode 100644 apps/web/src/routes/_view/solution/research.tsx diff --git a/apps/web/src/components/footer.tsx b/apps/web/src/components/footer.tsx index 9c90d81b26..612de3ea62 100644 --- a/apps/web/src/components/footer.tsx +++ b/apps/web/src/components/footer.tsx @@ -227,15 +227,9 @@ function ResourcesLinks() { onMouseEnter={() => { setVsIndex((prev) => getNextRandomIndex(vsList.length, prev)); }} - onMouseLeave={() => { - setVsIndex((prev) => getNextRandomIndex(vsList.length, prev)); - }} onFocus={() => { setVsIndex((prev) => getNextRandomIndex(vsList.length, prev)); }} - onBlur={() => { - setVsIndex((prev) => getNextRandomIndex(vsList.length, prev)); - }} > Versus{" "} @@ -253,21 +247,11 @@ function ResourcesLinks() { getNextRandomIndex(useCasesList.length, prev), ); }} - onMouseLeave={() => { - setUseCaseIndex((prev) => - getNextRandomIndex(useCasesList.length, prev), - ); - }} onFocus={() => { setUseCaseIndex((prev) => getNextRandomIndex(useCasesList.length, prev), ); }} - onBlur={() => { - setUseCaseIndex((prev) => - getNextRandomIndex(useCasesList.length, prev), - ); - }} > For{" "} diff --git a/apps/web/src/routeTree.gen.ts b/apps/web/src/routeTree.gen.ts index 1f3570567d..4fe3b3f95b 100644 --- a/apps/web/src/routeTree.gen.ts +++ b/apps/web/src/routeTree.gen.ts @@ -55,14 +55,18 @@ import { Route as ApiImagesSplatRouteImport } from './routes/api/images.$' import { Route as ViewVsSlugRouteImport } from './routes/_view/vs/$slug' import { Route as ViewTemplatesSlugRouteImport } from './routes/_view/templates/$slug' import { Route as ViewSolutionSalesRouteImport } from './routes/_view/solution/sales' +import { Route as ViewSolutionResearchRouteImport } from './routes/_view/solution/research' import { Route as ViewSolutionRecruitingRouteImport } from './routes/_view/solution/recruiting' import { Route as ViewSolutionProjectManagementRouteImport } from './routes/_view/solution/project-management' import { Route as ViewSolutionMediaRouteImport } from './routes/_view/solution/media' import { Route as ViewSolutionLegalRouteImport } from './routes/_view/solution/legal' +import { Route as ViewSolutionJournalismRouteImport } from './routes/_view/solution/journalism' import { Route as ViewSolutionHealthcareRouteImport } from './routes/_view/solution/healthcare' import { Route as ViewSolutionGovernmentRouteImport } from './routes/_view/solution/government' import { Route as ViewSolutionFieldEngineeringRouteImport } from './routes/_view/solution/field-engineering' import { Route as ViewSolutionCustomerSuccessRouteImport } from './routes/_view/solution/customer-success' +import { Route as ViewSolutionConsultingRouteImport } from './routes/_view/solution/consulting' +import { Route as ViewSolutionCoachingRouteImport } from './routes/_view/solution/coaching' import { Route as ViewShortcutsSlugRouteImport } from './routes/_view/shortcuts/$slug' import { Route as ViewRoadmapSlugRouteImport } from './routes/_view/roadmap/$slug' import { Route as ViewProductWorkflowsRouteImport } from './routes/_view/product/workflows' @@ -328,6 +332,11 @@ const ViewSolutionSalesRoute = ViewSolutionSalesRouteImport.update({ path: '/solution/sales', getParentRoute: () => ViewRouteRoute, } as any) +const ViewSolutionResearchRoute = ViewSolutionResearchRouteImport.update({ + id: '/solution/research', + path: '/solution/research', + getParentRoute: () => ViewRouteRoute, +} as any) const ViewSolutionRecruitingRoute = ViewSolutionRecruitingRouteImport.update({ id: '/solution/recruiting', path: '/solution/recruiting', @@ -349,6 +358,11 @@ const ViewSolutionLegalRoute = ViewSolutionLegalRouteImport.update({ path: '/solution/legal', getParentRoute: () => ViewRouteRoute, } as any) +const ViewSolutionJournalismRoute = ViewSolutionJournalismRouteImport.update({ + id: '/solution/journalism', + path: '/solution/journalism', + getParentRoute: () => ViewRouteRoute, +} as any) const ViewSolutionHealthcareRoute = ViewSolutionHealthcareRouteImport.update({ id: '/solution/healthcare', path: '/solution/healthcare', @@ -371,6 +385,16 @@ const ViewSolutionCustomerSuccessRoute = path: '/solution/customer-success', getParentRoute: () => ViewRouteRoute, } as any) +const ViewSolutionConsultingRoute = ViewSolutionConsultingRouteImport.update({ + id: '/solution/consulting', + path: '/solution/consulting', + getParentRoute: () => ViewRouteRoute, +} as any) +const ViewSolutionCoachingRoute = ViewSolutionCoachingRouteImport.update({ + id: '/solution/coaching', + path: '/solution/coaching', + getParentRoute: () => ViewRouteRoute, +} as any) const ViewShortcutsSlugRoute = ViewShortcutsSlugRouteImport.update({ id: '/shortcuts/$slug', path: '/shortcuts/$slug', @@ -601,14 +625,18 @@ export interface FileRoutesByFullPath { '/product/workflows': typeof ViewProductWorkflowsRoute '/roadmap/$slug': typeof ViewRoadmapSlugRoute '/shortcuts/$slug': typeof ViewShortcutsSlugRoute + '/solution/coaching': typeof ViewSolutionCoachingRoute + '/solution/consulting': typeof ViewSolutionConsultingRoute '/solution/customer-success': typeof ViewSolutionCustomerSuccessRoute '/solution/field-engineering': typeof ViewSolutionFieldEngineeringRoute '/solution/government': typeof ViewSolutionGovernmentRoute '/solution/healthcare': typeof ViewSolutionHealthcareRoute + '/solution/journalism': typeof ViewSolutionJournalismRoute '/solution/legal': typeof ViewSolutionLegalRoute '/solution/media': typeof ViewSolutionMediaRoute '/solution/project-management': typeof ViewSolutionProjectManagementRoute '/solution/recruiting': typeof ViewSolutionRecruitingRoute + '/solution/research': typeof ViewSolutionResearchRoute '/solution/sales': typeof ViewSolutionSalesRoute '/templates/$slug': typeof ViewTemplatesSlugRoute '/vs/$slug': typeof ViewVsSlugRoute @@ -686,14 +714,18 @@ export interface FileRoutesByTo { '/product/workflows': typeof ViewProductWorkflowsRoute '/roadmap/$slug': typeof ViewRoadmapSlugRoute '/shortcuts/$slug': typeof ViewShortcutsSlugRoute + '/solution/coaching': typeof ViewSolutionCoachingRoute + '/solution/consulting': typeof ViewSolutionConsultingRoute '/solution/customer-success': typeof ViewSolutionCustomerSuccessRoute '/solution/field-engineering': typeof ViewSolutionFieldEngineeringRoute '/solution/government': typeof ViewSolutionGovernmentRoute '/solution/healthcare': typeof ViewSolutionHealthcareRoute + '/solution/journalism': typeof ViewSolutionJournalismRoute '/solution/legal': typeof ViewSolutionLegalRoute '/solution/media': typeof ViewSolutionMediaRoute '/solution/project-management': typeof ViewSolutionProjectManagementRoute '/solution/recruiting': typeof ViewSolutionRecruitingRoute + '/solution/research': typeof ViewSolutionResearchRoute '/solution/sales': typeof ViewSolutionSalesRoute '/templates/$slug': typeof ViewTemplatesSlugRoute '/vs/$slug': typeof ViewVsSlugRoute @@ -776,14 +808,18 @@ export interface FileRoutesById { '/_view/product/workflows': typeof ViewProductWorkflowsRoute '/_view/roadmap/$slug': typeof ViewRoadmapSlugRoute '/_view/shortcuts/$slug': typeof ViewShortcutsSlugRoute + '/_view/solution/coaching': typeof ViewSolutionCoachingRoute + '/_view/solution/consulting': typeof ViewSolutionConsultingRoute '/_view/solution/customer-success': typeof ViewSolutionCustomerSuccessRoute '/_view/solution/field-engineering': typeof ViewSolutionFieldEngineeringRoute '/_view/solution/government': typeof ViewSolutionGovernmentRoute '/_view/solution/healthcare': typeof ViewSolutionHealthcareRoute + '/_view/solution/journalism': typeof ViewSolutionJournalismRoute '/_view/solution/legal': typeof ViewSolutionLegalRoute '/_view/solution/media': typeof ViewSolutionMediaRoute '/_view/solution/project-management': typeof ViewSolutionProjectManagementRoute '/_view/solution/recruiting': typeof ViewSolutionRecruitingRoute + '/_view/solution/research': typeof ViewSolutionResearchRoute '/_view/solution/sales': typeof ViewSolutionSalesRoute '/_view/templates/$slug': typeof ViewTemplatesSlugRoute '/_view/vs/$slug': typeof ViewVsSlugRoute @@ -866,14 +902,18 @@ export interface FileRouteTypes { | '/product/workflows' | '/roadmap/$slug' | '/shortcuts/$slug' + | '/solution/coaching' + | '/solution/consulting' | '/solution/customer-success' | '/solution/field-engineering' | '/solution/government' | '/solution/healthcare' + | '/solution/journalism' | '/solution/legal' | '/solution/media' | '/solution/project-management' | '/solution/recruiting' + | '/solution/research' | '/solution/sales' | '/templates/$slug' | '/vs/$slug' @@ -951,14 +991,18 @@ export interface FileRouteTypes { | '/product/workflows' | '/roadmap/$slug' | '/shortcuts/$slug' + | '/solution/coaching' + | '/solution/consulting' | '/solution/customer-success' | '/solution/field-engineering' | '/solution/government' | '/solution/healthcare' + | '/solution/journalism' | '/solution/legal' | '/solution/media' | '/solution/project-management' | '/solution/recruiting' + | '/solution/research' | '/solution/sales' | '/templates/$slug' | '/vs/$slug' @@ -1040,14 +1084,18 @@ export interface FileRouteTypes { | '/_view/product/workflows' | '/_view/roadmap/$slug' | '/_view/shortcuts/$slug' + | '/_view/solution/coaching' + | '/_view/solution/consulting' | '/_view/solution/customer-success' | '/_view/solution/field-engineering' | '/_view/solution/government' | '/_view/solution/healthcare' + | '/_view/solution/journalism' | '/_view/solution/legal' | '/_view/solution/media' | '/_view/solution/project-management' | '/_view/solution/recruiting' + | '/_view/solution/research' | '/_view/solution/sales' | '/_view/templates/$slug' | '/_view/vs/$slug' @@ -1413,6 +1461,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ViewSolutionSalesRouteImport parentRoute: typeof ViewRouteRoute } + '/_view/solution/research': { + id: '/_view/solution/research' + path: '/solution/research' + fullPath: '/solution/research' + preLoaderRoute: typeof ViewSolutionResearchRouteImport + parentRoute: typeof ViewRouteRoute + } '/_view/solution/recruiting': { id: '/_view/solution/recruiting' path: '/solution/recruiting' @@ -1441,6 +1496,13 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ViewSolutionLegalRouteImport parentRoute: typeof ViewRouteRoute } + '/_view/solution/journalism': { + id: '/_view/solution/journalism' + path: '/solution/journalism' + fullPath: '/solution/journalism' + preLoaderRoute: typeof ViewSolutionJournalismRouteImport + parentRoute: typeof ViewRouteRoute + } '/_view/solution/healthcare': { id: '/_view/solution/healthcare' path: '/solution/healthcare' @@ -1469,6 +1531,20 @@ declare module '@tanstack/react-router' { preLoaderRoute: typeof ViewSolutionCustomerSuccessRouteImport parentRoute: typeof ViewRouteRoute } + '/_view/solution/consulting': { + id: '/_view/solution/consulting' + path: '/solution/consulting' + fullPath: '/solution/consulting' + preLoaderRoute: typeof ViewSolutionConsultingRouteImport + parentRoute: typeof ViewRouteRoute + } + '/_view/solution/coaching': { + id: '/_view/solution/coaching' + path: '/solution/coaching' + fullPath: '/solution/coaching' + preLoaderRoute: typeof ViewSolutionCoachingRouteImport + parentRoute: typeof ViewRouteRoute + } '/_view/shortcuts/$slug': { id: '/_view/shortcuts/$slug' path: '/shortcuts/$slug' @@ -1793,14 +1869,18 @@ interface ViewRouteRouteChildren { ViewProductWorkflowsRoute: typeof ViewProductWorkflowsRoute ViewRoadmapSlugRoute: typeof ViewRoadmapSlugRoute ViewShortcutsSlugRoute: typeof ViewShortcutsSlugRoute + ViewSolutionCoachingRoute: typeof ViewSolutionCoachingRoute + ViewSolutionConsultingRoute: typeof ViewSolutionConsultingRoute ViewSolutionCustomerSuccessRoute: typeof ViewSolutionCustomerSuccessRoute ViewSolutionFieldEngineeringRoute: typeof ViewSolutionFieldEngineeringRoute ViewSolutionGovernmentRoute: typeof ViewSolutionGovernmentRoute ViewSolutionHealthcareRoute: typeof ViewSolutionHealthcareRoute + ViewSolutionJournalismRoute: typeof ViewSolutionJournalismRoute ViewSolutionLegalRoute: typeof ViewSolutionLegalRoute ViewSolutionMediaRoute: typeof ViewSolutionMediaRoute ViewSolutionProjectManagementRoute: typeof ViewSolutionProjectManagementRoute ViewSolutionRecruitingRoute: typeof ViewSolutionRecruitingRoute + ViewSolutionResearchRoute: typeof ViewSolutionResearchRoute ViewSolutionSalesRoute: typeof ViewSolutionSalesRoute ViewTemplatesSlugRoute: typeof ViewTemplatesSlugRoute ViewVsSlugRoute: typeof ViewVsSlugRoute @@ -1858,14 +1938,18 @@ const ViewRouteRouteChildren: ViewRouteRouteChildren = { ViewProductWorkflowsRoute: ViewProductWorkflowsRoute, ViewRoadmapSlugRoute: ViewRoadmapSlugRoute, ViewShortcutsSlugRoute: ViewShortcutsSlugRoute, + ViewSolutionCoachingRoute: ViewSolutionCoachingRoute, + ViewSolutionConsultingRoute: ViewSolutionConsultingRoute, ViewSolutionCustomerSuccessRoute: ViewSolutionCustomerSuccessRoute, ViewSolutionFieldEngineeringRoute: ViewSolutionFieldEngineeringRoute, ViewSolutionGovernmentRoute: ViewSolutionGovernmentRoute, ViewSolutionHealthcareRoute: ViewSolutionHealthcareRoute, + ViewSolutionJournalismRoute: ViewSolutionJournalismRoute, ViewSolutionLegalRoute: ViewSolutionLegalRoute, ViewSolutionMediaRoute: ViewSolutionMediaRoute, ViewSolutionProjectManagementRoute: ViewSolutionProjectManagementRoute, ViewSolutionRecruitingRoute: ViewSolutionRecruitingRoute, + ViewSolutionResearchRoute: ViewSolutionResearchRoute, ViewSolutionSalesRoute: ViewSolutionSalesRoute, ViewTemplatesSlugRoute: ViewTemplatesSlugRoute, ViewVsSlugRoute: ViewVsSlugRoute, diff --git a/apps/web/src/routes/_view/solution/coaching.tsx b/apps/web/src/routes/_view/solution/coaching.tsx new file mode 100644 index 0000000000..006652fdb1 --- /dev/null +++ b/apps/web/src/routes/_view/solution/coaching.tsx @@ -0,0 +1,244 @@ +import { Icon } from "@iconify-icon/react"; +import { createFileRoute, Link } from "@tanstack/react-router"; + +import { cn } from "@hypr/utils"; + +export const Route = createFileRoute("/_view/solution/coaching")({ + component: Component, + head: () => ({ + meta: [ + { title: "AI Meeting Notes for Coaches - Hyprnote" }, + { + name: "description", + content: + "Capture coaching sessions, client progress, and breakthrough moments with AI-powered meeting notes. Focus on your clients with Hyprnote.", + }, + { + property: "og:title", + content: "AI Meeting Notes for Coaches - Hyprnote", + }, + { + property: "og:description", + content: + "Be fully present with your clients while AI captures every insight. Track progress and deliver transformational coaching.", + }, + { property: "og:type", content: "website" }, + { + property: "og:url", + content: "https://hyprnote.com/solution/coaching", + }, + ], + }), +}); + +const features = [ + { + icon: "mdi:account-heart", + title: "Full Presence", + description: + "Stop taking notes during sessions. Be fully present and engaged with your clients while AI captures everything.", + }, + { + icon: "mdi:chart-line", + title: "Progress Tracking", + description: + "Track client goals, commitments, and breakthroughs across sessions. See patterns and progress over time.", + }, + { + icon: "mdi:lightbulb-on", + title: "Insight Capture", + description: + "AI identifies key themes, mindset shifts, and breakthrough moments from coaching conversations.", + }, + { + icon: "mdi:clipboard-check", + title: "Action Items", + description: + "Automatically extract commitments, homework, and action items from each session for follow-up.", + }, + { + icon: "mdi:message-reply-text", + title: "Session Summaries", + description: + "Generate session summaries to share with clients. Help them remember key insights and next steps.", + }, + { + icon: "mdi:shield-lock", + title: "Client Privacy", + description: + "Local AI processing ensures sensitive coaching conversations stay completely private and secure.", + }, +]; + +const useCases = [ + { + title: "Life Coaching", + description: + "Capture personal goals, challenges, and transformation journeys. Track client progress toward their vision.", + }, + { + title: "Executive Coaching", + description: + "Document leadership challenges, strategic thinking, and professional development goals. Support high-stakes growth.", + }, + { + title: "Career Coaching", + description: + "Track career goals, skill development, and job search progress. Help clients navigate their career journey.", + }, + { + title: "Health & Wellness", + description: + "Record wellness goals, habit changes, and health milestones. Support clients in sustainable lifestyle changes.", + }, +]; + +function Component() { + return ( +
    +
    + + + + +
    +
    + ); +} + +function HeroSection() { + return ( +
    +
    +
    +
    + + For Coaches +
    +

    + Transform lives with +
    + AI-powered meeting notes +

    +

    + Be fully present with your clients while Hyprnote captures every + insight, tracks progress, and helps you deliver transformational + coaching. +

    +
    + + Download for free + + + See how it works + +
    +
    +
    +
    + ); +} + +function FeaturesSection() { + return ( +
    +
    +

    + Built for coaching excellence +

    +

    + Every feature designed to help you be fully present and deliver + transformational results. +

    +
    + {features.map((feature) => ( +
    +
    + +
    +

    + {feature.title} +

    +

    + {feature.description} +

    +
    + ))} +
    +
    +
    + ); +} + +function UseCasesSection() { + return ( +
    +
    +

    + For every coaching practice +

    +

    + Whatever your coaching specialty, Hyprnote helps you capture and + leverage every client session. +

    +
    + {useCases.map((useCase) => ( +
    +

    + {useCase.title} +

    +

    + {useCase.description} +

    +
    + ))} +
    +
    +
    + ); +} + +function CTASection() { + return ( +
    +
    +

    + Ready to amplify your impact? +

    +

    + Join coaches who are transforming lives with AI-powered meeting notes. +

    + + Get started for free + +
    +
    + ); +} diff --git a/apps/web/src/routes/_view/solution/consulting.tsx b/apps/web/src/routes/_view/solution/consulting.tsx new file mode 100644 index 0000000000..50fd5a9d70 --- /dev/null +++ b/apps/web/src/routes/_view/solution/consulting.tsx @@ -0,0 +1,244 @@ +import { Icon } from "@iconify-icon/react"; +import { createFileRoute, Link } from "@tanstack/react-router"; + +import { cn } from "@hypr/utils"; + +export const Route = createFileRoute("/_view/solution/consulting")({ + component: Component, + head: () => ({ + meta: [ + { title: "AI Meeting Notes for Consultants - Hyprnote" }, + { + name: "description", + content: + "Capture client conversations, project details, and strategic insights with AI-powered meeting notes. Deliver better recommendations with Hyprnote.", + }, + { + property: "og:title", + content: "AI Meeting Notes for Consultants - Hyprnote", + }, + { + property: "og:description", + content: + "Focus on advising clients while AI captures every detail. Turn meeting notes into actionable insights and deliverables.", + }, + { property: "og:type", content: "website" }, + { + property: "og:url", + content: "https://hyprnote.com/solution/consulting", + }, + ], + }), +}); + +const features = [ + { + icon: "mdi:briefcase-check", + title: "Client Meeting Capture", + description: + "Record client discussions, requirements, and strategic priorities. Keep detailed records of every engagement.", + }, + { + icon: "mdi:lightbulb", + title: "Insight Extraction", + description: + "AI identifies key themes, pain points, and opportunities from client conversations to inform your recommendations.", + }, + { + icon: "mdi:file-document-multiple", + title: "Deliverable Support", + description: + "Turn meeting notes into reports, presentations, and recommendations. Extract quotes and data points for your deliverables.", + }, + { + icon: "mdi:account-group", + title: "Stakeholder Management", + description: + "Track different stakeholder perspectives, concerns, and priorities across multiple conversations.", + }, + { + icon: "mdi:clock-check", + title: "Billable Hours", + description: + "Accurate meeting records support time tracking and billing documentation for client engagements.", + }, + { + icon: "mdi:shield-lock", + title: "Client Confidentiality", + description: + "Local AI processing ensures sensitive client information stays private and secure on your device.", + }, +]; + +const useCases = [ + { + title: "Discovery & Scoping", + description: + "Capture project requirements, success criteria, and constraints. Build comprehensive understanding of client needs.", + }, + { + title: "Strategy Sessions", + description: + "Document strategic discussions, decision rationale, and alignment points. Create clear records of recommendations.", + }, + { + title: "Stakeholder Interviews", + description: + "Record perspectives from different stakeholders. Identify patterns and conflicts across the organization.", + }, + { + title: "Status Updates & Reviews", + description: + "Track project progress, client feedback, and change requests. Maintain detailed engagement history.", + }, +]; + +function Component() { + return ( +
    +
    + + + + +
    +
    + ); +} + +function HeroSection() { + return ( +
    +
    +
    +
    + + For Consultants +
    +

    + Deliver better insights with +
    + AI-powered meeting notes +

    +

    + Focus on advising your clients while Hyprnote captures every detail, + extracts insights, and helps you create compelling deliverables. +

    +
    + + Download for free + + + See how it works + +
    +
    +
    +
    + ); +} + +function FeaturesSection() { + return ( +
    +
    +

    + Built for consulting excellence +

    +

    + Every feature designed to help you deliver exceptional client value + and insights. +

    +
    + {features.map((feature) => ( +
    +
    + +
    +

    + {feature.title} +

    +

    + {feature.description} +

    +
    + ))} +
    +
    +
    + ); +} + +function UseCasesSection() { + return ( +
    +
    +

    + For every client engagement +

    +

    + From discovery to delivery, Hyprnote helps you capture and leverage + every client interaction. +

    +
    + {useCases.map((useCase) => ( +
    +

    + {useCase.title} +

    +

    + {useCase.description} +

    +
    + ))} +
    +
    +
    + ); +} + +function CTASection() { + return ( +
    +
    +

    + Ready to elevate your consulting? +

    +

    + Join consultants who are delivering better insights with AI-powered + meeting notes. +

    + + Get started for free + +
    +
    + ); +} diff --git a/apps/web/src/routes/_view/solution/journalism.tsx b/apps/web/src/routes/_view/solution/journalism.tsx new file mode 100644 index 0000000000..5ae185940f --- /dev/null +++ b/apps/web/src/routes/_view/solution/journalism.tsx @@ -0,0 +1,244 @@ +import { Icon } from "@iconify-icon/react"; +import { createFileRoute, Link } from "@tanstack/react-router"; + +import { cn } from "@hypr/utils"; + +export const Route = createFileRoute("/_view/solution/journalism")({ + component: Component, + head: () => ({ + meta: [ + { title: "AI Meeting Notes for Journalists - Hyprnote" }, + { + name: "description", + content: + "Capture interviews, press briefings, and source conversations with AI-powered meeting notes. Report with confidence using Hyprnote.", + }, + { + property: "og:title", + content: "AI Meeting Notes for Journalists - Hyprnote", + }, + { + property: "og:description", + content: + "Focus on the story while AI captures every quote. Get accurate transcriptions and never miss a detail.", + }, + { property: "og:type", content: "website" }, + { + property: "og:url", + content: "https://hyprnote.com/solution/journalism", + }, + ], + }), +}); + +const features = [ + { + icon: "mdi:microphone", + title: "Interview Recording", + description: + "Record interviews, press conferences, and source conversations. Get accurate transcriptions with timestamps.", + }, + { + icon: "mdi:format-quote-open", + title: "Precise Quotes", + description: + "Extract exact quotes with context. Ensure accuracy and attribution in your reporting.", + }, + { + icon: "mdi:magnify", + title: "Fact Verification", + description: + "Search through interview transcripts to verify details, dates, and claims from your sources.", + }, + { + icon: "mdi:clock-fast", + title: "Fast Turnaround", + description: + "Get instant transcriptions. Spend less time on notes and more time writing your story.", + }, + { + icon: "mdi:archive", + title: "Source Documentation", + description: + "Maintain detailed records of all interviews and conversations. Protect your reporting with documentation.", + }, + { + icon: "mdi:shield-lock", + title: "Source Protection", + description: + "Local AI processing ensures sensitive source information stays private and secure on your device.", + }, +]; + +const useCases = [ + { + title: "Investigative Reporting", + description: + "Document complex interviews and source conversations. Build comprehensive records for long-form investigations.", + }, + { + title: "Breaking News", + description: + "Capture press briefings and rapid interviews. Get quotes fast for deadline reporting.", + }, + { + title: "Feature Stories", + description: + "Record in-depth interviews and personal narratives. Find the perfect quotes to bring your stories to life.", + }, + { + title: "Press Events", + description: + "Capture statements from press conferences, panel discussions, and media events with accurate attribution.", + }, +]; + +function Component() { + return ( +
    +
    + + + + +
    +
    + ); +} + +function HeroSection() { + return ( +
    +
    +
    +
    + + For Journalists +
    +

    + Report with confidence using +
    + AI-powered meeting notes +

    +

    + Focus on asking the right questions while Hyprnote captures every + quote, verifies accuracy, and helps you tell compelling stories. +

    +
    + + Download for free + + + See how it works + +
    +
    +
    +
    + ); +} + +function FeaturesSection() { + return ( +
    +
    +

    + Built for journalism excellence +

    +

    + Every feature designed to help you report with accuracy and + confidence. +

    +
    + {features.map((feature) => ( +
    +
    + +
    +

    + {feature.title} +

    +

    + {feature.description} +

    +
    + ))} +
    +
    +
    + ); +} + +function UseCasesSection() { + return ( +
    +
    +

    + For every beat and story +

    +

    + Whatever you cover, Hyprnote helps you capture and verify every + conversation. +

    +
    + {useCases.map((useCase) => ( +
    +

    + {useCase.title} +

    +

    + {useCase.description} +

    +
    + ))} +
    +
    +
    + ); +} + +function CTASection() { + return ( +
    +
    +

    + Ready to report with confidence? +

    +

    + Join journalists who are telling better stories with AI-powered + meeting notes. +

    + + Get started for free + +
    +
    + ); +} diff --git a/apps/web/src/routes/_view/solution/research.tsx b/apps/web/src/routes/_view/solution/research.tsx new file mode 100644 index 0000000000..660c9cc068 --- /dev/null +++ b/apps/web/src/routes/_view/solution/research.tsx @@ -0,0 +1,245 @@ +import { Icon } from "@iconify-icon/react"; +import { createFileRoute, Link } from "@tanstack/react-router"; + +import { cn } from "@hypr/utils"; + +export const Route = createFileRoute("/_view/solution/research")({ + component: Component, + head: () => ({ + meta: [ + { title: "AI Meeting Notes for Researchers - Hyprnote" }, + { + name: "description", + content: + "Capture interviews, observations, and research insights with AI-powered meeting notes. Analyze faster with Hyprnote.", + }, + { + property: "og:title", + content: "AI Meeting Notes for Researchers - Hyprnote", + }, + { + property: "og:description", + content: + "Focus on discovery while AI captures every detail. Turn research conversations into actionable insights.", + }, + { property: "og:type", content: "website" }, + { + property: "og:url", + content: "https://hyprnote.com/solution/research", + }, + ], + }), +}); + +const features = [ + { + icon: "mdi:microphone-variant", + title: "Interview Recording", + description: + "Capture user interviews, expert conversations, and field observations. Get accurate transcriptions of every research session.", + }, + { + icon: "mdi:tag-multiple", + title: "Theme Identification", + description: + "AI helps identify recurring themes, patterns, and insights across multiple research sessions.", + }, + { + icon: "mdi:format-quote-close", + title: "Quote Extraction", + description: + "Easily find and extract participant quotes for reports, papers, and presentations.", + }, + { + icon: "mdi:file-search", + title: "Search & Analysis", + description: + "Search across all your research notes to find specific topics, responses, or insights instantly.", + }, + { + icon: "mdi:book-open-variant", + title: "Research Synthesis", + description: + "Turn raw interview data into structured findings. Support your analysis with detailed evidence.", + }, + { + icon: "mdi:shield-lock", + title: "Participant Privacy", + description: + "Local AI processing ensures sensitive research data and participant information stays private.", + }, +]; + +const useCases = [ + { + title: "User Research", + description: + "Capture user interviews, usability tests, and feedback sessions. Identify patterns in user needs and behaviors.", + }, + { + title: "Academic Research", + description: + "Record research interviews, focus groups, and field observations. Support qualitative analysis with detailed transcripts.", + }, + { + title: "Market Research", + description: + "Document customer conversations, market interviews, and competitive insights. Extract themes and trends.", + }, + { + title: "Ethnographic Studies", + description: + "Capture field notes, participant observations, and contextual inquiries. Build rich qualitative datasets.", + }, +]; + +function Component() { + return ( +
    +
    + + + + +
    +
    + ); +} + +function HeroSection() { + return ( +
    +
    +
    +
    + + For Researchers +
    +

    + Discover faster with +
    + AI-powered meeting notes +

    +

    + Focus on asking questions and observing while Hyprnote captures + every detail, identifies themes, and helps you analyze research + conversations. +

    +
    + + Download for free + + + See how it works + +
    +
    +
    +
    + ); +} + +function FeaturesSection() { + return ( +
    +
    +

    + Built for research excellence +

    +

    + Every feature designed to help you capture, analyze, and synthesize + research insights. +

    +
    + {features.map((feature) => ( +
    +
    + +
    +

    + {feature.title} +

    +

    + {feature.description} +

    +
    + ))} +
    +
    +
    + ); +} + +function UseCasesSection() { + return ( +
    +
    +

    + For every research method +

    +

    + Whatever your research approach, Hyprnote helps you capture and + analyze every conversation. +

    +
    + {useCases.map((useCase) => ( +
    +

    + {useCase.title} +

    +

    + {useCase.description} +

    +
    + ))} +
    +
    +
    + ); +} + +function CTASection() { + return ( +
    +
    +

    + Ready to accelerate your research? +

    +

    + Join researchers who are discovering faster with AI-powered meeting + notes. +

    + + Get started for free + +
    +
    + ); +} From e6a41921befef9e29883be07863f327e4ccaff16 Mon Sep 17 00:00:00 2001 From: ComputelessComputer Date: Sat, 6 Dec 2025 23:46:19 +0900 Subject: [PATCH 8/8] feat(footer): update footer links and navigation structure --- apps/web/src/components/footer.tsx | 95 ++++++++++++++++-------------- 1 file changed, 52 insertions(+), 43 deletions(-) diff --git a/apps/web/src/components/footer.tsx b/apps/web/src/components/footer.tsx index 612de3ea62..97c5046aca 100644 --- a/apps/web/src/components/footer.tsx +++ b/apps/web/src/components/footer.tsx @@ -2,6 +2,8 @@ import { Link, useRouterState } from "@tanstack/react-router"; import { ExternalLinkIcon, MailIcon } from "lucide-react"; import { useState } from "react"; +import { Image } from "@/components/image"; + function getNextRandomIndex(length: number, prevIndex: number): number { if (length <= 1) return 0; let next = prevIndex; @@ -58,7 +60,7 @@ function BrandSection({ currentYear }: { currentYear: number }) { return (
    - Hyprnote