diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000..7405768 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,215 @@ +# Apache License 2.0 + +> Apache License +> Version 2.0, January 2004 +> http://www.apache.org/licenses/ +> +> TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + +## 1. Definitions. + +- **"License"** shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + +- **"Licensor"** shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + +- **"Legal Entity"** shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + **"control"** means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + +- **"You"** (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + +- **"Source"** form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + +- **"Object"** form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + +- **"Work"** shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + +- **"Derivative Works"** shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + +- **"Contribution"** shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + +- **"Contributor"** shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + +## 2. Grant of Copyright License. Subject to the terms and conditions of + +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +copyright license to reproduce, prepare Derivative Works of, +publicly display, publicly perform, sublicense, and distribute the +Work and such Derivative Works in Source or Object form. + +## 3. Grant of Patent License. Subject to the terms and conditions of + +this License, each Contributor hereby grants to You a perpetual, +worldwide, non-exclusive, no-charge, royalty-free, irrevocable +(except as stated in this section) patent license to make, have made, +use, offer to sell, sell, import, and otherwise transfer the Work, +where such license applies only to those patent claims licensable +by such Contributor that are necessarily infringed by their +Contribution(s) alone or by combination of their Contribution(s) +with the Work to which such Contribution(s) was submitted. If You +institute patent litigation against any entity (including a +cross-claim or counterclaim in a lawsuit) alleging that the Work +or a Contribution incorporated within the Work constitutes direct +or contributory patent infringement, then any patent licenses +granted to You under this License for that Work shall terminate +as of the date such litigation is filed. + +## 4. Redistribution. You may reproduce and distribute copies of the + +Work or Derivative Works thereof in any medium, with or without +modifications, and in Source or Object form, provided that You +meet the following conditions: + +1. You must give any other recipients of the Work or + Derivative Works a copy of this License; and + +2. You must cause any modified files to carry prominent notices + stating that You changed the files; and + +3. You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + +4. If the Work includes a **"NOTICE"** text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + +You may add Your own copyright statement to Your modifications and +may provide additional or different license terms and conditions +for use, reproduction, or distribution of Your modifications, or +for any such Derivative Works as a whole, provided Your use, +reproduction, and distribution of the Work otherwise complies with +the conditions stated in this License. + +## 5. Submission of Contributions. Unless You explicitly state otherwise, + +any Contribution intentionally submitted for inclusion in the Work +by You to the Licensor shall be under the terms and conditions of +this License, without any additional terms or conditions. +Notwithstanding the above, nothing herein shall supersede or modify +the terms of any separate license agreement you may have executed +with Licensor regarding such Contributions. + +## 6. Trademarks. This License does not grant permission to use the trade + +names, trademarks, service marks, or product names of the Licensor, +except as required for reasonable and customary use in describing the +origin of the Work and reproducing the content of the NOTICE file. + +## 7. Disclaimer of Warranty. Unless required by applicable law or + +agreed to in writing, Licensor provides the Work (and each +Contributor provides its Contributions) on an **"AS IS"** BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +implied, including, without limitation, any warranties or conditions +of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A +PARTICULAR PURPOSE. You are solely responsible for determining the +appropriateness of using or redistributing the Work and assume any +risks associated with Your exercise of permissions under this License. + +## 8. Limitation of Liability. In no event and under no legal theory, + +whether in tort (including negligence), contract, or otherwise, +unless required by applicable law (such as deliberate and grossly +negligent acts) or agreed to in writing, shall any Contributor be +liable to You for damages, including any direct, indirect, special, +incidental, or consequential damages of any character arising as a +result of this License or out of the use or inability to use the +Work (including but not limited to damages for loss of goodwill, +work stoppage, computer failure or malfunction, or any and all +other commercial damages or losses), even if such Contributor +has been advised of the possibility of such damages. + +## 9. Accepting Warranty or Additional Liability. While redistributing + +the Work or Derivative Works thereof, You may choose to offer, +and charge a fee for, acceptance of support, warranty, indemnity, +or other liability obligations and/or rights consistent with this +License. However, in accepting such obligations, You may act only +on Your own behalf and on Your sole responsibility, not on behalf +of any other Contributor, and only if You agree to indemnify, +defend, and hold each Contributor harmless for any liability +incurred by, or claims asserted against, such Contributor by reason +of your accepting any such warranty or additional liability. + +> END OF TERMS AND CONDITIONS + +--- + +APPENDIX: How to apply the Apache License to your work. + +To apply the Apache License to your work, attach the following +boilerplate notice, with the fields enclosed by brackets **"[]"** +replaced with your own identifying information. (Don't include +the brackets!) The text should be enclosed in the appropriate +comment syntax for the file format. We also recommend that a +file or class name and description of purpose be included on the +same **"printed page"** as the copyright notice for easier +identification within third-party archives. + +--- + +© Copyright 2024 [Pavol Hejný](https://pavolhejny.com/) + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + +http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an **"AS IS"** BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. \ No newline at end of file diff --git a/README.md b/README.md index a8f8b6b..b764c46 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,17 @@ # 🆎 Alderon Landing Page + + + + +[![deploy](https://github.com/webgptorg/aldaron/actions/workflows/deploy.yml/badge.svg)](https://github.com/webgptorg/aldaron/actions/workflows/deploy.yml) +[![Known Vulnerabilities](https://snyk.io/test/github/webgptorg/aldaron/badge.svg)](https://snyk.io/test/github/webgptorg/aldaron) +[![Issues](https://img.shields.io/github/issues/webgptorg/aldaron.svg?style=flat)](https://github.com/webgptorg/aldaron/issues) + + + + + Reclaim Your Time with AI That Thinks Like You Stop spending 80% of your time on unimportant tasks. Let your AI avatar handle emails, meetings, and routine work while you focus on what truly matters. @@ -7,3 +19,20 @@ Stop spending 80% of your time on unimportant tasks. Let your AI avatar handle e Build on [Promptbook Engine](https://ptbk.io) For more information, [check out the landing page sheet](https://docs.google.com/spreadsheets/d/1X26iMQqubsxftqD1EJNSlzPYFS94QjCFPXyKdHHDeVs/edit?usp=sharing) + + + + + + +## ✨ Partners + + +WebGPT +      +Promptbook + + +[Become a partner](https://www.pavolhejny.com/contact/) + + \ No newline at end of file diff --git a/app/globals.css b/app/globals.css index 0e110ff..515478c 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,8 +1,12 @@ -@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap'); +@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Outfit:wght@400;500;600;700;800&display=swap'); @tailwind base; @tailwind components; @tailwind utilities; +html, body { + overflow-x: hidden; +} + :root { --promptbook-blue: #7aebff; --promptbook-blue-dark: #30a8bd; @@ -85,6 +89,10 @@ } body { @apply bg-background text-foreground; + font-family: var(--font-inter), 'Inter', ui-sans-serif, system-ui, sans-serif; + } + h1, h2, h3, h4, h5, h6 { + font-family: var(--font-outfit), 'Outfit', ui-sans-serif, system-ui, sans-serif; } } diff --git a/app/layout.tsx b/app/layout.tsx index 34251ae..dc0369e 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,17 +1,18 @@ import { GOOGLE_ANALYTICS_ID } from '@/config'; import { Metadata } from 'next'; -import { Inter } from 'next/font/google'; +import { Inter, Outfit } from 'next/font/google'; import Script from 'next/script'; -import ConditionalChatbot from './conditional-chatbot'; +// ConditionalChatbot removed — distraction from conversion CTA import './globals.css'; -const inter = Inter({ subsets: ['latin'] }); +const inter = Inter({ subsets: ['latin', 'latin-ext'], variable: '--font-inter' }); +const outfit = Outfit({ subsets: ['latin', 'latin-ext'], variable: '--font-outfit', weight: ['400', '500', '600', '700', '800'] }); export const metadata: Metadata = { - title: 'Promptbook - AI-Powered Prompt Engineering Platform', + title: 'Promptbook — AI paměť vaší firmy', description: - 'Create, manage, and optimize AI prompts with Promptbook. The ultimate platform for prompt engineering, testing, and collaboration.', - keywords: ['AI', 'prompt engineering', 'artificial intelligence', 'prompts', 'GPT', 'machine learning'], + 'Nahrajte firemní dokumenty, vytvořte virtuálního zaměstnance a ptejte se normální češtinou. Bez promptů, bez halucinací, 100% GDPR.', + keywords: ['AI', 'firemní data', 'znalostní báze', 'RAG', 'virtuální zaměstnanec', 'GDPR', 'promptbook'], authors: [{ name: 'Promptbook Team' }], creator: 'Promptbook', publisher: 'Promptbook', @@ -25,9 +26,9 @@ export const metadata: Metadata = { canonical: '/', }, openGraph: { - title: 'Promptbook - AI-Powered Prompt Engineering Platform', + title: 'Promptbook — AI paměť vaší firmy', description: - 'Create, manage, and optimize AI prompts with Promptbook. The ultimate platform for prompt engineering, testing, and collaboration.', + 'Nahrajte firemní dokumenty, vytvořte virtuálního zaměstnance a ptejte se normální češtinou. Bez promptů, bez halucinací.', url: 'https://aldaron.vercel.app', siteName: 'Promptbook', images: [ @@ -38,14 +39,14 @@ export const metadata: Metadata = { alt: 'Promptbook Logo', }, ], - locale: 'en_US', + locale: 'cs_CZ', type: 'website', }, twitter: { card: 'summary_large_image', - title: 'Promptbook - AI-Powered Prompt Engineering Platform', + title: 'Promptbook — AI paměť vaší firmy', description: - 'Create, manage, and optimize AI prompts with Promptbook. The ultimate platform for prompt engineering, testing, and collaboration.', + 'Nahrajte firemní dokumenty, vytvořte virtuálního zaměstnance a ptejte se normální češtinou.', images: ['/promptbook-logo-blue-256.png'], creator: '@promptbook', }, @@ -76,7 +77,7 @@ export const metadata: Metadata = { export default function RootLayout({ children }: { children: React.ReactNode }) { return ( - + @@ -98,9 +99,9 @@ export default function RootLayout({ children }: { children: React.ReactNode }) `} - + {children} - + {/* ConditionalChatbot removed for conversion LP */} ); diff --git a/app/page.tsx b/app/page.tsx index e2ed6c6..960717e 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,53 +1,46 @@ -import { AvatarBookSection } from '@/components/avatar-book-section'; -import { BenefitsSection } from '@/components/benefits-section'; -import { Footer } from '@/components/footer'; import { Header } from '@/components/header'; import { HeroSection } from '@/components/hero-section'; -import { IntegrationsSection } from '@/components/integrations-section'; -import { PricingSection } from '@/components/pricing-section'; +import { SocialProofStrip } from '@/components/social-proof-strip'; +import { PainPointsSection } from '@/components/pain-points-section'; +import { SolutionSection } from '@/components/solution-section'; +import { HowItWorksSection } from '@/components/how-it-works-section'; +import { EnemySection } from '@/components/enemy-section'; +import { TestimonialsSection } from '@/components/testimonials-section'; +import { FAQSection } from '@/components/faq-section'; +import { FinalCTASection } from '@/components/final-cta-section'; +import { MinimalFooter } from '@/components/minimal-footer'; +import { QualificationPopup } from '@/components/qualification-popup'; import { Metadata } from 'next'; import { Suspense } from 'react'; // Force static generation for static export export const dynamic = 'force-static'; -type Props = { - searchParams: { [key: string]: string | string[] | undefined }; -}; - export function generateMetadata(): Metadata { - // For static export, we can't use searchParams in generateMetadata - // The dynamic title will be handled client-side return { - title: '✨ Make AI that Thinks Like You', - description: 'Reclaim Your Time with AI That Thinks Like You ✨ Powered by Promptbook', + title: 'Promptbook — Okamžitý přístup ke všemu, co vaše firma kdy napsala', + description: + 'Nahrajte firemní dokumenty, vytvořte virtuálního zaměstnance a ptejte se normální češtinou. Bez promptů, bez halucinací, 100% GDPR. Česká AI platforma.', }; } -/** - * ROT13 decoder function - */ -function rot13(str: string): string { - return str.replace(/[a-zA-Z]/g, (char) => { - const start = char <= 'Z' ? 65 : 97; - return String.fromCharCode(((char.charCodeAt(0) - start + 13) % 26) + start); - }); -} - -export default function Home({ searchParams }: Props) { - console.log('Home page rendered'); - +export default function Home() { return (
- Loading...}> - + Načítání...}> + - - - - -
+ + + + + + + + + +
); } diff --git a/components/avatar-chip-manager.tsx b/components/avatar-chip-manager.tsx index 51c85a2..343e15f 100644 --- a/components/avatar-chip-manager.tsx +++ b/components/avatar-chip-manager.tsx @@ -1,8 +1,8 @@ 'use client'; import { AvatarChipFromSource } from '@promptbook/components'; -import type { BookAgent } from '../lib/book-registry'; import { useBookPersistence } from '../hooks/use-book-persistence'; +import type { BookAgent } from '../lib/book-registry'; interface AvatarChipManagerProps { agent: BookAgent; diff --git a/components/enemy-section.tsx b/components/enemy-section.tsx new file mode 100644 index 0000000..675316d --- /dev/null +++ b/components/enemy-section.tsx @@ -0,0 +1,119 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { X, Check } from 'lucide-react'; + +const comparisons = [ + { + feature: 'Když nezná odpověď', + chatgpt: 'Sebevědomě si ji vymyslí', + promptbook: 'Řekne „Nevím"', + }, + { + feature: 'Vaše firemní data', + chatgpt: 'Veřejný cloud. Kdo ví, kdo je čte', + promptbook: 'Zamčené ve vašem trezoru', + }, + { + feature: 'Trénink na vašich datech', + chatgpt: 'Ano — trénuje na nich další modely', + promptbook: 'Ne. Nikdy.', + }, + { + feature: 'Jak se ptáte', + chatgpt: '"Act as senior lawyer, temperature 0.2..."', + promptbook: '"Hele, kde je NDA z 2021?"', + }, + { + feature: 'Firemní kontext', + chatgpt: 'Žádný. Neví nic o vaší firmě', + promptbook: 'Zná vaše směrnice, smlouvy, procesy', + }, +]; + +export function EnemySection() { + return ( +
+ {/* Subtle bg */} +
+ +
+ {/* Header */} + +

+ Proč ne veřejný ChatGPT +

+

+ „Tak to hodím do ChatGPT"{' '} + + je firemní sebevražda. + +

+
+ + {/* Comparison Table */} + +
+
+ {/* Table Header */} +
+
+
+
+
+ Veřejný ChatGPT +
+
+
+
+
+ Promptbook +
+
+
+ + {/* Rows */} + {comparisons.map((row, i) => ( +
+
+ {row.feature} +
+
+
+ +
+ {row.chatgpt} +
+
+
+ +
+ {row.promptbook} +
+
+ ))} +
+
+
+
+
+ ); +} diff --git a/components/faq-section.tsx b/components/faq-section.tsx new file mode 100644 index 0000000..b534ac8 --- /dev/null +++ b/components/faq-section.tsx @@ -0,0 +1,105 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { useState } from 'react'; +import { ChevronDown } from 'lucide-react'; + +const faqs = [ + { + question: 'Nevymýšlí si to odpovědi? Jak můžu věřit AI?', + answer: 'Promptbook funguje na principu RAG — čerpá výhradně z dokumentů, které do něj sami nahrajete. Když odpověď ve vašich datech není, narovinu to přizná. Žádné halucinace, žádné sebevědomé vymýšlení. A každá odpověď obsahuje odkaz na zdrojový dokument, takže si ji snadno ověříte.', + }, + { + question: 'Je to bezpečné pro citlivá firemní data?', + answer: 'Vaše data nikdy neopustí vaši infrastrukturu. Nepoužíváme je k trénování žádných AI modelů. Jsme plně v souladu s GDPR a připravujeme certifikaci s AWS pro zpracování dat ve správné jurisdikci.', + }, + { + question: 'Musím školit zaměstnance?', + answer: 'Ne. To je celý princip Zero-Prompt. Vaši lidé jsou experti na svůj obor, ne ajťáci. Zeptají se normální češtinou — písemně nebo hlasovkou — jako by psali zprávu kolegovi. Žádné prompty, žádné školení.', + }, + { + question: 'Jak dlouho trvá nasazení?', + answer: 'Minuty, ne měsíce. Nahrajete dokumenty, vytvoříte agenta a je online. Nepotřebujete IT oddělení, API integraci ani konzultanta.', + }, + { + question: 'Nahradí to lidi ve firmě?', + answer: 'Ne. Promptbook automatizuje tu „pěnu dní" — rutinní dotazy, hledání PDFek, opakované odpovídání na to samé dokola. Vaši lidé díky tomu mají čas na smysluplnou práci a skutečnou spolupráci. Nenahrazujeme lidskou komunikaci — odstraňujeme tu její část, která všechny štve.', + }, + { + question: 'Kolik to stojí?', + answer: 'Na strategickém hovoru vám připravíme nabídku přesně na míru vaší firmě. Hovor je zdarma a nezávazný — i kdybyste se rozhodli Promptbook nepoužívat, odnesete si konkrétní strategii, jak vyřešit chaos ve firemních datech.', + }, +]; + +function FAQItem({ question, answer, index }: { question: string; answer: string; index: number }) { + const [isOpen, setIsOpen] = useState(false); + + return ( + + +
+

+ {answer} +

+
+
+ ); +} + +export function FAQSection() { + return ( +
+
+ {/* Header */} + +

+ Časté otázky +

+

+ Máte otázky?{' '} + + My máme odpovědi. + +

+

+ Na rozdíl od ChatGPT si je nevymýšlíme. +

+
+ + {/* FAQ Items */} +
+ {faqs.map((faq, i) => ( + + ))} +
+
+
+ ); +} diff --git a/components/final-cta-section.tsx b/components/final-cta-section.tsx new file mode 100644 index 0000000..af9c951 --- /dev/null +++ b/components/final-cta-section.tsx @@ -0,0 +1,87 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { ArrowRight, Shield, Clock, Gift } from 'lucide-react'; +import { Button } from '@/components/ui/button'; + +export function FinalCTASection() { + const handleCTAClick = () => { + window.dispatchEvent(new CustomEvent('open-qualification-popup')); + }; + + return ( +
+ {/* Background gradient */} +
+ {/* Noise / texture overlay */} +
+ {/* Decorative orbs */} +
+
+ +
+ + {/* Badge */} +
+ + + Nyní pro prvních 10 firem + +
+ + {/* Heading */} +

+ Přestaňte platit za hledání.
+ Začněte platit za práci. +

+ + {/* Body */} +

+ Zarezervujte si 20minutový strategický hovor s naším týmem. Žádný agresivní sales pitch — projdeme vaši konkrétní situaci a ukážeme vám Promptbook přímo na vašich firemních datech. +

+ + {/* CTA */} +
+ +
+ + {/* Trust micro-copy */} +
+
+ + Bez závazků +
+ | +
+ + 20 minut +
+ | + Ukázka na vašich firemních datech +
+ + {/* Confidentiality note */} +

+ Vše, co na hovoru probereme, zůstane důvěrné. +

+
+
+
+ ); +} diff --git a/components/header.tsx b/components/header.tsx index ca4b4ac..93c0643 100644 --- a/components/header.tsx +++ b/components/header.tsx @@ -1,52 +1,39 @@ 'use client'; import { Button } from '@/components/ui/button'; -import { WaitlistPopup } from '@/components/waitlist-popup'; -import { getLandingBehavior, getRedirectUrl } from '@/lib/landing-behavior'; -import { shouldShowWaitlist } from '@/lib/waitlist'; import { ArrowRight } from 'lucide-react'; import Image from 'next/image'; import Link from 'next/link'; -import { useRouter, useSearchParams } from 'next/navigation'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; export function Header() { - const router = useRouter(); - const searchParams = useSearchParams(); - const [showWaitlistPopup, setShowWaitlistPopup] = useState(false); + const [scrolled, setScrolled] = useState(false); - // Determine landing behavior based on URL parameters - const landingBehavior = getLandingBehavior(searchParams); + useEffect(() => { + const handleScroll = () => { + setScrolled(window.scrollY > 20); + }; + window.addEventListener('scroll', handleScroll); + return () => window.removeEventListener('scroll', handleScroll); + }, []); - const scrollToSection = (sectionId: string) => { - const element = document.getElementById(sectionId); - if (element) { - element.scrollIntoView({ behavior: 'smooth' }); - } + const handleCTAClick = () => { + // Dispatch custom event to open qualification popup + window.dispatchEvent(new CustomEvent('open-qualification-popup')); }; - const handleGetStartedClick = () => { - // Check if waitlist should be shown - if (shouldShowWaitlist(searchParams)) { - setShowWaitlistPopup(true); - return; - } - - if (landingBehavior === 'direct') { - // Direct navigation to promptbook.studio/from-social-links - const redirectUrl = getRedirectUrl('direct'); - window.location.href = redirectUrl; - } else { - // Show popup for platform selection - router.push('/get-started'); - } - }; return ( -
+
-
+
{/* Logo */} - + Promptbook - {/* Navigation */} - + {/* FOMO text — centered, hidden on small screens */} +
+ 🔥 + Zbývá 7 míst z 10 pro strategický hovor zdarma +
{/* CTA Button */}
- - {/* Waitlist Popup */} - setShowWaitlistPopup(false)} />
); } diff --git a/components/hero-section.tsx b/components/hero-section.tsx index 6c54270..d8d8bb0 100644 --- a/components/hero-section.tsx +++ b/components/hero-section.tsx @@ -1,567 +1,331 @@ 'use client'; import { Button } from '@/components/ui/button'; -import { Checkbox } from '@/components/ui/checkbox'; -import { Dialog, DialogContent, DialogHeader, DialogTitle } from '@/components/ui/dialog'; -import { Progress } from '@/components/ui/progress'; -import { WaitlistPopup } from '@/components/waitlist-popup'; -import { useYou } from '@/hooks/use-you'; -import { getLandingBehavior, getRedirectUrl } from '@/lib/landing-behavior'; -import { shouldShowWaitlist } from '@/lib/waitlist'; -import { motion } from 'framer-motion'; -import { ArrowRight, Brain, CheckCircle, Clock, Facebook, Github, Linkedin, Mail } from 'lucide-react'; -import { usePathname, useRouter, useSearchParams } from 'next/navigation'; -import { useEffect, useState } from 'react'; - -const platforms = [ - { name: 'Facebook', icon: Facebook, color: 'bg-blue-500', status: 'ready', isPreselected: true }, - { name: 'LinkedIn', icon: Linkedin, color: 'bg-blue-600', status: 'preparing', isPreselected: false }, - { name: 'GitHub', icon: Github, color: 'bg-gray-800', status: 'preparing', isPreselected: false }, - { name: 'Google', icon: Mail, color: 'bg-red-500', status: 'preparing', isPreselected: false }, +import { motion, AnimatePresence } from 'framer-motion'; +import { ArrowRight, Shield, FileText, Sparkles } from 'lucide-react'; +import { useEffect, useState, useCallback } from 'react'; + +const chatMessages = [ + { + id: 1, + type: 'user' as const, + text: 'Ahoj, jsem nová zaměstnankyně a potřebuju najít informace o dovolenkové politice firmy. Může mi někdo pomoct?', + startDelay: 0, + static: true, + }, + { + id: 2, + type: 'bot' as const, + text: 'Vítejte ve firmě, Anno! Všechny informace o dovolenkové politice najdete v naší interní znalostní bázi. Máte nárok na 25 dní dovolené ročně.', + startDelay: 2000, // 2s po loadu — bot "přemýšlí" + static: false, + }, + { + id: 3, + type: 'bot' as const, + text: 'Pošlu vám shrnutí přímo na e-mail, ať to máte po ruce. Potřebujete ještě s něčím pomoct?', + startDelay: 1200, + static: false, + }, + { + id: 4, + type: 'user' as const, + text: 'Super, děkuji moc! To je přesně to, co jsem potřebovala. 🙌', + startDelay: 1000, + static: false, + }, ]; -interface HeroSectionProps { - searchParams?: { [key: string]: string | string[] | undefined }; -} - -export function HeroSection({ searchParams = {} }: HeroSectionProps) { - const router = useRouter(); - const pathname = usePathname(); - const clientSearchParams = useSearchParams(); - const isModalOpen = pathname === '/get-started' || pathname === '/get-started/'; - - const [selectedPlatforms, setSelectedPlatforms] = useState([]); - const [isProcessing, setIsProcessing] = useState(false); - const [progress, setProgress] = useState(0); - const [shouldAnimate, setShouldAnimate] = useState(false); - const [showConfirmDialog, setShowConfirmDialog] = useState(false); - const [pendingDeselection, setPendingDeselection] = useState(null); - const [userPreferences, setUserPreferences] = useState>({}); - const [deepScrapingMode, setDeepScrapingMode] = useState(false); - const [showWaitlistPopup, setShowWaitlistPopup] = useState(false); +// Speed: ms per character +const CHAR_SPEED_USER = 25; +const CHAR_SPEED_BOT = 18; - const you = useYou(); +function TypewriterBubble({ text, type, onComplete }: { text: string; type: 'user' | 'bot'; onComplete: () => void }) { + const [displayedText, setDisplayedText] = useState(''); + const charSpeed = type === 'user' ? CHAR_SPEED_USER : CHAR_SPEED_BOT; - // Determine landing behavior based on URL parameters (use client-side search params) - const landingBehavior = getLandingBehavior(clientSearchParams); - - // Check if animations should play based on session storage useEffect(() => { - const hasSeenAnimations = sessionStorage.getItem('hero-animations-shown'); - if (!hasSeenAnimations) { - setShouldAnimate(true); - sessionStorage.setItem('hero-animations-shown', 'true'); - } - }, []); - - // Load user preferences and initialize selected platforms - useEffect(() => { - if (typeof window !== 'undefined') { - const savedPreferences = localStorage.getItem('platform-preferences'); - if (savedPreferences) { - try { - const preferences = JSON.parse(savedPreferences) as any; - setUserPreferences(preferences); - } catch (error) { - console.error('Failed to parse saved preferences:', error); - } + let i = 0; + const interval = setInterval(() => { + i++; + setDisplayedText(text.slice(0, i)); + if (i >= text.length) { + clearInterval(interval); + onComplete(); } + }, charSpeed); + return () => clearInterval(interval); + }, [text, charSpeed, onComplete]); - // Initialize selected platforms based on preselected and user preferences - const initialSelection = platforms - .filter((platform) => { - const savedPref = savedPreferences ? (JSON.parse(savedPreferences) as any)[platform.name] : null; - if (savedPref === 'deselect') return false; - if (savedPref === 'import') return true; - return platform.isPreselected; - }) - .map((p) => p.name); + return ( +
+ {displayedText} + +
+ ); +} - setSelectedPlatforms(initialSelection); - } - }, []); +function CompletedBubble({ text, type }: { text: string; type: 'user' | 'bot' }) { + return ( +
+ {text} +
+ ); +} - // Save user preferences to localStorage - const saveUserPreferences = (preferences: Record) => { - if (typeof window !== 'undefined') { - localStorage.setItem('platform-preferences', JSON.stringify(preferences)); - setUserPreferences(preferences); - } - }; +export function HeroSection() { + const [mounted, setMounted] = useState(false); + // Which message is currently typing + const [currentMessageIndex, setCurrentMessageIndex] = useState(-1); + // Which messages have finished typing + const [completedMessages, setCompletedMessages] = useState([]); + // Whether we're showing typing indicator (between messages) + const [showTypingIndicator, setShowTypingIndicator] = useState(false); - // Create dynamic hero text - const heroText = `${!you ? '' : `${you}, `}Reclaim Your Time with AI That Thinks Like You`; + // Client-only: set static messages + start animation sequence + useEffect(() => { + // Mark static messages as completed immediately + const staticIndices = chatMessages.reduce((acc, m, i) => (m.static ? [...acc, i] : acc), []); + setCompletedMessages(staticIndices); + setMounted(true); + + const firstAnimatedIndex = chatMessages.findIndex((m) => !m.static); + if (firstAnimatedIndex === -1) return; + + // Show typing indicator after 800ms + const indicatorTimer = setTimeout(() => { + setShowTypingIndicator(true); + }, 800); + + // Start typewriter after full delay (800ms + startDelay) + const typingTimer = setTimeout(() => { + setShowTypingIndicator(false); + setCurrentMessageIndex(firstAnimatedIndex); + }, 800 + chatMessages[firstAnimatedIndex].startDelay); + + return () => { + clearTimeout(indicatorTimer); + clearTimeout(typingTimer); + }; + }, []); - console.log('HeroSection rendered', { - isModalOpen, - pathname, - selectedPlatforms, - isProcessing, - progress, - you, - heroText, - }); + const handleMessageComplete = useCallback(() => { + setCompletedMessages((prev) => [...prev, currentMessageIndex]); - const togglePlatform = (platform: string) => { - console.log('Toggling platform:', platform); - const isCurrentlySelected = selectedPlatforms.includes(platform); + // Find next non-static message to animate + let nextIndex = currentMessageIndex + 1; + while (nextIndex < chatMessages.length && chatMessages[nextIndex].static) { + nextIndex++; + } - if (isCurrentlySelected) { - // Check if user has a saved preference for this platform - const savedPreference = userPreferences[platform]; - if (savedPreference) { - // User has a saved preference, apply it directly - if (savedPreference === 'deselect') { - setSelectedPlatforms((prev) => prev.filter((p) => p !== platform)); - } else { - // savedPreference === 'import', start import process - setSelectedPlatforms([platform]); - startProcessing(); - } + if (nextIndex < chatMessages.length) { + const nextMsg = chatMessages[nextIndex]; + if (nextMsg.type === 'bot') { + // Bot message: show typing indicator first + setShowTypingIndicator(true); + const timer = setTimeout(() => { + setShowTypingIndicator(false); + setCurrentMessageIndex(nextIndex); + }, nextMsg.startDelay); + return () => clearTimeout(timer); } else { - // No saved preference, show confirmation dialog - setPendingDeselection(platform); - setShowConfirmDialog(true); + // User message: no typing indicator, just a pause + const timer = setTimeout(() => { + setCurrentMessageIndex(nextIndex); + }, nextMsg.startDelay); + return () => clearTimeout(timer); } - } else { - // Adding platform - no confirmation needed - setSelectedPlatforms((prev) => [...prev, platform]); - } - }; - - const handleConfirmDeselection = (action: 'deselect' | 'import', rememberChoice: boolean) => { - if (!pendingDeselection) return; - - if (action === 'deselect') { - setSelectedPlatforms((prev) => prev.filter((p) => p !== pendingDeselection)); - } else { - // Start import with just this platform - setSelectedPlatforms([pendingDeselection]); - setTimeout(() => startProcessing(), 100); // Small delay to ensure state is updated - } - - if (rememberChoice) { - const newPreferences = { ...userPreferences, [pendingDeselection]: action }; - saveUserPreferences(newPreferences); - } - - setShowConfirmDialog(false); - setPendingDeselection(null); - }; - - const startProcessing = () => { - console.log('Starting processing with platforms:', selectedPlatforms); - - if (selectedPlatforms.length === 0) { - return; } + }, [currentMessageIndex]); - // Use the utility function to generate the redirect URL - const redirectUrl = getRedirectUrl('popup', selectedPlatforms, deepScrapingMode); - - console.log('Redirecting to:', redirectUrl); - window.location.href = redirectUrl; - - // Reset modal state by navigating back to home - router.push('/'); - setIsProcessing(false); - setProgress(0); - setSelectedPlatforms([]); + const handleCTAClick = () => { + window.dispatchEvent(new CustomEvent('open-qualification-popup')); }; return ( - <> -
- {/* Background Elements */} -
-
-
-
- -
-
- {/* Left Column - Content */} - -
-
- - Your Personal AI Avatar -
-

- {you && ( - <> - - {you} - - :{' '} - - )} - Reclaim Your{' '} - - Time - {' '} - with AI That Thinks Like{' '} - - You - -

-

- Stop spending 80% of your time on unimportant tasks. Let your AI avatar handle - emails, meetings, and routine work while you focus on what truly matters. -

+
+ {/* Background Elements */} +
+
+
+
+ +
+
+ {/* Left Column - Content */} + +
+
+ + Česká AI platforma pro firemní data
-
- if (landingBehavior === 'direct') { - // Direct navigation to promptbook.studio/from-social-links - const redirectUrl = getRedirectUrl('direct'); - console.log('Direct redirect to:', redirectUrl); - window.location.href = redirectUrl; - } else { - // Show popup for platform selection - router.push('/get-started'); - } - }} +
+ -
-
- - Open Source -
-
- - Your Data, Your Control -
-
- - Easy Setup -
-
+
- {/* Powered by Promptbook */} -
- {/* eslint-disable-next-line @next/next/no-img-element */} - Promptbook -
- Powered by - - Promptbook - - • Truly Your AI{you && <>, {you}} + {/* Trust Badges */} +
+
+ + 100% GDPR +
+ | +
+ + Až 1 000 000 normostran +
+ | +
+ + + + Česká platforma +
+
+ + + {/* Right Column - Chat Animation */} + +
+ {/* Chat Header */} +
+
+
+
+
+ + Promptbook — HR Asistent +
- - {/* Right Column - 80/20 Visualization */} - -
-

- Time Allocation Shift -

+ {/* Chat Messages */} +
+ + {chatMessages.map((msg, index) => { + const isCompleted = completedMessages.includes(index); + const isCurrentlyTyping = currentMessageIndex === index && !isCompleted; -
- {/* Before */} -
-

Before

-
- - 80% Unimportant - - - 20% - -
-

Emails • Meetings • Routine Tasks

-
+ if (!isCompleted && !isCurrentlyTyping) return null; - {/* After */} -
-

After

-
+ return ( - 20% + {isCurrentlyTyping ? ( + + ) : ( + + )} - - 80% Important - -
-
-
-
-

- Family • Creativity • Strategic Work{/* • Deep Focus */} -

-
-
-
-
-
- -
-
-
- - {/* Modal */} - { - if (!open) { - router.push('/'); - } - }} - > - - - Create Your AI Avatar - - - {!isProcessing ? ( -
-

- Select platforms to import your data and create your personalized AI avatar -

- -
- {platforms.map((platform) => { - const Icon = platform.icon; - const isSelected = selectedPlatforms.includes(platform.name); - const isReady = platform.status === 'ready'; - const isPreparing = platform.status === 'preparing'; - - return ( - isReady && togglePlatform(platform.name)} - disabled={!isReady} - className={`p-6 rounded-lg border-2 transition-all duration-300 relative ${ - !isReady - ? 'border-gray-200 bg-gray-50 cursor-not-allowed opacity-60' - : isSelected - ? 'border-primary bg-primary/5' - : 'border-gray-200 hover:border-gray-300' - }`} - > - {/* Checkbox in top right corner */} -
- + + {/* Typing indicator - show between messages */} + {showTypingIndicator && ( + +
+
+ + +
- -
-
- -
-
- - {platform.name} - - {isPreparing && ( -
- - Preparing... -
- )} -
-
- - ); - })} -
- - {/* Deep Scraping Mode Option */} -
- setDeepScrapingMode(checked as boolean)} - /> -
- -

- Performs more thorough data analysis for better avatar accuracy (takes longer) -

-
-
- - -
- ) : ( -
-
- - - -

Creating Your Avatar...

-

Analyzing your data and learning your style

+
+ + )}
-
-
- Progress - {progress}% + {/* Chat Input */} +
+
+
+ Napište dotaz... +
+
+ +
-
- -
This may take a few moments...
- )} - -
- - {/* Confirmation Dialog */} - - - - - What would you like to do with {pendingDeselection}? - - -
-

You clicked on {pendingDeselection}. Would you like to:

- -
- - - -
- -
-
- { - // Handle remember choice state if needed - }} - /> - -
- -
- - - -
-
-
-
-
- - {/* Waitlist Popup */} - setShowWaitlistPopup(false)} - /> - + {/* Decorative elements */} +
+
+ +
+
+
); } diff --git a/components/how-it-works-section.tsx b/components/how-it-works-section.tsx new file mode 100644 index 0000000..fb30274 --- /dev/null +++ b/components/how-it-works-section.tsx @@ -0,0 +1,123 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { Upload, Bot, MessageSquareText, ArrowRight } from 'lucide-react'; +import { Button } from '@/components/ui/button'; + +const steps = [ + { + number: '01', + icon: Upload, + title: 'Nahrajete dokumenty', + description: + 'Směrnice, smlouvy, manuály, NDAčka, zápisy z porad — cokoliv, co dnes leží rozházené po SharePointu, Google Disku nebo v šuplíku. Promptbook pojme až milion normostran.', + gradient: 'from-cyan-500 to-blue-500', + }, + { + number: '02', + icon: Bot, + title: 'Vytvoříte virtuálního zaměstnance', + description: + 'HR-istu, který zná pracovní řád. Právníka, který zná všechny smlouvy. Technika, který zná manuály. Každý agent odpovídá přesně podle vašich firemních dat.', + gradient: 'from-blue-500 to-indigo-500', + }, + { + number: '03', + icon: MessageSquareText, + title: 'Lidé se ptají', + description: + 'Normální češtinou. Jako by psali zprávu na WhatsApp. Nebo pošlou hlasovku. Bez promptů, bez školení, bez ajťáků.', + gradient: 'from-indigo-500 to-violet-500', + }, +]; + +export function HowItWorksSection() { + const handleCTAClick = () => { + window.dispatchEvent(new CustomEvent('open-qualification-popup')); + }; + + return ( +
+
+ {/* Section Header */} + +

+ Jak to funguje +

+

+ Nasazení,{' '} + + které nebolí. + +

+
+ + {/* Steps */} +
+ {/* Connecting line (desktop) */} +
+
+
+ + {steps.map((step, i) => ( + + {/* Step number circle */} +
+
+
+ +
+
+
+ {step.number} +
+
+ + {/* Content */} +

+ {step.title} +

+

+ {step.description} +

+
+ ))} +
+ + {/* CTA */} + + + +
+
+ ); +} diff --git a/components/minimal-footer.tsx b/components/minimal-footer.tsx new file mode 100644 index 0000000..4fd9ab8 --- /dev/null +++ b/components/minimal-footer.tsx @@ -0,0 +1,37 @@ +import Link from 'next/link'; + +export function MinimalFooter() { + return ( +
+
+
+ {/* Logo + Copyright */} +
+
+ P +
+ + © 2026 Promptbook. Všechna práva vyhrazena. + +
+ + {/* Links */} +
+ + Ochrana soukromí + + + Podmínky užití + +
+
+
+
+ ); +} diff --git a/components/pain-points-section.tsx b/components/pain-points-section.tsx new file mode 100644 index 0000000..03ef248 --- /dev/null +++ b/components/pain-points-section.tsx @@ -0,0 +1,129 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { FolderSearch, Users, ShieldAlert, UserMinus } from 'lucide-react'; + +const painPoints = [ + { + icon: FolderSearch, + title: 'Roztříštěná firemní data', + description: + 'Směrnice na SharePointu, smlouvy v e-mailech, manuály na Google Disku, procesy v hlavách lidí. Informace existují — ale jsou rozházené po desítkách systémů.', + consequence: 'Zaměstnanci tráví výraznou část dne hledáním místo práce, za kterou je platíte.', + accentColor: 'from-orange-500 to-amber-500', + bgAccent: 'from-orange-50 to-amber-50', + borderAccent: 'border-orange-200/50', + iconBg: 'bg-orange-100', + iconColor: 'text-orange-600', + }, + { + icon: Users, + title: 'Klíčoví lidé jako interní helpdesk', + description: + 'Seniorní zaměstnanci zodpovídají stále stejné dotazy od nováčků a kolegů. Místo strategické práce řeší rutinní informační servis.', + consequence: 'Vaši nejdražší lidé dělají práci, kterou by měl dělat systém.', + accentColor: 'from-blue-500 to-indigo-500', + bgAccent: 'from-blue-50 to-indigo-50', + borderAccent: 'border-blue-200/50', + iconBg: 'bg-blue-100', + iconColor: 'text-blue-600', + }, + { + icon: ShieldAlert, + title: 'Riziko veřejné AI', + description: + 'Zaměstnanci řeší pracovní úkoly přes veřejný ChatGPT — včetně citlivých firemních dokumentů. Veřejná AI nemá kontext vaší firmy a při neznalosti si odpověď domyslí.', + consequence: 'Jedno rozhodnutí na základě vymyšlené informace může stát víc než roční rozpočet na nástroje.', + accentColor: 'from-red-500 to-rose-500', + bgAccent: 'from-red-50 to-rose-50', + borderAccent: 'border-red-200/50', + iconBg: 'bg-red-100', + iconColor: 'text-red-600', + }, + { + icon: UserMinus, + title: 'Odcházející know-how', + description: + 'Když z firmy odejde zkušený člověk, odchází s ním znalosti, které nikde nejsou zdokumentované — rozhodovací procesy, kontext klientských vztahů, historické know-how.', + consequence: 'Firma přichází o roky budovanou expertízu, kterou nelze jednoduše nahradit.', + accentColor: 'from-purple-500 to-violet-500', + bgAccent: 'from-purple-50 to-violet-50', + borderAccent: 'border-purple-200/50', + iconBg: 'bg-purple-100', + iconColor: 'text-purple-600', + }, +]; + +export function PainPointsSection() { + return ( +
+ {/* Subtle background orbs */} +
+
+ +
+ {/* Section Header */} + +

+ Proč firmy ztrácejí miliony +

+

+ Znalosti ve firmě existují.{' '} + + Problém je, že je nikdo nenajde. + +

+
+ + {/* Pain Point Cards — 2x2 Grid */} +
+ {painPoints.map((point, i) => ( + + {/* Top accent line */} +
+ +
+ {/* Icon */} +
+ +
+ + {/* Content */} +
+

+ {point.title} +

+

+ {point.description} +

+ {/* Consequence tag */} +
+ + → {point.consequence} + +
+
+
+
+ ))} +
+
+
+ ); +} diff --git a/components/qualification-popup.tsx b/components/qualification-popup.tsx new file mode 100644 index 0000000..9c32173 --- /dev/null +++ b/components/qualification-popup.tsx @@ -0,0 +1,284 @@ +'use client'; + +import { Dialog, DialogContent } from '@/components/ui/dialog'; +import { Button } from '@/components/ui/button'; +import { ArrowRight, ArrowLeft, CheckCircle2, Send, X } from 'lucide-react'; +import { useState, useEffect } from 'react'; + +interface Question { + id: string; + question: string; + type: 'single' | 'text' | 'email'; + options?: string[]; + placeholder?: string; + required?: boolean; +} + +const questions: Question[] = [ + { + id: 'company_size', + question: 'Kolik má vaše firma zaměstnanců?', + type: 'single', + options: ['1–10', '11–50', '51–200', '201–1000', '1000+'], + }, + { + id: 'industry', + question: 'V jakém oboru působíte?', + type: 'single', + options: ['Výroba / Průmysl', 'Právo / Advokacie', 'Stavebnictví', 'Veřejná správa', 'Zdravotnictví', 'IT / Technologie', 'Finance / Bankovnictví', 'Vzdělávání', 'Jiné'], + }, + { + id: 'pain_point', + question: 'Co vás nejvíc trápí?', + type: 'single', + options: [ + 'Zaměstnanci nedokáží najít interní informace', + 'Klíčoví lidé tráví čas odpovídáním na rutinní dotazy', + 'Obavy z používání veřejného ChatGPT na firemní data', + 'Odcházejí nám zkušení lidé a jejich know-how s nimi', + 'Chci prostě vědět, co Promptbook umí', + ], + }, + { + id: 'name', + question: 'Jak vám říkáte?', + type: 'text', + placeholder: 'Vaše jméno', + required: true, + }, + { + id: 'email', + question: 'Kam vám pošleme potvrzení?', + type: 'email', + placeholder: 'vas@email.cz', + required: true, + }, +]; + +export function QualificationPopup() { + const [isOpen, setIsOpen] = useState(false); + const [currentStep, setCurrentStep] = useState(0); + const [answers, setAnswers] = useState>({}); + const [isSubmitted, setIsSubmitted] = useState(false); + const [isSubmitting, setIsSubmitting] = useState(false); + + // Listen for open-waitlist event + useEffect(() => { + const handleOpen = () => setIsOpen(true); + window.addEventListener('open-qualification-popup', handleOpen); + return () => window.removeEventListener('open-qualification-popup', handleOpen); + }, []); + + const currentQuestion = questions[currentStep]; + const totalSteps = questions.length; + const progress = ((currentStep + 1) / totalSteps) * 100; + + const canProceed = () => { + const answer = answers[currentQuestion.id]; + if (currentQuestion.required && !answer) return false; + if (currentQuestion.type === 'email' && answer) { + return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(answer); + } + return !!answer; + }; + + const handleNext = () => { + if (currentStep < totalSteps - 1) { + setCurrentStep((s) => s + 1); + } else { + handleSubmit(); + } + }; + + const handleBack = () => { + if (currentStep > 0) { + setCurrentStep((s) => s - 1); + } + }; + + const handleOptionSelect = (option: string) => { + setAnswers((prev) => ({ ...prev, [currentQuestion.id]: option })); + // Auto-advance for single-select after a brief delay + setTimeout(() => { + if (currentStep < totalSteps - 1) { + setCurrentStep((s) => s + 1); + } + }, 300); + }; + + const handleSubmit = async () => { + setIsSubmitting(true); + // Simulate submission — replace with actual API call + await new Promise((resolve) => setTimeout(resolve, 1000)); + setIsSubmitting(false); + setIsSubmitted(true); + }; + + const handleClose = () => { + setIsOpen(false); + // Reset after animation + setTimeout(() => { + setCurrentStep(0); + setAnswers({}); + setIsSubmitted(false); + }, 300); + }; + + return ( + + + {/* Progress bar */} + {!isSubmitted && ( +
+
+
+ )} + + {isSubmitted ? ( + /* Success State */ +
+
+ +
+

+ Skvělé, ozveme se! +

+

+ Na {answers.email} vám do 24 hodin pošleme odkaz na rezervaci strategického hovoru. +

+

+ Díky za důvěru, {answers.name}. Připravíme se na váš obor. +

+ +
+ ) : ( + /* Question Steps */ +
+ {/* Step indicator + close */} +
+ + Krok {currentStep + 1} z {totalSteps} + + +
+ + {/* Question */} +

+ {currentQuestion.question} +

+ + {/* Single select options */} + {currentQuestion.type === 'single' && currentQuestion.options && ( +
+ {currentQuestion.options.map((option) => ( + + ))} +
+ )} + + {/* Text input */} + {currentQuestion.type === 'text' && ( + + setAnswers((prev) => ({ + ...prev, + [currentQuestion.id]: e.target.value, + })) + } + placeholder={currentQuestion.placeholder} + className="w-full px-5 py-4 rounded-xl border border-gray-200 text-[16px] text-[#0f172a] placeholder:text-gray-300 focus:outline-none focus:border-[#0891b2] focus:ring-2 focus:ring-cyan-100 transition-all duration-200" + autoFocus + /> + )} + + {/* Email input */} + {currentQuestion.type === 'email' && ( + + setAnswers((prev) => ({ + ...prev, + [currentQuestion.id]: e.target.value, + })) + } + placeholder={currentQuestion.placeholder} + className="w-full px-5 py-4 rounded-xl border border-gray-200 text-[16px] text-[#0f172a] placeholder:text-gray-300 focus:outline-none focus:border-[#0891b2] focus:ring-2 focus:ring-cyan-100 transition-all duration-200" + autoFocus + /> + )} + + {/* Navigation */} +
+ + + {/* Show Next/Submit only for text/email fields */} + {(currentQuestion.type === 'text' || currentQuestion.type === 'email') && ( + + )} +
+ + {/* Privacy note on last step */} + {currentStep === totalSteps - 1 && ( +

+ Vaše data zpracováváme v souladu s GDPR. Žádný spam. +

+ )} +
+ )} +
+
+ ); +} diff --git a/components/social-proof-strip.tsx b/components/social-proof-strip.tsx new file mode 100644 index 0000000..2dc04f6 --- /dev/null +++ b/components/social-proof-strip.tsx @@ -0,0 +1,53 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { Building2, Scale, HardHat, Landmark, Stethoscope, GraduationCap } from 'lucide-react'; + +const industries = [ + { icon: Building2, label: 'Výrobní firmy' }, + { icon: Scale, label: 'Advokátní kanceláře' }, + { icon: HardHat, label: 'Stavební firmy' }, + { icon: Landmark, label: 'Veřejná správa' }, + { icon: Stethoscope, label: 'Zdravotnictví' }, + { icon: GraduationCap, label: 'Vzdělávání' }, +]; + +export function SocialProofStrip() { + return ( +
+
+ +

+ Navrženo pro firmy, které berou svá data vážně +

+ +
+ {industries.map((industry, i) => ( + +
+ +
+ + {industry.label} + +
+ ))} +
+
+
+
+ ); +} diff --git a/components/solution-section.tsx b/components/solution-section.tsx new file mode 100644 index 0000000..b7e9e3e --- /dev/null +++ b/components/solution-section.tsx @@ -0,0 +1,106 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { MessageCircle, Lock, ShieldCheck } from 'lucide-react'; + +const benefits = [ + { + icon: MessageCircle, + title: 'Zero-Prompt', + description: + 'Žádné školení. Žádné „napiš prompt jako ajťák". Vaši zaměstnanci jsou experti na svůj obor, ne na zaříkávání robotů. Prostě se zeptají — písemně nebo hlasovkou — a dostanou odpověď.', + highlight: 'Tak, jak jsou zvyklí komunikovat s lidmi.', + gradient: 'from-cyan-500 to-blue-500', + iconBg: 'bg-gradient-to-br from-cyan-100 to-blue-100', + iconColor: 'text-cyan-600', + }, + { + icon: Lock, + title: 'Kontextový trezor', + description: + 'Data nikdy neopustí vaši infrastrukturu. Nepoužíváme je na trénování žádných modelů. Víte, co se stane, když zaměstnanec zkopíruje NDA do veřejného ChatGPT?', + highlight: 'My taky. Proto jsme to udělali jinak.', + gradient: 'from-emerald-500 to-teal-500', + iconBg: 'bg-gradient-to-br from-emerald-100 to-teal-100', + iconColor: 'text-emerald-600', + }, + { + icon: ShieldCheck, + title: '„Nevím" je lepší než halucinace', + description: + 'Veřejná AI si vymyslí pět odstavců, které zní důvěryhodně — a mohou vás stát firmu. Promptbook čerpá výhradně z vašich dat. A když odpověď nenajde?', + highlight: 'Narovinu řekne: „Tuto informaci ve vašich dokumentech nemám."', + gradient: 'from-violet-500 to-purple-500', + iconBg: 'bg-gradient-to-br from-violet-100 to-purple-100', + iconColor: 'text-violet-600', + }, +]; + +export function SolutionSection() { + return ( +
+ {/* Background */} +
+ +
+ {/* Section Header */} + +

+ Řešení +

+

+ Virtuální zaměstnanec, který{' '} + + zná celou firmu. + +

+

+ Nahrajte firemní dokumenty do bezpečného trezoru. Promptbook z nich vytvoří virtuálního zaměstnance — HR-istu, právníka, technika — kterého se kdokoliv zeptá normální češtinou. +

+
+ + {/* 3 Benefit Cards */} +
+ {benefits.map((benefit, i) => ( + + {/* Top gradient line */} +
+ + {/* Icon */} +
+ +
+ + {/* Content */} +

+ {benefit.title} +

+

+ {benefit.description} +

+

+ {benefit.highlight} +

+
+ ))} +
+
+
+ ); +} diff --git a/components/testimonials-section.tsx b/components/testimonials-section.tsx new file mode 100644 index 0000000..f48f97e --- /dev/null +++ b/components/testimonials-section.tsx @@ -0,0 +1,111 @@ +'use client'; + +import { motion } from 'framer-motion'; +import { Quote, BookOpen, Building2, Users } from 'lucide-react'; + +const testimonials = [ + { + quote: 'Promptbook nás od sebe neodstřihl. Naopak — konečně máme čas řešit opravdovou práci, za kterou jsme placeni.', + author: 'IT oddělení', + company: 'Slezská univerzita v Opavě', + icon: BookOpen, + }, + { + quote: 'Nováčci se už nemusí bát zeptat. Mají odpovědi okamžitě a přesně podle našich interních směrnic.', + author: 'Městská část', + company: 'Praha 13', + icon: Building2, + }, +]; + +const metrics = [ + { value: '1 000 000', label: 'normostran kapacity', suffix: '' }, + { value: '100%', label: 'GDPR compliance', suffix: '' }, + { value: '0', label: 'halucinací', suffix: '' }, +]; + +export function TestimonialsSection() { + return ( +
+
+ {/* Header */} + +

+ Reference +

+

+ Co říkají firmy, které{' '} + + přestaly hledat. + +

+
+ + {/* Testimonial Cards */} +
+ {testimonials.map((testimonial, i) => ( + + {/* Quote icon */} +
+ +
+ + {/* Quote text */} +
+ „{testimonial.quote}" +
+ + {/* Author */} +
+
+ +
+
+

{testimonial.company}

+

{testimonial.author}

+
+
+
+ ))} +
+ + {/* Metrics Strip */} + + {metrics.map((metric, i) => ( +
+
+ {metric.value} +
+

{metric.label}

+
+ ))} +
+
+
+ ); +} diff --git a/components/ui/accordion.tsx b/components/ui/accordion.tsx index 84bf2eb..b56fc7c 100644 --- a/components/ui/accordion.tsx +++ b/components/ui/accordion.tsx @@ -1,8 +1,8 @@ 'use client'; -import * as React from 'react'; import * as AccordionPrimitive from '@radix-ui/react-accordion'; import { ChevronDown } from 'lucide-react'; +import * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/components/ui/alert-dialog.tsx b/components/ui/alert-dialog.tsx index 5cba559..26a32ae 100644 --- a/components/ui/alert-dialog.tsx +++ b/components/ui/alert-dialog.tsx @@ -1,10 +1,10 @@ 'use client'; -import * as React from 'react'; import * as AlertDialogPrimitive from '@radix-ui/react-alert-dialog'; +import * as React from 'react'; -import { cn } from '@/lib/utils'; import { buttonVariants } from '@/components/ui/button'; +import { cn } from '@/lib/utils'; const AlertDialog = AlertDialogPrimitive.Root; diff --git a/components/ui/alert.tsx b/components/ui/alert.tsx index d2b59cc..7a1c959 100644 --- a/components/ui/alert.tsx +++ b/components/ui/alert.tsx @@ -1,5 +1,5 @@ -import * as React from 'react'; import { cva, type VariantProps } from 'class-variance-authority'; +import * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/components/ui/avatar.tsx b/components/ui/avatar.tsx index 1346957..31b6f8a 100644 --- a/components/ui/avatar.tsx +++ b/components/ui/avatar.tsx @@ -1,7 +1,7 @@ 'use client'; -import * as React from 'react'; import * as AvatarPrimitive from '@radix-ui/react-avatar'; +import * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/components/ui/badge.tsx b/components/ui/badge.tsx index 2eb790a..b68b31e 100644 --- a/components/ui/badge.tsx +++ b/components/ui/badge.tsx @@ -1,5 +1,5 @@ -import * as React from 'react'; import { cva, type VariantProps } from 'class-variance-authority'; +import * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/components/ui/breadcrumb.tsx b/components/ui/breadcrumb.tsx index 8b62197..5830eed 100644 --- a/components/ui/breadcrumb.tsx +++ b/components/ui/breadcrumb.tsx @@ -1,6 +1,6 @@ -import * as React from 'react'; import { Slot } from '@radix-ui/react-slot'; import { ChevronRight, MoreHorizontal } from 'lucide-react'; +import * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/components/ui/button.tsx b/components/ui/button.tsx index 81e2e6e..3f6bead 100644 --- a/components/ui/button.tsx +++ b/components/ui/button.tsx @@ -1,6 +1,6 @@ -import * as React from 'react'; import { Slot } from '@radix-ui/react-slot'; import { cva, type VariantProps } from 'class-variance-authority'; +import * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/components/ui/calendar.tsx b/components/ui/calendar.tsx index 7ee4f82..7a7bd84 100644 --- a/components/ui/calendar.tsx +++ b/components/ui/calendar.tsx @@ -1,11 +1,11 @@ 'use client'; -import * as React from 'react'; import { ChevronLeft, ChevronRight } from 'lucide-react'; +import * as React from 'react'; import { DayPicker } from 'react-day-picker'; -import { cn } from '@/lib/utils'; import { buttonVariants } from '@/components/ui/button'; +import { cn } from '@/lib/utils'; export type CalendarProps = React.ComponentProps; diff --git a/components/ui/carousel.tsx b/components/ui/carousel.tsx index f689200..1798fbd 100644 --- a/components/ui/carousel.tsx +++ b/components/ui/carousel.tsx @@ -1,13 +1,13 @@ 'use client'; -import * as React from 'react'; import useEmblaCarousel, { - type UseEmblaCarouselType, + type UseEmblaCarouselType } from 'embla-carousel-react'; import { ArrowLeft, ArrowRight } from 'lucide-react'; +import * as React from 'react'; -import { cn } from '@/lib/utils'; import { Button } from '@/components/ui/button'; +import { cn } from '@/lib/utils'; type CarouselApi = UseEmblaCarouselType[1]; type UseCarouselParameters = Parameters; diff --git a/components/ui/checkbox.tsx b/components/ui/checkbox.tsx index 13a6a29..256b6b9 100644 --- a/components/ui/checkbox.tsx +++ b/components/ui/checkbox.tsx @@ -1,8 +1,8 @@ 'use client'; -import * as React from 'react'; import * as CheckboxPrimitive from '@radix-ui/react-checkbox'; import { Check } from 'lucide-react'; +import * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/components/ui/command.tsx b/components/ui/command.tsx index 5b9ca50..c44393b 100644 --- a/components/ui/command.tsx +++ b/components/ui/command.tsx @@ -1,12 +1,12 @@ 'use client'; -import * as React from 'react'; import { type DialogProps } from '@radix-ui/react-dialog'; import { Command as CommandPrimitive } from 'cmdk'; import { Search } from 'lucide-react'; +import * as React from 'react'; -import { cn } from '@/lib/utils'; import { Dialog, DialogContent } from '@/components/ui/dialog'; +import { cn } from '@/lib/utils'; const Command = React.forwardRef< React.ElementRef, diff --git a/components/ui/context-menu.tsx b/components/ui/context-menu.tsx index 1046ab8..9682801 100644 --- a/components/ui/context-menu.tsx +++ b/components/ui/context-menu.tsx @@ -1,8 +1,8 @@ 'use client'; -import * as React from 'react'; import * as ContextMenuPrimitive from '@radix-ui/react-context-menu'; import { Check, ChevronRight, Circle } from 'lucide-react'; +import * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/components/ui/dialog.tsx b/components/ui/dialog.tsx index b552952..00c82cc 100644 --- a/components/ui/dialog.tsx +++ b/components/ui/dialog.tsx @@ -1,8 +1,8 @@ 'use client'; -import * as React from 'react'; import * as DialogPrimitive from '@radix-ui/react-dialog'; import { X } from 'lucide-react'; +import * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/components/ui/dropdown-menu.tsx b/components/ui/dropdown-menu.tsx index 283467c..9963513 100644 --- a/components/ui/dropdown-menu.tsx +++ b/components/ui/dropdown-menu.tsx @@ -1,8 +1,8 @@ 'use client'; -import * as React from 'react'; import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'; import { Check, ChevronRight, Circle } from 'lucide-react'; +import * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/components/ui/form.tsx b/components/ui/form.tsx index b9fb860..d0dc90e 100644 --- a/components/ui/form.tsx +++ b/components/ui/form.tsx @@ -1,19 +1,19 @@ 'use client'; -import * as React from 'react'; import * as LabelPrimitive from '@radix-ui/react-label'; import { Slot } from '@radix-ui/react-slot'; +import * as React from 'react'; import { Controller, ControllerProps, FieldPath, FieldValues, FormProvider, - useFormContext, + useFormContext } from 'react-hook-form'; -import { cn } from '@/lib/utils'; import { Label } from '@/components/ui/label'; +import { cn } from '@/lib/utils'; const Form = FormProvider; diff --git a/components/ui/hover-card.tsx b/components/ui/hover-card.tsx index 1c9816e..98535e6 100644 --- a/components/ui/hover-card.tsx +++ b/components/ui/hover-card.tsx @@ -1,7 +1,7 @@ 'use client'; -import * as React from 'react'; import * as HoverCardPrimitive from '@radix-ui/react-hover-card'; +import * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/components/ui/input-otp.tsx b/components/ui/input-otp.tsx index e7f1585..70c385a 100644 --- a/components/ui/input-otp.tsx +++ b/components/ui/input-otp.tsx @@ -1,8 +1,8 @@ 'use client'; -import * as React from 'react'; import { OTPInput, OTPInputContext } from 'input-otp'; import { Dot } from 'lucide-react'; +import * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/components/ui/label.tsx b/components/ui/label.tsx index 1e24ec0..ba5dc13 100644 --- a/components/ui/label.tsx +++ b/components/ui/label.tsx @@ -1,8 +1,8 @@ 'use client'; -import * as React from 'react'; import * as LabelPrimitive from '@radix-ui/react-label'; import { cva, type VariantProps } from 'class-variance-authority'; +import * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/components/ui/menubar.tsx b/components/ui/menubar.tsx index 045ad98..d73255c 100644 --- a/components/ui/menubar.tsx +++ b/components/ui/menubar.tsx @@ -1,8 +1,8 @@ 'use client'; -import * as React from 'react'; import * as MenubarPrimitive from '@radix-ui/react-menubar'; import { Check, ChevronRight, Circle } from 'lucide-react'; +import * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/components/ui/navigation-menu.tsx b/components/ui/navigation-menu.tsx index 97ec318..dbcbd71 100644 --- a/components/ui/navigation-menu.tsx +++ b/components/ui/navigation-menu.tsx @@ -1,7 +1,7 @@ -import * as React from 'react'; import * as NavigationMenuPrimitive from '@radix-ui/react-navigation-menu'; import { cva } from 'class-variance-authority'; import { ChevronDown } from 'lucide-react'; +import * as React from 'react'; import { cn } from '@/lib/utils'; diff --git a/components/ui/pagination.tsx b/components/ui/pagination.tsx index 7462acc..b200d1f 100644 --- a/components/ui/pagination.tsx +++ b/components/ui/pagination.tsx @@ -1,8 +1,8 @@ -import * as React from 'react'; import { ChevronLeft, ChevronRight, MoreHorizontal } from 'lucide-react'; +import * as React from 'react'; -import { cn } from '@/lib/utils'; import { ButtonProps, buttonVariants } from '@/components/ui/button'; +import { cn } from '@/lib/utils'; const Pagination = ({ className, ...props }: React.ComponentProps<'nav'>) => (