From 0574abb63fb3e14df1ef644defbb802de06788d1 Mon Sep 17 00:00:00 2001 From: DK Date: Thu, 22 Jan 2026 17:13:24 +0000 Subject: [PATCH] Initial commit --- .env | 2 + .gitea/workflows/build.yml | 57 ++ src/app/blog/page.tsx | 134 ++++ src/app/globals.css | 595 +++++++++++++++++ src/app/layout.tsx | 1264 ++++++++++++++++++++++++++++++++++++ src/app/page.tsx | 235 +++++++ vercel.json | 5 + 7 files changed, 2292 insertions(+) create mode 100644 .env create mode 100644 .gitea/workflows/build.yml create mode 100644 src/app/blog/page.tsx create mode 100644 src/app/globals.css create mode 100644 src/app/layout.tsx create mode 100644 src/app/page.tsx create mode 100644 vercel.json diff --git a/.env b/.env new file mode 100644 index 0000000..9df1d48 --- /dev/null +++ b/.env @@ -0,0 +1,2 @@ +NEXT_PUBLIC_API_URL=https://dev.api.webild.io +NEXT_PUBLIC_PROJECT_ID=26080950-9687-45e7-9e06-b9d561b202c2 \ No newline at end of file diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..d372c5f --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,57 @@ +name: Code Check + +on: + workflow_dispatch: + inputs: + branch: + description: 'Branch to check' + required: true + default: 'main' + +permissions: + contents: read + +jobs: + check: + runs-on: ubuntu-latest + timeout-minutes: 10 + + steps: + - name: Checkout branch + uses: actions/checkout@v3 + with: + ref: ${{ gitea.event.inputs.branch }} + + - name: Setup Node.js + uses: actions/setup-node@v3 + with: + node-version: 24 + + - name: Install dependencies + run: | + set -euo pipefail + npm ci --prefer-offline --no-audit 2>&1 | tee install.log + env: + NODE_OPTIONS: '--max-old-space-size=4096' + + - name: TypeScript check + run: | + set -euo pipefail + npm run typecheck 2>&1 | tee build.log + + - name: ESLint check + run: | + set -euo pipefail + npm run lint 2>&1 | tee build.log + + - name: Upload build log on failure + if: failure() + uses: actions/upload-artifact@v3 + with: + name: build-log + path: build.log + if-no-files-found: ignore + + - name: Check completed + if: success() + run: echo "Typecheck and lint passed successfully" diff --git a/src/app/blog/page.tsx b/src/app/blog/page.tsx new file mode 100644 index 0000000..29473bf --- /dev/null +++ b/src/app/blog/page.tsx @@ -0,0 +1,134 @@ +"use client"; + +import { useEffect, useState } from "react"; +import ReactLenis from "lenis/react"; +import BlogCardTwo from '@/components/sections/blog/BlogCardTwo'; +import FooterLogoReveal from '@/components/sections/footer/FooterLogoReveal'; +import { ThemeProvider } from "@/providers/themeProvider/ThemeProvider"; +import NavbarStyleApple from '@/components/navbar/NavbarStyleApple/NavbarStyleApple'; + +type BlogPost = { + id: string; + category: string; + title: string; + excerpt: string; + imageSrc: string; + imageAlt?: string; + authorName: string; + authorAvatar: string; + date: string; + onBlogClick?: () => void; +}; + +const defaultPosts: BlogPost[] = [ + { + id: "1", category: "Design", title: "UX review presentations", excerpt: "How do you create compelling presentations that wow your colleagues and impress your managers?", imageSrc: "/placeholders/placeholder3.avif", imageAlt: "Abstract design with purple and silver tones", authorName: "Olivia Rhye", authorAvatar: "/placeholders/placeholder3.avif", date: "20 Jan 2025", onBlogClick: () => console.log("Blog 1 clicked"), + }, + { + id: "2", category: "Development", title: "Building scalable applications", excerpt: "Learn the best practices for building applications that can handle millions of users.", imageSrc: "/placeholders/placeholder4.webp", imageAlt: "Development workspace", authorName: "John Smith", authorAvatar: "/placeholders/placeholder4.webp", date: "18 Jan 2025", onBlogClick: () => console.log("Blog 2 clicked"), + }, + { + id: "3", category: "Marketing", title: "Content strategy essentials", excerpt: "Discover how to create a content strategy that drives engagement and conversions.", imageSrc: "/placeholders/placeholder3.avif", imageAlt: "Marketing strategy board", authorName: "Sarah Johnson", authorAvatar: "/placeholders/placeholder3.avif", date: "15 Jan 2025", onBlogClick: () => console.log("Blog 3 clicked"), + }, + { + id: "4", category: "Product", title: "Product management 101", excerpt: "Everything you need to know to become an effective product manager in 2025.", imageSrc: "/placeholders/placeholder4.webp", imageAlt: "Product planning session", authorName: "Mike Davis", authorAvatar: "/placeholders/placeholder4.webp", date: "12 Jan 2025", onBlogClick: () => console.log("Blog 4 clicked"), + }, +]; + +export default function BlogPage() { + const [posts, setPosts] = useState(defaultPosts); + const [isLoading, setIsLoading] = useState(true); + + useEffect(() => { + const fetchPosts = async () => { + try { + const apiUrl = process.env.NEXT_PUBLIC_API_URL; + const projectId = process.env.NEXT_PUBLIC_PROJECT_ID; + + if (!apiUrl || !projectId) { + console.warn("NEXT_PUBLIC_API_URL or NEXT_PUBLIC_PROJECT_ID not configured, using default posts"); + setIsLoading(false); + return; + } + + const url = `${apiUrl}/posts/${projectId}?status=published`; + const response = await fetch(url, { + method: "GET", headers: { + "Content-Type": "application/json"}, + }); + + if (response.ok) { + const resp = await response.json(); + const data = resp.data; + if (Array.isArray(data) && data.length > 0) { + const mappedPosts = data.map((post: any) => ({ + id: post.id || String(Math.random()), + category: post.category || "General", title: post.title || "Untitled", excerpt: post.excerpt || post.content.slice(0, 30) || "", imageSrc: post.imageUrl || "/placeholders/placeholder3.avif", imageAlt: post.imageAlt || post.title || "", authorName: post.author?.name || "Anonymous", authorAvatar: post.author?.avatar || "/placeholders/placeholder3.avif", date: post.date || post.createdAt || new Date().toLocaleDateString("en-GB", { day: "numeric", month: "short", year: "numeric" }), + onBlogClick: () => console.log(`Blog ${post.id} clicked`), + })); + setPosts(mappedPosts); + } + } else { + console.warn(`API request failed with status ${response.status}, using default posts`); + } + } catch (error) { + console.error("Error fetching posts:", error); + } finally { + setIsLoading(false); + } + }; + + fetchPosts(); + }, []); + + return ( + + +
+ + + {isLoading ? ( +
+

Loading posts...

+
+ ) : ( + + )} + + +
+
+
+ ); +} \ No newline at end of file diff --git a/src/app/globals.css b/src/app/globals.css new file mode 100644 index 0000000..989314f --- /dev/null +++ b/src/app/globals.css @@ -0,0 +1,595 @@ +@import "tailwindcss"; + +:root { + /* Base units */ + /* --vw is set by ThemeProvider */ + + /* --background: #f5f4ef;; + --card: #dad6cd;; + --foreground: #2a2928;; + --primary-cta: #2a2928;; + --secondary-cta: #ecebea;; + --accent: #ffffff;; + --background-accent: #c6b180;; */ + + --background: #f5f4ef;; + --card: #dad6cd;; + --foreground: #2a2928;; + --primary-cta: #2a2928;; + --secondary-cta: #ecebea;; + --accent: #ffffff;; + --background-accent: #c6b180;; + + /* text sizing - set by ThemeProvider */ + /* --text-2xs: clamp(0.465rem, 0.62vw, 0.62rem); + --text-xs: clamp(0.54rem, 0.72vw, 0.72rem); + --text-sm: clamp(0.615rem, 0.82vw, 0.82rem); + --text-base: clamp(0.69rem, 0.92vw, 0.92rem); + --text-lg: clamp(0.75rem, 1vw, 1rem); + --text-xl: clamp(0.825rem, 1.1vw, 1.1rem); + --text-2xl: clamp(0.975rem, 1.3vw, 1.3rem); + --text-3xl: clamp(1.2rem, 1.6vw, 1.6rem); + --text-4xl: clamp(1.5rem, 2vw, 2rem); + --text-5xl: clamp(2.025rem, 2.75vw, 2.75rem); + --text-6xl: clamp(2.475rem, 3.3vw, 3.3rem); + --text-7xl: clamp(3rem, 4vw, 4rem); + --text-8xl: clamp(3.5rem, 4.5vw, 4.5rem); + --text-9xl: clamp(5.25rem, 7vw, 7rem); */ + + /* Base spacing units */ + --vw-0_25: calc(var(--vw) * 0.25); + --vw-0_5: calc(var(--vw) * 0.5); + --vw-0_625: calc(var(--vw) * 0.625); + --vw-0_75: calc(var(--vw) * 0.75); + --vw-1: calc(var(--vw) * 1); + --vw-1_25: calc(var(--vw) * 1.25); + --vw-1_5: calc(var(--vw) * 1.5); + --vw-1_75: calc(var(--vw) * 1.75); + --vw-2: calc(var(--vw) * 2); + --vw-2_25: calc(var(--vw) * 2.25); + --vw-2_5: calc(var(--vw) * 2.5); + --vw-2_75: calc(var(--vw) * 2.75); + --vw-3: calc(var(--vw) * 3); + + /* width */ + --width-5: clamp(4rem, 5vw, 6rem); + --width-7_5: clamp(5.625rem, 7.5vw, 7.5rem); + --width-10: clamp(7.5rem, 10vw, 10rem); + --width-12_5: clamp(9.375rem, 12.5vw, 12.5rem); + --width-15: clamp(11.25rem, 15vw, 15rem); + --width-17: clamp(12.75rem, 17vw, 17rem); + --width-17_5: clamp(13.125rem, 17.5vw, 17.5rem); + --width-20: clamp(15rem, 20vw, 20rem); + --width-21: clamp(15.75rem, 21vw, 21rem); + --width-22_5: clamp(16.875rem, 22.5vw, 22.5rem); + --width-25: clamp(18.75rem, 25vw, 25rem); + --width-26: clamp(19.5rem, 26vw, 26rem); + --width-27_5: clamp(20.625rem, 27.5vw, 27.5rem); + --width-30: clamp(22.5rem, 30vw, 30rem); + --width-32_5: clamp(24.375rem, 32.5vw, 32.5rem); + --width-35: clamp(26.25rem, 35vw, 35rem); + --width-37_5: clamp(28.125rem, 37.5vw, 37.5rem); + --width-40: clamp(30rem, 40vw, 40rem); + --width-42_5: clamp(31.875rem, 42.5vw, 42.5rem); + --width-45: clamp(33.75rem, 45vw, 45rem); + --width-47_5: clamp(35.625rem, 47.5vw, 47.5rem); + --width-50: clamp(37.5rem, 50vw, 50rem); + --width-52_5: clamp(39.375rem, 52.5vw, 52.5rem); + --width-55: clamp(41.25rem, 55vw, 55rem); + --width-57_5: clamp(43.125rem, 57.5vw, 57.5rem); + --width-60: clamp(45rem, 60vw, 60rem); + --width-62_5: clamp(46.875rem, 62.5vw, 62.5rem); + --width-65: clamp(48.75rem, 65vw, 65rem); + --width-67_5: clamp(50.625rem, 67.5vw, 67.5rem); + --width-70: clamp(52.5rem, 70vw, 70rem); + --width-72_5: clamp(54.375rem, 72.5vw, 72.5rem); + --width-75: clamp(56.25rem, 75vw, 75rem); + --width-77_5: clamp(58.125rem, 77.5vw, 77.5rem); + --width-80: clamp(60rem, 80vw, 80rem); + --width-82_5: clamp(61.875rem, 82.5vw, 82.5rem); + --width-85: clamp(63.75rem, 85vw, 85rem); + --width-87_5: clamp(65.625rem, 87.5vw, 87.5rem); + --width-90: clamp(67.5rem, 90vw, 90rem); + --width-92_5: clamp(69.375rem, 92.5vw, 92.5rem); + --width-95: clamp(71.25rem, 95vw, 95rem); + --width-97_5: clamp(73.125rem, 97.5vw, 97.5rem); + --width-100: clamp(75rem, 100vw, 100rem); + /* --width-content-width and --width-content-width-expanded are set by ThemeProvider */ + --width-carousel-padding: calc((100vw - var(--width-content-width)) / 2 + 1px - var(--vw-1_5)); + --width-carousel-padding-controls: calc((100vw - var(--width-content-width)) / 2 + 1px); + --width-carousel-padding-expanded: calc((var(--width-content-width-expanded) - var(--width-content-width)) / 2 + 1px - var(--vw-1_5)); + --width-carousel-padding-controls-expanded: calc((var(--width-content-width-expanded) - var(--width-content-width)) / 2 + 1px); + --width-carousel-item-3: calc(var(--width-content-width) / 3 - var(--vw-1_5) / 3 * 2); + --width-carousel-item-4: calc(var(--width-content-width) / 4 - var(--vw-1_5) / 4 * 3); + --width-x-padding-mask-fade: clamp(1.5rem, 4vw, 4rem); + + --height-4: 1rem; + --height-5: 1.25rem; + --height-6: 1.5rem; + --height-7: 1.75rem; + --height-8: 2rem; + --height-9: 2.25rem; + --height-10: 2.5rem; + --height-11: 2.75rem; + --height-12: 3rem; + --height-30: 7.5rem; + --height-90: 22.5rem; + --height-100: 25rem; + --height-110: 27.5rem; + --height-120: 30rem; + --height-130: 32.5rem; + --height-140: 35rem; + --height-150: 37.5rem; + + /* hero page padding */ + --padding-hero-page-padding-half: calc((var(--height-10) + var(--vw-1_5) + var(--vw-1_5) + var(--height-10)) / 2); + --padding-hero-page-padding: calc(var(--height-10) + var(--vw-1_5) + var(--vw-1_5) + var(--height-10)); + --padding-hero-page-padding-1_5: calc(1.5 * (var(--height-10) + var(--vw-1_5) + var(--vw-1_5) + var(--height-10))); + --padding-hero-page-padding-double: calc(2 * (var(--height-10) + var(--vw-1_5) + var(--vw-1_5) + var(--height-10))); +} + +@media (max-width: 767px) { + :root { + /* --vw and text sizing are set by ThemeProvider */ + /* --vw: 3vw; + + --text-2xs: 2.5vw; + --text-xs: 2.75vw; + --text-sm: 3vw; + --text-base: 3.25vw; + --text-lg: 3.5vw; + --text-xl: 4.25vw; + --text-2xl: 5vw; + --text-3xl: 6vw; + --text-4xl: 7vw; + --text-5xl: 7.5vw; + --text-6xl: 8.5vw; + --text-7xl: 10vw; + --text-8xl: 12vw; + --text-9xl: 14vw; */ + + --width-5: 5vw; + --width-7_5: 7.5vw; + --width-10: 10vw; + --width-12_5: 12.5vw; + --width-15: 15vw; + --width-17_5: 17.5vw; + --width-20: 20vw; + --width-22_5: 22.5vw; + --width-25: 25vw; + --width-27_5: 27.5vw; + --width-30: 30vw; + --width-32_5: 32.5vw; + --width-35: 35vw; + --width-37_5: 37.5vw; + --width-40: 40vw; + --width-42_5: 42.5vw; + --width-45: 45vw; + --width-47_5: 47.5vw; + --width-50: 50vw; + --width-52_5: 52.5vw; + --width-55: 55vw; + --width-57_5: 57.5vw; + --width-60: 60vw; + --width-62_5: 62.5vw; + --width-65: 65vw; + --width-67_5: 67.5vw; + --width-70: 70vw; + --width-72_5: 72.5vw; + --width-75: 75vw; + --width-77_5: 77.5vw; + --width-80: 80vw; + --width-82_5: 82.5vw; + --width-85: 85vw; + --width-87_5: 87.5vw; + --width-90: 90vw; + --width-92_5: 92.5vw; + --width-95: 95vw; + --width-97_5: 97.5vw; + --width-100: 100vw; + /* --width-content-width and --width-content-width-expanded are set by ThemeProvider */ + --width-carousel-padding: calc((100vw - var(--width-content-width)) / 2 + 1px - var(--vw-1_5)); + --width-carousel-padding-controls: calc((100vw - var(--width-content-width)) / 2 + 1px); + --width-carousel-padding-expanded: calc((var(--width-content-width-expanded) - var(--width-content-width)) / 2 + 1px - var(--vw-1_5)); + --width-carousel-padding-controls-expanded: calc((var(--width-content-width-expanded) - var(--width-content-width)) / 2 + 1px); + --width-carousel-item-3: var(--width-content-width); + --width-carousel-item-4: var(--width-content-width); + --width-x-padding-mask-fade: 10vw; + + --height-4: 3.5vw; + --height-5: 4.5vw; + --height-6: 5.5vw; + --height-7: 6.5vw; + --height-8: 7.5vw; + --height-9: 8.5vw; + --height-10: 9vw; + --height-11: 10vw; + --height-12: 11vw; + --height-30: 25vw; + --height-90: 81vw; + --height-100: 90vw; + --height-110: 99vw; + --height-120: 108vw; + --height-130: 117vw; + --height-140: 126vw; + --height-150: 135vw; + } +} + +@theme inline { + --color-background: var(--background); + --color-card: var(--card); + --color-foreground: var(--foreground); + --color-primary-cta: var(--primary-cta); + --color-secondary-cta: var(--secondary-cta); + --color-accent: var(--accent); + --color-background-accent: var(--background-accent); + + /* theme border radius */ + --radius-theme: var(--theme-border-radius); + --radius-theme-capped: var(--theme-border-radius-capped); + + /* text */ + --text-2xs: var(--text-2xs); + --text-xs: var(--text-xs); + --text-sm: var(--text-sm); + --text-base: var(--text-base); + --text-lg: var(--text-lg); + --text-xl: var(--text-xl); + --text-2xl: var(--text-2xl); + --text-3xl: var(--text-3xl); + --text-4xl: var(--text-4xl); + --text-5xl: var(--text-5xl); + --text-6xl: var(--text-6xl); + --text-7xl: var(--text-7xl); + --text-8xl: var(--text-8xl); + --text-9xl: var(--text-9xl); + + /* height */ + --height-4: var(--height-4); + --height-5: var(--height-5); + --height-6: var(--height-6); + --height-7: var(--height-7); + --height-8: var(--height-8); + --height-9: var(--height-9); + --height-11: var(--height-11); + --height-12: var(--height-12); + + --height-10: var(--height-10); + --height-30: var(--height-30); + --height-90: var(--height-90); + --height-100: var(--height-100); + --height-110: var(--height-110); + --height-120: var(--height-120); + --height-130: var(--height-130); + --height-140: var(--height-140); + --height-150: var(--height-150); + + --height-page-padding: calc(2.25rem+var(--vw-1_5)+var(--vw-1_5)); + + /* width */ + --width-5: var(--width-5); + --width-7_5: var(--width-7_5); + --width-10: var(--width-10); + --width-12_5: var(--width-12_5); + --width-15: var(--width-15); + --width-17: var(--width-17); + --width-17_5: var(--width-17_5); + --width-20: var(--width-20); + --width-21: var(--width-21); + --width-22_5: var(--width-22_5); + --width-25: var(--width-25); + --width-26: var(--width-26); + --width-27_5: var(--width-27_5); + --width-30: var(--width-30); + --width-32_5: var(--width-32_5); + --width-35: var(--width-35); + --width-37_5: var(--width-37_5); + --width-40: var(--width-40); + --width-42_5: var(--width-42_5); + --width-45: var(--width-45); + --width-47_5: var(--width-47_5); + --width-50: var(--width-50); + --width-52_5: var(--width-52_5); + --width-55: var(--width-55); + --width-57_5: var(--width-57_5); + --width-60: var(--width-60); + --width-62_5: var(--width-62_5); + --width-65: var(--width-65); + --width-67_5: var(--width-67_5); + --width-70: var(--width-70); + --width-72_5: var(--width-72_5); + --width-75: var(--width-75); + --width-77_5: var(--width-77_5); + --width-80: var(--width-80); + --width-82_5: var(--width-82_5); + --width-85: var(--width-85); + --width-87_5: var(--width-87_5); + --width-90: var(--width-90); + --width-92_5: var(--width-92_5); + --width-95: var(--width-95); + --width-97_5: var(--width-97_5); + --width-100: var(--width-100); + --width-content-width: var(--width-content-width); + --width-carousel-padding: var(--width-carousel-padding); + --width-carousel-padding-controls: var(--width-carousel-padding-controls); + --width-carousel-padding-expanded: var(--width-carousel-padding-expanded); + --width-carousel-padding-controls-expanded: var(--width-carousel-padding-controls-expanded); + --width-carousel-item-3: var(--width-carousel-item-3); + --width-carousel-item-4: var(--width-carousel-item-4); + --width-x-padding-mask-fade: var(--width-x-padding-mask-fade); + --width-content-width-expanded: var(--width-content-width-expanded); + + /* gap */ + --spacing-1: var(--vw-0_25); + --spacing-2: var(--vw-0_5); + --spacing-3: var(--vw-0_75); + --spacing-4: var(--vw-1); + --spacing-5: var(--vw-1_25); + --spacing-6: var(--vw-1_5); + --spacing-7: var(--vw-1_75); + --spacing-8: var(--vw-2); + + --spacing-x-1: var(--vw-0_25); + --spacing-x-2: var(--vw-0_5); + --spacing-x-3: var(--vw-0_75); + --spacing-x-4: var(--vw-1); + --spacing-x-5: var(--vw-1_25); + --spacing-x-6: var(--vw-1_5); + + /* border radius */ + --radius-none: 0; + --radius-sm: var(--vw-0_5); + --radius: var(--vw-0_75); + --radius-md: var(--vw-1); + --radius-lg: var(--vw-1_25); + --radius-xl: var(--vw-1_75); + --radius-full: 999px; + + /* padding */ + --padding-1: var(--vw-0_25); + --padding-2: var(--vw-0_5); + --padding-2.5: var(--vw-0_625); + --padding-3: var(--vw-0_75); + --padding-4: var(--vw-1); + --padding-5: var(--vw-1_25); + --padding-6: var(--vw-1_5); + --padding-7: var(--vw-1_75); + --padding-8: var(--vw-2); + + --padding-x-1: var(--vw-0_25); + --padding-x-2: var(--vw-0_5); + --padding-x-3: var(--vw-0_75); + --padding-x-4: var(--vw-1); + --padding-x-5: var(--vw-1_25); + --padding-x-6: var(--vw-1_5); + --padding-x-7: var(--vw-1_75); + --padding-x-8: var(--vw-2); + + --padding-hero-page-padding-half: var(--padding-hero-page-padding-half); + --padding-hero-page-padding: var(--padding-hero-page-padding); + --padding-hero-page-padding-1_5: var(--padding-hero-page-padding-1_5); + --padding-hero-page-padding-double: var(--padding-hero-page-padding-double); + + /* margin */ + --margin-1: var(--vw-0_25); + --margin-2: var(--vw-0_5); + --margin-3: var(--vw-0_75); + --margin-4: var(--vw-1); + --margin-5: var(--vw-1_25); + --margin-6: var(--vw-1_5); + --margin-7: var(--vw-1_75); + --margin-8: var(--vw-2); + + --margin-x-1: var(--vw-0_25); + --margin-x-2: var(--vw-0_5); + --margin-x-3: var(--vw-0_75); + --margin-x-4: var(--vw-1); + --margin-x-5: var(--vw-1_25); + --margin-x-6: var(--vw-1_5); + --margin-x-7: var(--vw-1_75); + --margin-x-8: var(--vw-2); +} + +@layer components {} + +@layer utilities { + + /* Card, primary-button, and secondary-button styles are now dynamically injected via ThemeProvider */ + + /* .card { + @apply backdrop-blur-sm bg-gradient-to-br from-card/80 to-card/40 shadow-sm border border-card; + } + + .primary-button { + @apply bg-gradient-to-b from-primary-cta/83 to-primary-cta; + box-shadow: + color-mix(in srgb, var(--color-background) 25%, transparent) 0px 1px 1px 0px inset, + color-mix(in srgb, var(--color-primary-cta) 15%, transparent) 3px 3px 3px 0px; + } + + .secondary-button { + @apply backdrop-blur-sm bg-gradient-to-br from-secondary-cta/80 to-secondary-cta shadow-sm border border-secondary-cta; + } */ + + .tag-card { + @apply backdrop-blur-sm bg-gradient-to-br from-card/80 to-card/40 shadow-sm border border-card; + } + + .mask-padding-x { + -webkit-mask-image: linear-gradient(to right, transparent 0%, black var(--width-x-padding-mask-fade), black calc(100% - var(--width-x-padding-mask-fade)), transparent 100%); + mask-image: linear-gradient(to right, transparent 0%, black var(--width-x-padding-mask-fade), black calc(100% - var(--width-x-padding-mask-fade)), transparent 100%); + } + + .mask-fade-bottom { + -webkit-mask-image: linear-gradient(to bottom, black 0%, black 50%, transparent 100%); + mask-image: linear-gradient(to bottom, black 0%, black 50%, transparent 100%); + } + + .mask-fade-y { + mask-image: linear-gradient(to bottom, + transparent 0%, + black var(--vw-1_5), + black calc(100% - var(--vw-1_5)), + transparent 100%); + } + + .mask-fade-bottom-large { + -webkit-mask-image: linear-gradient(to bottom, black 0%, black 50%, transparent 75%, transparent 100%); + mask-image: linear-gradient(to bottom, black 0%, black 50%, transparent 75%, transparent 100%); + } + + .mask-fade-bottom-long { + -webkit-mask-image: linear-gradient(to bottom, black 0%, black 5%, transparent 100%); + mask-image: linear-gradient(to bottom, black 0%, black 5%, transparent 100%); + } + + .mask-fade-top-long { + -webkit-mask-image: linear-gradient(to top, black 0%, black 5%, transparent 100%); + mask-image: linear-gradient(to top, black 0%, black 5%, transparent 100%); + } + + .mask-fade-xy { + -webkit-mask-image: + linear-gradient(to right, transparent 0%, black 20%, black 80%, transparent 100%), + linear-gradient(to bottom, transparent 0%, black 20%, black 80%, transparent 100%); + mask-image: + linear-gradient(to right, transparent 0%, black 20%, black 80%, transparent 100%), + linear-gradient(to bottom, transparent 0%, black 20%, black 80%, transparent 100%); + -webkit-mask-composite: source-in; + mask-composite: intersect; + } + + /* ANIMATION */ + + .animation-container { + animation: + fadeInOpacity 0.8s ease-in-out forwards, + fadeInTranslate 0.6s forwards; + } + + .animation-container-fade { + animation: fadeInOpacity 0.8s ease-in-out forwards; + } + + @keyframes fadeInOpacity { + from { + opacity: 0; + } + + to { + opacity: 1; + } + } + + @keyframes fadeInTranslate { + from { + transform: translateY(0.75vh); + } + + to { + transform: translateY(0vh); + } + } + + @keyframes aurora { + from { + background-position: 50% 50%, 50% 50%; + } + + to { + background-position: 350% 50%, 350% 50%; + } + } + + @keyframes spin-slow { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } + } + + @keyframes spin-reverse { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(-360deg); + } + } + + .animate-spin-slow { + animation: spin-slow 15s linear infinite; + } + + .animate-spin-reverse { + animation: spin-reverse 10s linear infinite; + } + + @keyframes marquee-vertical { + from { + transform: translateY(0); + } + to { + transform: translateY(-50%); + } + } + + .animate-marquee-vertical { + animation: marquee-vertical 20s linear infinite; + } + + @keyframes orbit { + from { + transform: rotate(var(--initial-position, 0deg)) translateX(var(--translate-position, 120px)) rotate(calc(-1 * var(--initial-position, 0deg))); + } + to { + transform: rotate(calc(var(--initial-position, 0deg) + 360deg)) translateX(var(--translate-position, 120px)) rotate(calc(-1 * (var(--initial-position, 0deg) + 360deg))); + } + } + + @keyframes map-dot-pulse { + 0%, 100% { + transform: scale(0.4); + opacity: 0.6; + } + 50% { + transform: scale(1.4); + opacity: 1; + } + } + +} + +* { + scrollbar-width: thin; + scrollbar-color: rgba(255, 255, 255, 1) rgba(255, 255, 255, 0); +} + +html { + overscroll-behavior: none; + overscroll-behavior-y: none; +} + +body { + background-color: var(--background); + color: var(--foreground); + font-family: var(--font-poppins), sans-serif; + position: relative; + min-height: 100vh; + overscroll-behavior: none; + overscroll-behavior-y: none; +} + +h1, +h2, +h3, +h4, +h5, +h6 { + font-family: var(--font-poppins), sans-serif; +} \ No newline at end of file diff --git a/src/app/layout.tsx b/src/app/layout.tsx new file mode 100644 index 0000000..a1ba4a8 --- /dev/null +++ b/src/app/layout.tsx @@ -0,0 +1,1264 @@ +import type { Metadata } from "next"; +import { Poppins } from "next/font/google"; +import "./globals.css"; +import { ServiceWrapper } from "@/components/ServiceWrapper"; +import Tag from "@/tag/Tag"; + +const poppins = Poppins({ + variable: "--font-poppins", subsets: ["latin"], + weight: ["100", "200", "300", "400", "500", "600", "700", "800", "900"], +}); + +export const metadata: Metadata = { + title: "Paws & Whiskers Pet Shelter - Rescue, Adoption & Animal Care", description: "Adopt a rescue animal from Paws & Whiskers. We provide veterinary care, rehabilitation, and forever homes for abandoned pets. Browse our available animals today.", keywords: "pet adoption, animal shelter, rescue animals, dog adoption, cat adoption, animal rescue, pet rescue near me, no-kill shelter", metadataBase: new URL("https://pawsandwhiskers.com"), + alternates: { + canonical: "https://pawsandwhiskers.com" + }, + openGraph: { + title: "Paws & Whiskers - Save a Life Today", description: "Join our mission to rescue and rehome animals in need. Browse available pets for adoption.", siteName: "Paws & Whiskers", type: "website" + }, + twitter: { + card: "summary_large_image", title: "Adopt from Paws & Whiskers Pet Shelter", description: "Find your perfect pet. Browse animals available for adoption and help save lives." + }, + robots: { + index: true, + follow: true + } +}; + +export default function RootLayout({ + children, +}: Readonly<{ + children: React.ReactNode; +}>) { + return ( + + + + + {children} + +