feat: implement landing page structure with legal pages, footer, CTA, and domain redirection proxy

This commit is contained in:
2026-04-14 10:30:46 +02:00
parent 765eea05f7
commit 383d8484a6
14 changed files with 515 additions and 319 deletions

View File

@@ -0,0 +1,71 @@
'use client'
import Link from 'next/link'
import { useLang } from '@/context/LangContext'
import { siteConfig } from '@/lib/site'
const CONTENT = {
de: {
title: 'Impressum',
companyLabel: 'Unternehmen',
addressLabel: 'Adresse',
representativeLabel: 'Vertretungsberechtigt',
contactLabel: 'Kontakt',
registryLabel: 'Register',
vatLabel: 'USt-ID',
},
en: {
title: 'Imprint',
companyLabel: 'Company',
addressLabel: 'Address',
representativeLabel: 'Represented by',
contactLabel: 'Contact',
registryLabel: 'Registry',
vatLabel: 'VAT ID',
},
es: {
title: 'Aviso Legal',
companyLabel: 'Empresa',
addressLabel: 'Direccion',
representativeLabel: 'Representante',
contactLabel: 'Contacto',
registryLabel: 'Registro',
vatLabel: 'IVA',
},
}
export default function ImprintContent() {
const { lang } = useLang()
const c = CONTENT[lang]
return (
<main className="container" style={{ paddingTop: '8rem', paddingBottom: '8rem', maxWidth: '800px' }}>
<p style={{ marginBottom: '1rem', opacity: 0.75 }}>
<Link href="/">Home</Link> / <span>Legal</span> / <span>{c.title}</span>
</p>
<h1>{c.title}</h1>
<div style={{ marginTop: '2rem', lineHeight: '1.8', opacity: 0.9 }}>
<p>
<strong>{c.companyLabel}:</strong> {siteConfig.company.legalName}
</p>
{siteConfig.company.addressLine1 ? (
<p><strong>{c.addressLabel}:</strong> {siteConfig.company.addressLine1}</p>
) : null}
{siteConfig.company.addressLine2 ? <p>{siteConfig.company.addressLine2}</p> : null}
<p>{siteConfig.company.country}</p>
<p>
<strong>{c.representativeLabel}:</strong> {siteConfig.company.representative}
</p>
<p>
<strong>{c.contactLabel}:</strong> <a href={`mailto:${siteConfig.legalEmail}`}>{siteConfig.legalEmail}</a>
</p>
{siteConfig.company.registry ? (
<p><strong>{c.registryLabel}:</strong> {siteConfig.company.registry}</p>
) : null}
{siteConfig.company.vatId ? (
<p><strong>{c.vatLabel}:</strong> {siteConfig.company.vatId}</p>
) : null}
</div>
</main>
)
}

View File

@@ -1,67 +1,37 @@
'use client'
import { useLang } from '@/context/LangContext'
import type { Metadata } from 'next'
import Navbar from '@/components/Navbar'
import Footer from '@/components/Footer'
import ImprintContent from './ImprintContent'
import { siteConfig } from '@/lib/site'
const CONTENT = {
de: {
title: 'Impressum',
companyLabel: 'Unternehmen',
addressLabel: 'Adresse',
representativeLabel: 'Vertretungsberechtigt',
contactLabel: 'Kontakt',
registryLabel: 'Register',
vatLabel: 'USt-ID',
const title = 'Imprint'
const description = 'Legal imprint and company contact information for GreenLens.'
export const metadata: Metadata = {
title,
description,
alternates: {
canonical: '/imprint',
},
en: {
title: 'Imprint',
companyLabel: 'Company',
addressLabel: 'Address',
representativeLabel: 'Represented by',
contactLabel: 'Contact',
registryLabel: 'Registry',
vatLabel: 'VAT ID',
openGraph: {
title: `${title} | GreenLens`,
description,
url: `${siteConfig.domain}/imprint`,
type: 'website',
},
es: {
title: 'Aviso Legal',
companyLabel: 'Empresa',
addressLabel: 'Direccion',
representativeLabel: 'Representante',
contactLabel: 'Contacto',
registryLabel: 'Registro',
vatLabel: 'IVA',
twitter: {
card: 'summary_large_image',
title: `${title} | GreenLens`,
description,
},
}
export default function ImprintPage() {
const { lang } = useLang()
const c = CONTENT[lang]
return (
<main className="container" style={{ paddingTop: '8rem', paddingBottom: '8rem', maxWidth: '800px' }}>
<h1>{c.title}</h1>
<div style={{ marginTop: '2rem', lineHeight: '1.8', opacity: 0.9 }}>
<p>
<strong>{c.companyLabel}:</strong> {siteConfig.company.legalName}
</p>
{siteConfig.company.addressLine1 ? (
<p><strong>{c.addressLabel}:</strong> {siteConfig.company.addressLine1}</p>
) : null}
{siteConfig.company.addressLine2 ? <p>{siteConfig.company.addressLine2}</p> : null}
<p>{siteConfig.company.country}</p>
<p>
<strong>{c.representativeLabel}:</strong> {siteConfig.company.representative}
</p>
<p>
<strong>{c.contactLabel}:</strong> <a href={`mailto:${siteConfig.legalEmail}`}>{siteConfig.legalEmail}</a>
</p>
{siteConfig.company.registry ? (
<p><strong>{c.registryLabel}:</strong> {siteConfig.company.registry}</p>
) : null}
{siteConfig.company.vatId ? (
<p><strong>{c.vatLabel}:</strong> {siteConfig.company.vatId}</p>
) : null}
</div>
</main>
<>
<Navbar />
<ImprintContent />
<Footer />
</>
)
}

View File

@@ -29,6 +29,10 @@ export const metadata: Metadata = {
type: 'website',
url: siteConfig.domain,
},
alternates: {
// Do not emit hreflang until each language has its own URL.
languages: {},
},
twitter: {
card: 'summary_large_image',
title: 'GreenLens - Plant Identifier and Care Planner',

View File

@@ -1,3 +1,4 @@
import type { Metadata } from 'next'
import Navbar from '@/components/Navbar'
import Hero from '@/components/Hero'
import Ticker from '@/components/Ticker'
@@ -9,6 +10,12 @@ import FAQ from '@/components/FAQ'
import CTA from '@/components/CTA'
import Footer from '@/components/Footer'
export const metadata: Metadata = {
alternates: {
canonical: '/',
},
}
const howToSchema = {
'@context': 'https://schema.org',
'@type': 'HowTo',

View File

@@ -0,0 +1,84 @@
'use client'
import Link from 'next/link'
import { useLang } from '@/context/LangContext'
import { siteConfig } from '@/lib/site'
const CONTENT = {
de: {
title: 'Datenschutzerklaerung',
intro:
'Der Schutz personenbezogener Daten ist uns wichtig. Diese Seite beschreibt in knapper Form, welche Daten GreenLens verarbeiten kann und warum.',
section1: '1. Verarbeitete Daten',
text1:
'Je nach Nutzung der App koennen Pflanzenfotos, Kontodaten, technische Geraeteinformationen, In-App-Kaufdaten sowie freiwillig uebermittelte Support-Anfragen verarbeitet werden.',
section2: '2. Zweck der Verarbeitung',
text2:
'Diese Daten werden verwendet, um Pflanzenscans auszufuehren, deine Sammlung zu speichern, Erinnerungen bereitzustellen, Abos und Credits zu verwalten sowie Support-Anfragen zu beantworten.',
section3: '3. Drittanbieter',
text3:
'GreenLens nutzt technische Dienstleister fuer App-Betrieb, Analyse, Authentifizierung und In-App-Kaeufe. Dazu koennen je nach Plattform Apple, RevenueCat, PostHog oder Hosting-Anbieter gehoeren.',
section4: '4. Kontakt',
text4: 'Bei Fragen zum Datenschutz oder zu deinen Datenrechten kannst du uns per E-Mail kontaktieren.',
},
en: {
title: 'Privacy Policy',
intro:
'Protecting personal data matters to us. This page summarizes what GreenLens may process and why.',
section1: '1. Data we may process',
text1:
'Depending on how you use the app, GreenLens may process plant photos, account details, technical device information, in-app purchase data, and support messages you send to us.',
section2: '2. Why we process it',
text2:
'We use this information to run plant scans, store your collection, provide reminders, manage subscriptions and credits, and respond to support requests.',
section3: '3. Third-party services',
text3:
'GreenLens uses service providers for app delivery, analytics, authentication, and in-app purchases. Depending on platform and setup, this can include Apple, RevenueCat, PostHog, or hosting providers.',
section4: '4. Contact',
text4: 'If you have privacy questions or want to exercise your data rights, contact us by email.',
},
es: {
title: 'Politica de Privacidad',
intro:
'La proteccion de los datos personales es importante para nosotros. Esta pagina resume que datos puede procesar GreenLens y por que.',
section1: '1. Datos que podemos procesar',
text1:
'Segun el uso de la app, GreenLens puede procesar fotos de plantas, datos de cuenta, informacion tecnica del dispositivo, datos de compras dentro de la app y mensajes de soporte.',
section2: '2. Para que los usamos',
text2:
'Usamos estos datos para ejecutar escaneos, guardar tu coleccion, ofrecer recordatorios, gestionar suscripciones y creditos, y responder solicitudes de soporte.',
section3: '3. Servicios de terceros',
text3:
'GreenLens utiliza proveedores para la operacion de la app, analitica, autenticacion y compras integradas. Segun la plataforma, esto puede incluir Apple, RevenueCat, PostHog o proveedores de hosting.',
section4: '4. Contacto',
text4: 'Si tienes preguntas sobre privacidad o tus derechos de datos, contactanos por correo electronico.',
},
}
export default function PrivacyContent() {
const { lang } = useLang()
const c = CONTENT[lang]
return (
<main className="container" style={{ paddingTop: '8rem', paddingBottom: '8rem', maxWidth: '800px' }}>
<p style={{ marginBottom: '1rem', opacity: 0.75 }}>
<Link href="/">Home</Link> / <span>Legal</span> / <span>{c.title}</span>
</p>
<h1>{c.title}</h1>
<div style={{ marginTop: '2rem', lineHeight: '1.8', opacity: 0.9 }}>
<p>{c.intro}</p>
<h2 style={{ marginTop: '1.5rem', fontSize: '1.25rem' }}>{c.section1}</h2>
<p>{c.text1}</p>
<h2 style={{ marginTop: '1.5rem', fontSize: '1.25rem' }}>{c.section2}</h2>
<p>{c.text2}</p>
<h2 style={{ marginTop: '1.5rem', fontSize: '1.25rem' }}>{c.section3}</h2>
<p>{c.text3}</p>
<h2 style={{ marginTop: '1.5rem', fontSize: '1.25rem' }}>{c.section4}</h2>
<p>{c.text4}</p>
<p style={{ marginTop: '0.75rem' }}>
<a href={`mailto:${siteConfig.legalEmail}`}>{siteConfig.legalEmail}</a>
</p>
</div>
</main>
)
}

View File

@@ -1,80 +1,37 @@
'use client'
import { useLang } from '@/context/LangContext'
import type { Metadata } from 'next'
import Navbar from '@/components/Navbar'
import Footer from '@/components/Footer'
import PrivacyContent from './PrivacyContent'
import { siteConfig } from '@/lib/site'
const CONTENT = {
de: {
title: 'Datenschutzerklaerung',
intro:
'Der Schutz personenbezogener Daten ist uns wichtig. Diese Seite beschreibt in knapper Form, welche Daten GreenLens verarbeiten kann und warum.',
section1: '1. Verarbeitete Daten',
text1:
'Je nach Nutzung der App koennen Pflanzenfotos, Kontodaten, technische Geraeteinformationen, In-App-Kaufdaten sowie freiwillig uebermittelte Support-Anfragen verarbeitet werden.',
section2: '2. Zweck der Verarbeitung',
text2:
'Diese Daten werden verwendet, um Pflanzenscans auszufuehren, deine Sammlung zu speichern, Erinnerungen bereitzustellen, Abos und Credits zu verwalten sowie Support-Anfragen zu beantworten.',
section3: '3. Drittanbieter',
text3:
'GreenLens nutzt technische Dienstleister fuer App-Betrieb, Analyse, Authentifizierung und In-App-Kaeufe. Dazu koennen je nach Plattform Apple, RevenueCat, PostHog oder Hosting-Anbieter gehoeren.',
section4: '4. Kontakt',
text4: 'Bei Fragen zum Datenschutz oder zu deinen Datenrechten kannst du uns per E-Mail kontaktieren.',
const title = 'Privacy Policy'
const description = 'Learn what personal data GreenLens processes, why it is used, and how to contact us about privacy.'
export const metadata: Metadata = {
title,
description,
alternates: {
canonical: '/privacy',
},
en: {
title: 'Privacy Policy',
intro:
'Protecting personal data matters to us. This page summarizes what GreenLens may process and why.',
section1: '1. Data we may process',
text1:
'Depending on how you use the app, GreenLens may process plant photos, account details, technical device information, in-app purchase data, and support messages you send to us.',
section2: '2. Why we process it',
text2:
'We use this information to run plant scans, store your collection, provide reminders, manage subscriptions and credits, and respond to support requests.',
section3: '3. Third-party services',
text3:
'GreenLens uses service providers for app delivery, analytics, authentication, and in-app purchases. Depending on platform and setup, this can include Apple, RevenueCat, PostHog, or hosting providers.',
section4: '4. Contact',
text4: 'If you have privacy questions or want to exercise your data rights, contact us by email.',
openGraph: {
title: `${title} | GreenLens`,
description,
url: `${siteConfig.domain}/privacy`,
type: 'website',
},
es: {
title: 'Politica de Privacidad',
intro:
'La proteccion de los datos personales es importante para nosotros. Esta pagina resume que datos puede procesar GreenLens y por que.',
section1: '1. Datos que podemos procesar',
text1:
'Segun el uso de la app, GreenLens puede procesar fotos de plantas, datos de cuenta, informacion tecnica del dispositivo, datos de compras dentro de la app y mensajes de soporte.',
section2: '2. Para que los usamos',
text2:
'Usamos estos datos para ejecutar escaneos, guardar tu coleccion, ofrecer recordatorios, gestionar suscripciones y creditos, y responder solicitudes de soporte.',
section3: '3. Servicios de terceros',
text3:
'GreenLens utiliza proveedores para la operacion de la app, analitica, autenticacion y compras integradas. Segun la plataforma, esto puede incluir Apple, RevenueCat, PostHog o proveedores de hosting.',
section4: '4. Contacto',
text4: 'Si tienes preguntas sobre privacidad o tus derechos de datos, contactanos por correo electronico.',
twitter: {
card: 'summary_large_image',
title: `${title} | GreenLens`,
description,
},
}
export default function PrivacyPage() {
const { lang } = useLang()
const c = CONTENT[lang]
return (
<main className="container" style={{ paddingTop: '8rem', paddingBottom: '8rem', maxWidth: '800px' }}>
<h1>{c.title}</h1>
<div style={{ marginTop: '2rem', lineHeight: '1.8', opacity: 0.9 }}>
<p>{c.intro}</p>
<h2 style={{ marginTop: '1.5rem', fontSize: '1.25rem' }}>{c.section1}</h2>
<p>{c.text1}</p>
<h2 style={{ marginTop: '1.5rem', fontSize: '1.25rem' }}>{c.section2}</h2>
<p>{c.text2}</p>
<h2 style={{ marginTop: '1.5rem', fontSize: '1.25rem' }}>{c.section3}</h2>
<p>{c.text3}</p>
<h2 style={{ marginTop: '1.5rem', fontSize: '1.25rem' }}>{c.section4}</h2>
<p>{c.text4}</p>
<p style={{ marginTop: '0.75rem' }}>
<a href={`mailto:${siteConfig.legalEmail}`}>{siteConfig.legalEmail}</a>
</p>
</div>
</main>
<>
<Navbar />
<PrivacyContent />
<Footer />
</>
)
}

View File

@@ -1,10 +1,26 @@
import type { Metadata } from 'next'
import Link from 'next/link'
import Navbar from '@/components/Navbar'
import Footer from '@/components/Footer'
import { siteConfig } from '@/lib/site'
export const metadata: Metadata = {
title: 'Support',
description: 'Get support for GreenLens, including contact details, onboarding help, billing guidance, and privacy links.',
alternates: {
canonical: '/support',
},
openGraph: {
title: 'Support | GreenLens',
description: 'Get support for GreenLens, including contact details, onboarding help, billing guidance, and privacy links.',
url: `${siteConfig.domain}/support`,
type: 'website',
},
twitter: {
card: 'summary_large_image',
title: 'Support | GreenLens',
description: 'Get support for GreenLens, including contact details, onboarding help, billing guidance, and privacy links.',
},
}
const faqs = [
@@ -32,73 +48,81 @@ const faqs = [
export default function SupportPage() {
return (
<main className="support-page">
<section className="support-hero">
<div className="container support-hero-inner">
<p className="tag">Support</p>
<h1>Help for scans, care plans, billing, and account questions.</h1>
<p className="support-lead">
GreenLens helps users identify plants, understand their condition, and keep a collection organized.
If something breaks or feels unclear, this is the fastest place to start.
</p>
<div className="support-actions">
<a className="btn-primary" href={`mailto:${siteConfig.supportEmail}`}>
Email Support
</a>
<Link className="btn-outline support-outline" href="/privacy">
Privacy Policy
</Link>
</div>
</div>
</section>
<section className="support-grid-wrap">
<div className="container support-grid">
<div className="support-card">
<h2>Contact</h2>
<p>
Email us at <a href={`mailto:${siteConfig.supportEmail}`}>{siteConfig.supportEmail}</a>
<>
<Navbar />
<main className="support-page">
<section className="support-hero">
<div className="container support-hero-inner">
<p style={{ marginBottom: '1rem', opacity: 0.75 }}>
<Link href="/">Home</Link> / <span>Support</span>
</p>
<p>Include your device type, app version, and what happened right before the issue.</p>
</div>
<div className="support-card">
<h2>Common topics</h2>
<ul className="support-list">
<li>Plant identification issues</li>
<li>Care reminder questions</li>
<li>Subscriptions and credit purchases</li>
<li>Account access and saved data</li>
</ul>
</div>
<div className="support-card">
<h2>Legal</h2>
<p>
Review our <Link href="/privacy">Privacy Policy</Link> and <Link href="/imprint">Imprint</Link>.
<p className="tag">Support</p>
<h1>Help for scans, care plans, billing, and account questions.</h1>
<p className="support-lead">
GreenLens helps users identify plants, understand their condition, and keep a collection organized.
If something breaks or feels unclear, this is the fastest place to start.
</p>
<p>These links should be used in App Store Connect before submission.</p>
<div className="support-actions">
<a className="btn-primary" href={`mailto:${siteConfig.supportEmail}`}>
Email Support
</a>
<Link className="btn-outline support-outline" href="/privacy">
Privacy Policy
</Link>
</div>
</div>
</div>
</section>
</section>
<section className="support-faq">
<div className="container">
<div className="support-section-head">
<p className="tag">FAQ</p>
<h2>Quick answers before you write support.</h2>
</div>
<section className="support-grid-wrap">
<div className="container support-grid">
<div className="support-card">
<h2>Contact</h2>
<p>
Email us at <a href={`mailto:${siteConfig.supportEmail}`}>{siteConfig.supportEmail}</a>
</p>
<p>Include your device type, app version, and what happened right before the issue.</p>
</div>
<div className="support-faq-list">
{faqs.map((item) => (
<article key={item.question} className="support-faq-item">
<h3>{item.question}</h3>
<p>{item.answer}</p>
</article>
))}
<div className="support-card">
<h2>Common topics</h2>
<ul className="support-list">
<li>Plant identification issues</li>
<li>Care reminder questions</li>
<li>Subscriptions and credit purchases</li>
<li>Account access and saved data</li>
</ul>
</div>
<div className="support-card">
<h2>Legal</h2>
<p>
Review our <Link href="/privacy">Privacy Policy</Link>, <Link href="/terms">Terms of Service</Link>,
and <Link href="/imprint">Imprint</Link>.
</p>
<p>These links should be used in App Store Connect before submission.</p>
</div>
</div>
</div>
</section>
</main>
</section>
<section className="support-faq">
<div className="container">
<div className="support-section-head">
<p className="tag">FAQ</p>
<h2>Quick answers before you write support.</h2>
</div>
<div className="support-faq-list">
{faqs.map((item) => (
<article key={item.question} className="support-faq-item">
<h3>{item.question}</h3>
<p>{item.answer}</p>
</article>
))}
</div>
</div>
</section>
</main>
<Footer />
</>
)
}

View File

@@ -0,0 +1,51 @@
'use client'
import Link from 'next/link'
import { useLang } from '@/context/LangContext'
import { siteConfig } from '@/lib/site'
const CONTENT = {
de: {
title: 'Nutzungsbedingungen',
intro: 'Diese Bedingungen regeln die Nutzung von GreenLens und der dazugehoerigen Services.',
section1: 'GreenLens wird als digitale App und Web-Service fuer Pflanzenscans, Informationen und accountbezogene Funktionen bereitgestellt.',
section2: 'Vor dem Livegang muessen diese Bedingungen durch rechtlich gepruefte und vollstaendige Vertragstexte ersetzt werden.',
contactLabel: 'Kontakt',
},
en: {
title: 'Terms of Service',
intro: 'These terms govern the use of GreenLens and its related services.',
section1: 'GreenLens is provided as a digital app and web service for plant scans, information, and account-related functionality.',
section2: 'Before launch, replace this placeholder with legally reviewed and complete terms for your business.',
contactLabel: 'Contact',
},
es: {
title: 'Terminos del Servicio',
intro: 'Estos terminos regulan el uso de GreenLens y sus servicios relacionados.',
section1: 'GreenLens se ofrece como app y servicio web para escaneo de plantas, informacion y funciones de cuenta.',
section2: 'Antes del lanzamiento, sustituye este texto por terminos completos revisados legalmente.',
contactLabel: 'Contacto',
},
}
export default function TermsContent() {
const { lang } = useLang()
const c = CONTENT[lang]
return (
<main className="container" style={{ paddingTop: '8rem', paddingBottom: '8rem', maxWidth: '800px' }}>
<p style={{ marginBottom: '1rem', opacity: 0.75 }}>
<Link href="/">Home</Link> / <span>Legal</span> / <span>{c.title}</span>
</p>
<h1>{c.title}</h1>
<div style={{ marginTop: '2rem', lineHeight: '1.8', opacity: 0.9 }}>
<p>{c.intro}</p>
<p>{c.section1}</p>
<p>{c.section2}</p>
<p>
<strong>{c.contactLabel}:</strong> <a href={`mailto:${siteConfig.legalEmail}`}>{siteConfig.legalEmail}</a>
</p>
</div>
</main>
)
}

View File

@@ -1,47 +1,37 @@
'use client'
import { useLang } from '@/context/LangContext'
import type { Metadata } from 'next'
import Navbar from '@/components/Navbar'
import Footer from '@/components/Footer'
import TermsContent from './TermsContent'
import { siteConfig } from '@/lib/site'
const CONTENT = {
de: {
title: 'Nutzungsbedingungen',
intro: 'Diese Bedingungen regeln die Nutzung von GreenLens und der dazugehoerigen Services.',
section1: 'GreenLens wird als digitale App und Web-Service fuer Pflanzenscans, Informationen und accountbezogene Funktionen bereitgestellt.',
section2: 'Vor dem Livegang muessen diese Bedingungen durch rechtlich gepruefte und vollstaendige Vertragstexte ersetzt werden.',
contactLabel: 'Kontakt',
const title = 'Terms of Service'
const description = 'Review the current GreenLens terms governing use of the app and related services.'
export const metadata: Metadata = {
title,
description,
alternates: {
canonical: '/terms',
},
en: {
title: 'Terms of Service',
intro: 'These terms govern the use of GreenLens and its related services.',
section1: 'GreenLens is provided as a digital app and web service for plant scans, information, and account-related functionality.',
section2: 'Before launch, replace this placeholder with legally reviewed and complete terms for your business.',
contactLabel: 'Contact',
openGraph: {
title: `${title} | GreenLens`,
description,
url: `${siteConfig.domain}/terms`,
type: 'website',
},
es: {
title: 'Terminos del Servicio',
intro: 'Estos terminos regulan el uso de GreenLens y sus servicios relacionados.',
section1: 'GreenLens se ofrece como app y servicio web para escaneo de plantas, informacion y funciones de cuenta.',
section2: 'Antes del lanzamiento, sustituye este texto por terminos completos revisados legalmente.',
contactLabel: 'Contacto',
twitter: {
card: 'summary_large_image',
title: `${title} | GreenLens`,
description,
},
}
export default function TermsPage() {
const { lang } = useLang()
const c = CONTENT[lang]
return (
<main className="container" style={{ paddingTop: '8rem', paddingBottom: '8rem', maxWidth: '800px' }}>
<h1>{c.title}</h1>
<div style={{ marginTop: '2rem', lineHeight: '1.8', opacity: 0.9 }}>
<p>{c.intro}</p>
<p>{c.section1}</p>
<p>{c.section2}</p>
<p>
<strong>{c.contactLabel}:</strong> <a href={`mailto:${siteConfig.legalEmail}`}>{siteConfig.legalEmail}</a>
</p>
</div>
</main>
<>
<Navbar />
<TermsContent />
<Footer />
</>
)
}

View File

@@ -38,7 +38,7 @@ export default function CTA() {
</div>
</a>
<a
href={hasAndroidStoreUrl ? siteConfig.androidPlayStoreUrl : `mailto:${siteConfig.supportEmail}`}
href={hasAndroidStoreUrl ? siteConfig.androidPlayStoreUrl : '/support'}
className="store-btn"
id="cta-googleplay"
aria-label="Google Play or contact"

View File

@@ -1,17 +1,19 @@
'use client'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { useLang } from '@/context/LangContext'
import { siteConfig } from '@/lib/site'
const LINK_HREFS = [
['#features', '#intelligence', '#cta', '/support'],
['/#how', '/#faq', '/support'],
['/imprint', '/privacy'],
]
export default function Footer() {
const pathname = usePathname()
const { t } = useLang()
const homeHref = (hash: string) => (pathname === '/' ? hash : `/${hash}`)
const linkHrefs = [
[homeHref('#features'), homeHref('#intelligence'), homeHref('#cta'), '/support'],
[homeHref('#how'), homeHref('#faq'), '/support'],
['/imprint', '/privacy', '/terms'],
]
return (
<footer className="footer" id="footer">
@@ -28,7 +30,7 @@ export default function Footer() {
<div className="footer-col" key={col.title}>
<div className="footer-col-title">{col.title}</div>
{col.links.map((label, li) => (
<Link key={label} href={LINK_HREFS[ci]?.[li] ?? '/support'}>
<Link key={label} href={linkHrefs[ci]?.[li] ?? '/support'}>
{label}
</Link>
))}
@@ -51,9 +53,9 @@ export default function Footer() {
<div className="footer-bottom">
<p>{t.footer.copy}</p>
<a href={`mailto:${siteConfig.supportEmail}`} className="footer-contact">
{siteConfig.supportEmail}
</a>
<Link href="/support" className="footer-contact">
Support
</Link>
</div>
</div>
</footer>

View File

@@ -1,6 +1,7 @@
'use client'
import Link from 'next/link'
import { usePathname } from 'next/navigation'
import { useEffect, useState } from 'react'
import { useLang } from '@/context/LangContext'
import type { Lang } from '@/lib/i18n'
@@ -14,7 +15,9 @@ const LANGS: { code: Lang; label: string; flag: string }[] = [
export default function Navbar() {
const [scrolled, setScrolled] = useState(false)
const [menuOpen, setMenuOpen] = useState(false)
const pathname = usePathname()
const { lang, setLang, t } = useLang()
const homeHref = (hash: string) => (pathname === '/' ? hash : `/${hash}`)
useEffect(() => {
const onScroll = () => setScrolled(window.scrollY > 40)
@@ -30,12 +33,12 @@ export default function Navbar() {
</Link>
<div className={`nav-links${menuOpen ? ' nav-links--open' : ''}`}>
<a href="#features" onClick={() => setMenuOpen(false)}>{t.nav.features}</a>
<a href="#intelligence" onClick={() => setMenuOpen(false)}>{t.nav.tech}</a>
<a href="#faq" onClick={() => setMenuOpen(false)}>FAQ</a>
<a href="#how" onClick={() => setMenuOpen(false)}>{t.nav.how}</a>
<a href={homeHref('#features')} onClick={() => setMenuOpen(false)}>{t.nav.features}</a>
<a href={homeHref('#intelligence')} onClick={() => setMenuOpen(false)}>{t.nav.tech}</a>
<a href={homeHref('#faq')} onClick={() => setMenuOpen(false)}>FAQ</a>
<a href={homeHref('#how')} onClick={() => setMenuOpen(false)}>{t.nav.how}</a>
<Link href="/support" onClick={() => setMenuOpen(false)}>Support</Link>
<a href="#cta" onClick={() => setMenuOpen(false)}>{t.nav.download}</a>
<a href={homeHref('#cta')} onClick={() => setMenuOpen(false)}>{t.nav.download}</a>
<div className="lang-switcher" role="group" aria-label="Language selector">
{LANGS.map((l) => (
@@ -55,7 +58,7 @@ export default function Navbar() {
))}
</div>
<a href="#cta" className="nav-cta" onClick={() => setMenuOpen(false)}>{t.nav.cta}</a>
<a href={homeHref('#cta')} className="nav-cta" onClick={() => setMenuOpen(false)}>{t.nav.cta}</a>
</div>
<button

View File

@@ -63,7 +63,7 @@ export const translations = {
cols: [
{ title: 'Produkt', links: ['Features', 'Technologie', 'App laden', 'Support'] },
{ title: 'Ressourcen', links: ['So funktioniert es', 'FAQ', 'Support'] },
{ title: 'Rechtliches', links: ['Impressum', 'Datenschutz'] },
{ title: 'Rechtliches', links: ['Impressum', 'Datenschutz', 'Nutzungsbedingungen'] },
],
},
},
@@ -129,7 +129,7 @@ export const translations = {
cols: [
{ title: 'Product', links: ['Features', 'Technology', 'Get the App', 'Support'] },
{ title: 'Resources', links: ['How it works', 'FAQ', 'Support'] },
{ title: 'Legal', links: ['Imprint', 'Privacy Policy'] },
{ title: 'Legal', links: ['Imprint', 'Privacy Policy', 'Terms of Service'] },
],
},
},
@@ -195,7 +195,7 @@ export const translations = {
cols: [
{ title: 'Producto', links: ['Funciones', 'Tecnologia', 'Descargar', 'Support'] },
{ title: 'Recursos', links: ['Como funciona', 'FAQ', 'Support'] },
{ title: 'Legal', links: ['Aviso legal', 'Privacidad'] },
{ title: 'Legal', links: ['Aviso legal', 'Privacidad', 'Terminos del servicio'] },
],
},
},

33
greenlns-landing/proxy.ts Normal file
View File

@@ -0,0 +1,33 @@
import type { NextRequest } from 'next/server'
import { NextResponse } from 'next/server'
const APEX_HOST = 'greenlenspro.com'
const WWW_HOST = `www.${APEX_HOST}`
export function proxy(request: NextRequest) {
const host = request.headers.get('host')
const forwardedProto = request.headers.get('x-forwarded-proto')
if (!host || (host !== APEX_HOST && host !== WWW_HOST)) {
return NextResponse.next()
}
const url = request.nextUrl.clone()
let shouldRedirect = false
if (host === WWW_HOST) {
url.host = APEX_HOST
shouldRedirect = true
}
if (forwardedProto === 'http' || url.protocol === 'http:') {
url.protocol = 'https:'
shouldRedirect = true
}
return shouldRedirect ? NextResponse.redirect(url, 308) : NextResponse.next()
}
export const config = {
matcher: '/:path*',
}