diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..17027d6 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "plugins": ["prettier-plugin-tailwindcss"], + + "trailingComma": "es5" +} diff --git a/app/globals.css b/app/globals.css index a2dc41e..5c46589 100644 --- a/app/globals.css +++ b/app/globals.css @@ -1,26 +1,154 @@ @import "tailwindcss"; - -:root { - --background: #ffffff; - --foreground: #171717; +@font-face { + font-family: "Switzer Variable"; + src: url("/font/Switzer-Variable.ttf") format("truetype"); + font-weight: 100 900; /* Full variable range */ + font-style: normal; + font-display: swap; +} +@font-face { + font-family: "Mium Regular"; + src: url("/font/miumAccess-Regular.ttf") format("truetype"); + font-weight: 300; /* Full variable range */ + font-style: normal; + font-display: swap; +} +@font-face { + font-family: "Playfair Display"; + src: url("/font/PlayfairDisplay-Italic-VariableFont_wght.ttf") + format("truetype"); + font-weight: 300; /* Full variable range */ + font-style: normal; + font-display: swap; } - @theme inline { - --color-background: var(--background); - --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); - --font-mono: var(--font-geist-mono); + --font-switzer: "Switzer Variable", system-ui, sans-serif; + --font-mium-reg: "Mium Regular", system-ui, sans-serif; + --font-playfair: "Playfair Display", Georgia, serif; + + /* === HEADINGS === */ + --text-h-1-96: 96px; + --text-h-2-60: 60px; + --text-h-3-40: 40px; + --text-h-4-34: 34px; + --text-h-5-28: 28px; + --text-h-6-38: 38px; + --text-h-7-24: 24px; + --text-h-mobile-30: 30px; + --text-hero-mobile: 56px; + + --leading-h1: 90px; + --leading-h2: 63px; + --leading-h3: 48px; + --leading-h4: 42px; + --leading-h5: 30px; + --leading-mobile: 38px; + + /* === BODY TEXT === */ + --text-b-1-20: 20px; + --text-b-2-17: 17px; + --text-b-3-16: 16px; + --text-b-4-14: 14px; + + --leading-b1: 25px; + --leading-b2: 26px; + --leading-b3: 23px; + --leading-b4: 16px; + + --color-background: #f8f8ff; + --color-light-100: #d5ffdc; + --color-light-200: #ecf9db; + --color-light-300: #d5ffdc33; + --color-brand: #274b2d; + --color-hover: #2f5a36; + --color-btn: #274b2d; + + --color-border: #3a6f43; + + --color-secondary: #636369; + --color-gray: #423f3f; + + --breakpoint-xl: 1600px; + --breakpoint-lg: 1400px; + --breakpoint-md: 1024px; + --breakpoint-tab: 767px; + --breakpoint-sm: 320px; } -@media (prefers-color-scheme: dark) { - :root { - --background: #0a0a0a; - --foreground: #ededed; +@keyframes marquee { + from { + transform: translateX(0); + } + to { + transform: translateX(calc(-100% - var(--gap))); } } -body { - background: var(--background); - color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; +/* Base marquee container */ +.marquee { + display: flex; + gap: var(--gap); + animation: marquee var(--duration) linear infinite; +} + +@keyframes marquee { + from { + transform: translateX(0); + } + to { + transform: translateX(-50%); + } +} + +.marquee { + display: flex; + gap: var(--gap); + animation: marquee var(--duration) linear infinite; +} + +/* Reverse direction support */ +.marquee.reverse { + animation-direction: reverse; +} + +/* Pause on hover */ +.group:hover .marquee.pause { + animation-play-state: paused; +} +/* === PAGE TRANSITION FIX === */ + +.overlay { + grid-area: 1 / 1 / 2 / 2; + position: fixed; /* instead of relative — ensures full viewport coverage */ + top: 0; + left: 0; + width: 100vw; /* use viewport units explicitly */ + height: 100vh; + z-index: 1000; + pointer-events: none; +} + +.overlay__path { + fill: var(--color-brand, #274b2d); + stroke: none; + vector-effect: non-scaling-stroke; +} + +.view { + position: relative; + grid-area: 1 / 1 / 2 / 2; + display: grid; + place-items: center; +} + +.view--2 { + background: var(--color-bg-view-2, #cbb37e); + pointer-events: none; + opacity: 0; + transition: opacity 0.5s ease; +} + +.view--open { + pointer-events: auto; + opacity: 1; } diff --git a/app/layout.tsx b/app/layout.tsx index f7fa87e..73e3096 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,20 +1,42 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; +import { Playfair_Display } from "next/font/google"; import "./globals.css"; - -const geistSans = Geist({ - variable: "--font-geist-sans", - subsets: ["latin"], -}); - -const geistMono = Geist_Mono({ - variable: "--font-geist-mono", - subsets: ["latin"], -}); +import SmoothScrollProvider from "@/lib/useLenisScroll"; +import { LayoutWithTransition } from "@/layout/Layout"; export const metadata: Metadata = { - title: "Create Next App", - description: "Generated by create next app", + title: + "Whispering Tree | Smart IoT & AI Solutions for Tree Health Monitoring", + description: + "Whispering Tree combines IoT sensors and AI analytics to monitor the health of trees in real time. Our smart environmental system helps cities, organizations, and individuals optimize irrigation, detect stress early, and promote sustainable growth through data-driven insights.", + keywords: [ + "Whispering Tree", + "tree health monitoring", + "IoT for environment", + "AI tree analytics", + "smart irrigation", + "urban forestry technology", + "environmental intelligence", + "FalktronTrees", + "Matthias Mut", + "AI for sustainability", + "smart city solutions", + "real-time tree monitoring", + "IoT sensors for trees", + "green technology", + "water optimization system", + ], + openGraph: { + title: + "Whispering Tree | Smart IoT & AI Solutions for Tree Health Monitoring", + description: + "Whispering Tree combines IoT sensors and AI analytics to monitor the health of trees in real time. Our smart environmental system helps cities, organizations, and individuals optimize irrigation, detect stress early, and promote sustainable growth through data-driven insights.", + url: "https://whisperingtree.com", + type: "website", + }, + icons: { + icon: "/fav.svg", + }, }; export default function RootLayout({ @@ -24,10 +46,10 @@ export default function RootLayout({ }>) { return ( - - {children} + + + {children} + ); diff --git a/app/page.tsx b/app/page.tsx index 295f8fd..0fd0cbb 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,65 +1,34 @@ -import Image from "next/image"; +"use client"; +import Footer from "@/components/shared/Footer"; +import FAQ from "@/sections/FAQ"; +import About from "@/sections/About"; +import Benefit from "@/sections/Benefit"; +import CaseStudy from "@/sections/CaseStudy"; +import CTA from "@/sections/CTA"; +import Hero from "@/sections/Hero"; +import HowWork from "@/sections/HowWork"; +import ProductPreview from "@/sections/ProductPreview"; +import SocialProff from "@/sections/SocialProff"; +import Testimonial from "@/sections/Testimonial"; export default function Home() { return ( -
-
- Next.js logo -
-

- To get started, edit the page.tsx file. -

-

- Looking for a starting point or more instructions? Head over to{" "} - - Templates - {" "} - or the{" "} - - Learning - {" "} - center. -

-
-
- - Vercel logomark - Deploy Now - - - Documentation - -
-
-
+ <> +
+ {/* */} + + + + + + + + + + + +
+ ); } diff --git a/app/sitemap.xml/route.ts b/app/sitemap.xml/route.ts new file mode 100644 index 0000000..c4bffb1 --- /dev/null +++ b/app/sitemap.xml/route.ts @@ -0,0 +1,46 @@ +// app/sitemap/route.ts + +interface SitemapPage { + loc: string; + lastmod: string; +} + +export const GET = async () => { + const BASE_URL = "https://trees.falktron.com"; + + const useCases = [ + "forestry", + "research", + "agriculture", + "cities-communities", + ]; + + const pages: SitemapPage[] = [ + { loc: `${BASE_URL}/`, lastmod: new Date().toISOString().split("T")[0] }, + ...useCases.map((slug) => ({ + loc: `${BASE_URL}/use-cases/${slug}`, + lastmod: new Date().toISOString().split("T")[0], + })), + ]; + + const xml = ` + + ${pages + .map( + (page) => ` + + ${page.loc} + ${page.lastmod} + ` + ) + .join("")} + `; + + return new Response(xml, { + status: 200, + headers: { + "Content-Type": "application/xml", + "Cache-Control": "public, s-maxage=86400, stale-while-revalidate=3600", + }, + }); +}; diff --git a/app/use-cases/[slug]/page.tsx b/app/use-cases/[slug]/page.tsx new file mode 100644 index 0000000..51329ba --- /dev/null +++ b/app/use-cases/[slug]/page.tsx @@ -0,0 +1,83 @@ +import Image from "next/image"; +import { notFound } from "next/navigation"; +import { useCases } from "@/data/useCases"; +import Footer from "@/components/shared/Footer"; +import Navbar from "@/components/shared/Navbar"; +import MobileNavbar from "@/components/ui/mobile-navbar"; +import CTA from "@/sections/CTA"; +import HeroClient from "@/sections/HeroCase"; + +interface Props { + params: Promise<{ slug: string }>; // ← Promise! +} + +export default async function UseCaseDetail({ params }: Props) { + const { slug } = await params; + const item = useCases.find((c) => c.slug === slug); + if (!item) notFound(); + + return ( + <> + + + +
+
+
+

+ Overview +

+

+ {item.description} +

+
+ +
+

+ Challenge +

+

+ {item.challenge} +

+
+ {`${item.title} + +
+

+ Our Solution +

+

+ {item.solution} +

+
+ +
+

+ Impact +

+

+ {item.outcome} +

+
+ {`${item.title} +
+
+
+ +
+