Compare commits
17 Commits
db48e03cb9
...
v2.3.22-rc
| Author | SHA1 | Date | |
|---|---|---|---|
| ac437e19f3 | |||
| 5a61ebf269 | |||
| 6b8ae11e45 | |||
| 071fba540d | |||
| 04fb328850 | |||
| 84da0e57e5 | |||
| ad3708e29e | |||
| 8b0cd6028b | |||
| 94627ba20e | |||
| a782fdfe53 | |||
| f17a8c8c36 | |||
| adbb2419b7 | |||
| 5809291741 | |||
| e263e7f25c | |||
| 1fee592ee7 | |||
| ff73aa7c4e | |||
| 26f6ddc098 |
@@ -394,10 +394,11 @@ jobs:
|
|||||||
run: |
|
run: |
|
||||||
pnpm store prune
|
pnpm store prune
|
||||||
pnpm install --no-frozen-lockfile
|
pnpm install --no-frozen-lockfile
|
||||||
- name: 🌐 Install Chrome & Dependencies
|
- name: Setup Chrome
|
||||||
run: |
|
id: setup-chrome
|
||||||
apt-get update && apt-get install -y libnss3 libnspr4 libatk1.0-0 libatk-bridge2.0-0 libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 libxext6 libxfixes3 libxrandr2 libgbm1 libasound2t64 libpango-1.0-0 libcairo2
|
uses: browser-actions/setup-chrome@v1
|
||||||
npx puppeteer browsers install chrome
|
with:
|
||||||
|
install-dependencies: true
|
||||||
|
|
||||||
# ── Minimalist Smoke Test ──────────────────────────────────────────
|
# ── Minimalist Smoke Test ──────────────────────────────────────────
|
||||||
- name: 🌐 Smoke Test (Essential Flow)
|
- name: 🌐 Smoke Test (Essential Flow)
|
||||||
@@ -405,6 +406,7 @@ jobs:
|
|||||||
env:
|
env:
|
||||||
NEXT_PUBLIC_BASE_URL: ${{ needs.prepare.outputs.next_public_url }}
|
NEXT_PUBLIC_BASE_URL: ${{ needs.prepare.outputs.next_public_url }}
|
||||||
GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD || 'klz2026' }}
|
GATEKEEPER_PASSWORD: ${{ secrets.GATEKEEPER_PASSWORD || 'klz2026' }}
|
||||||
|
PUPPETEER_EXECUTABLE_PATH: ${{ steps.setup-chrome.outputs.chrome-path }}
|
||||||
run: npx tsx scripts/smoke.ts
|
run: npx tsx scripts/smoke.ts
|
||||||
|
|
||||||
# ──────────────────────────────────────────────────────────────────────────────
|
# ──────────────────────────────────────────────────────────────────────────────
|
||||||
|
|||||||
5
.gitignore
vendored
@@ -2,10 +2,7 @@ node_modules
|
|||||||
.next
|
.next
|
||||||
.DS_Store
|
.DS_Store
|
||||||
.pnpm-store
|
.pnpm-store
|
||||||
public/uploads
|
# lighthouse CI
|
||||||
public/media
|
|
||||||
|
|
||||||
# Lighthouse CI
|
|
||||||
.lighthouseci/
|
.lighthouseci/
|
||||||
lighthouserc.cjs
|
lighthouserc.cjs
|
||||||
.lighthouserc.json
|
.lighthouserc.json
|
||||||
|
|||||||
12
Dockerfile
@@ -59,10 +59,10 @@ RUN pnpm build
|
|||||||
FROM node:20-alpine AS runner
|
FROM node:20-alpine AS runner
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
# Install curl for health checks
|
# Install dependencies for health checks and system compatibility
|
||||||
RUN apk add --no-cache curl
|
RUN apk add --no-cache libc6-compat curl
|
||||||
|
|
||||||
# Create nextjs user and group (standardized in runtime image but ensuring local ownership)
|
# Create nextjs user and group
|
||||||
RUN addgroup --system --gid 1001 nodejs && \
|
RUN addgroup --system --gid 1001 nodejs && \
|
||||||
adduser --system --uid 1001 nextjs && \
|
adduser --system --uid 1001 nextjs && \
|
||||||
chown -R nextjs:nodejs /app
|
chown -R nextjs:nodejs /app
|
||||||
@@ -73,10 +73,10 @@ ENV HOSTNAME="0.0.0.0"
|
|||||||
ENV PORT=3000
|
ENV PORT=3000
|
||||||
ENV NODE_ENV=production
|
ENV NODE_ENV=production
|
||||||
|
|
||||||
# Copy standalone output and static files
|
# Copy standalone output
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
|
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./
|
||||||
|
# Copy public and static files
|
||||||
|
COPY --from=builder --chown=nextjs:nodejs /app/public ./public
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/static
|
||||||
COPY --from=builder --chown=nextjs:nodejs /app/.next/cache ./.next/cache
|
|
||||||
|
|
||||||
CMD ["node", "server.js"]
|
CMD ["node", "server.js"]
|
||||||
|
|||||||
@@ -45,6 +45,28 @@ export async function sendContactFormAction(formData: FormData) {
|
|||||||
email,
|
email,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 1.5. Simple Fail-Safe Backup to Disk
|
||||||
|
// To ensure leads are never lost if email fails or Gotify is down, we append them to a local JSON Lines file.
|
||||||
|
try {
|
||||||
|
const fs = await import('fs');
|
||||||
|
const path = await import('path');
|
||||||
|
const backupDir = path.join(process.cwd(), '.data');
|
||||||
|
if (!fs.existsSync(backupDir)) fs.mkdirSync(backupDir, { recursive: true });
|
||||||
|
|
||||||
|
const backupFile = path.join(backupDir, 'leads-backup.jsonl');
|
||||||
|
const leadData = {
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
productName,
|
||||||
|
message,
|
||||||
|
};
|
||||||
|
fs.appendFileSync(backupFile, JSON.stringify(leadData) + '\\n');
|
||||||
|
logger.info('Successfully saved lead to local backup file', { backupFile });
|
||||||
|
} catch (backupError) {
|
||||||
|
logger.error('Failed to write to local leads backup', { error: String(backupError) });
|
||||||
|
}
|
||||||
|
|
||||||
// 2. Send Emails
|
// 2. Send Emails
|
||||||
logger.info('Sending branded emails', { email, productName });
|
logger.info('Sending branded emails', { email, productName });
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,27 @@
|
|||||||
import React from 'react';
|
import React from 'react';
|
||||||
import { MDXRemote } from 'next-mdx-remote/rsc';
|
import { MDXRemote } from 'next-mdx-remote/rsc';
|
||||||
|
import { getLocale } from 'next-intl/server';
|
||||||
|
|
||||||
|
// Blog components
|
||||||
|
import TechnicalGrid from './blog/TechnicalGrid';
|
||||||
|
import Stats from './blog/Stats';
|
||||||
|
import VisualLinkPreview from './blog/VisualLinkPreview';
|
||||||
|
import StickyNarrative from './blog/StickyNarrative';
|
||||||
|
import ComparisonGrid from './blog/ComparisonGrid';
|
||||||
|
import HighlightBox from './blog/HighlightBox';
|
||||||
|
import ChatBubble from './blog/ChatBubble';
|
||||||
|
|
||||||
|
// Home components
|
||||||
|
import Hero from './home/Hero';
|
||||||
|
import ProductCategories from './home/ProductCategories';
|
||||||
|
import WhatWeDo from './home/WhatWeDo';
|
||||||
|
import RecentPosts from './home/RecentPosts';
|
||||||
|
import Experience from './home/Experience';
|
||||||
|
import WhyChooseUs from './home/WhyChooseUs';
|
||||||
|
import MeetTheTeam from './home/MeetTheTeam';
|
||||||
|
import GallerySection from './home/GallerySection';
|
||||||
|
import VideoSection from './home/VideoSection';
|
||||||
|
import CTA from './home/CTA';
|
||||||
function ContactSection(props: any) {
|
function ContactSection(props: any) {
|
||||||
return (
|
return (
|
||||||
<div className="p-8 border-2 border-dashed border-primary my-8 text-center text-primary font-bold">
|
<div className="p-8 border-2 border-dashed border-primary my-8 text-center text-primary font-bold">
|
||||||
@@ -17,10 +39,62 @@ function HeroSection(props: any) {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Block(props: any) {
|
async function Block(props: any) {
|
||||||
return (
|
const { type, data, children } = props;
|
||||||
<div className="p-4 border-2 border-dashed border-gray-300">Unknown Block: {props.type}</div>
|
const locale = await getLocale();
|
||||||
);
|
|
||||||
|
// Normalize type (Payload sometimes uses blockType in data)
|
||||||
|
const blockType = type || data?.blockType;
|
||||||
|
|
||||||
|
switch (blockType) {
|
||||||
|
// Blog Components
|
||||||
|
case 'technicalGrid':
|
||||||
|
return <TechnicalGrid {...data} />;
|
||||||
|
case 'stats':
|
||||||
|
return <Stats {...data} />;
|
||||||
|
case 'visualLinkPreview':
|
||||||
|
return <VisualLinkPreview {...data} />;
|
||||||
|
case 'stickyNarrative':
|
||||||
|
return <StickyNarrative {...data} />;
|
||||||
|
case 'comparisonGrid':
|
||||||
|
return <ComparisonGrid {...data} />;
|
||||||
|
case 'highlightBox':
|
||||||
|
return <HighlightBox {...data}>{children}</HighlightBox>;
|
||||||
|
case 'chatBubble':
|
||||||
|
return <ChatBubble {...data}>{children}</ChatBubble>;
|
||||||
|
|
||||||
|
// Home Components
|
||||||
|
case 'homeHero':
|
||||||
|
return <Hero data={data} />;
|
||||||
|
case 'homeProductCategories':
|
||||||
|
return <ProductCategories data={data} />;
|
||||||
|
case 'homeWhatWeDo':
|
||||||
|
return <WhatWeDo data={data} />;
|
||||||
|
case 'homeRecentPosts':
|
||||||
|
return <RecentPosts data={data} locale={locale} />;
|
||||||
|
case 'homeExperience':
|
||||||
|
return <Experience data={data} />;
|
||||||
|
case 'homeWhyChooseUs':
|
||||||
|
return <WhyChooseUs data={data} />;
|
||||||
|
case 'homeMeetTheTeam':
|
||||||
|
return <MeetTheTeam data={data} />;
|
||||||
|
case 'homeGallery':
|
||||||
|
return <GallerySection data={data} />;
|
||||||
|
case 'homeVideo':
|
||||||
|
return <VideoSection data={data} />;
|
||||||
|
case 'homeCTA':
|
||||||
|
return <CTA data={data} />;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<div className="p-8 border-2 border-dashed border-red-200 rounded-2xl my-8 bg-red-50 text-red-600">
|
||||||
|
<p className="font-bold mb-2">Unknown Block Type: {blockType}</p>
|
||||||
|
<pre className="text-xs overflow-auto p-4 bg-white/50 rounded">
|
||||||
|
{JSON.stringify({ type, data: !!data, hasChildren: !!children }, null, 2)}
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const components = {
|
const components = {
|
||||||
|
|||||||
@@ -12,8 +12,6 @@ services:
|
|||||||
environment:
|
environment:
|
||||||
POSTGRES_URI: postgres://${PAYLOAD_DB_USER:-payload}:${PAYLOAD_DB_PASSWORD:-payload}@klz-db:5432/${PAYLOAD_DB_NAME:-payload}
|
POSTGRES_URI: postgres://${PAYLOAD_DB_USER:-payload}:${PAYLOAD_DB_PASSWORD:-payload}@klz-db:5432/${PAYLOAD_DB_NAME:-payload}
|
||||||
PAYLOAD_SECRET: ${PAYLOAD_SECRET:-fallback-secret-for-production-needs-change}
|
PAYLOAD_SECRET: ${PAYLOAD_SECRET:-fallback-secret-for-production-needs-change}
|
||||||
volumes:
|
|
||||||
- klz_media_data:/app/public/media
|
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
# HTTP ⇒ HTTPS redirect
|
# HTTP ⇒ HTTPS redirect
|
||||||
|
|||||||
@@ -20,12 +20,13 @@ export default async function middleware(request: NextRequest) {
|
|||||||
|
|
||||||
if (
|
if (
|
||||||
isServerAction ||
|
isServerAction ||
|
||||||
pathname.startsWith('/admin') ||
|
|
||||||
pathname.startsWith('/api') ||
|
pathname.startsWith('/api') ||
|
||||||
pathname.startsWith('/stats') ||
|
pathname.startsWith('/stats') ||
|
||||||
pathname.startsWith('/errors') ||
|
pathname.startsWith('/errors') ||
|
||||||
pathname.startsWith('/health') ||
|
pathname.startsWith('/health') ||
|
||||||
pathname.startsWith('/uploads') ||
|
pathname.startsWith('/uploads') ||
|
||||||
|
pathname.startsWith('/media') ||
|
||||||
|
pathname.startsWith('/_next/image') ||
|
||||||
pathname.includes('/api/og') ||
|
pathname.includes('/api/og') ||
|
||||||
pathname.includes('opengraph-image') ||
|
pathname.includes('opengraph-image') ||
|
||||||
pathname.endsWith('sitemap.xml') ||
|
pathname.endsWith('sitemap.xml') ||
|
||||||
@@ -61,7 +62,7 @@ export default async function middleware(request: NextRequest) {
|
|||||||
if (['GET', 'HEAD'].includes(request.method)) {
|
if (['GET', 'HEAD'].includes(request.method)) {
|
||||||
// Clone headers to ensure all Next.js internal headers (like router state) are preserved
|
// Clone headers to ensure all Next.js internal headers (like router state) are preserved
|
||||||
const clonedHeaders = new Headers(request.headers);
|
const clonedHeaders = new Headers(request.headers);
|
||||||
|
|
||||||
effectiveRequest = new NextRequest(urlObj, {
|
effectiveRequest = new NextRequest(urlObj, {
|
||||||
headers: clonedHeaders,
|
headers: clonedHeaders,
|
||||||
method: request.method,
|
method: request.method,
|
||||||
@@ -112,7 +113,7 @@ export default async function middleware(request: NextRequest) {
|
|||||||
|
|
||||||
export const config = {
|
export const config = {
|
||||||
matcher: [
|
matcher: [
|
||||||
'/((?!api|_next/static|_next/image|favicon.ico|admin|manifest.webmanifest|.*\\.(?:svg|png|jpg|jpeg|gif|webp|pdf|txt|vcf|xml|webm|mp4|map)$).*)',
|
'/((?!api|_next/static|_next/image|favicon.ico|manifest.webmanifest|.*\\.(?:svg|png|jpg|jpeg|gif|webp|pdf|txt|vcf|xml|webm|mp4|map)$).*)',
|
||||||
'/(de|en)/:path*',
|
'/(de|en)/:path*',
|
||||||
],
|
],
|
||||||
};
|
};
|
||||||
|
|||||||
2
next-env.d.ts
vendored
@@ -1,6 +1,6 @@
|
|||||||
/// <reference types="next" />
|
/// <reference types="next" />
|
||||||
/// <reference types="next/image-types/global" />
|
/// <reference types="next/image-types/global" />
|
||||||
import "./.next/dev/types/routes.d.ts";
|
import "./.next/types/routes.d.ts";
|
||||||
|
|
||||||
// NOTE: This file should not be edited
|
// NOTE: This file should not be edited
|
||||||
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.
|
||||||
|
|||||||
@@ -402,8 +402,9 @@ const nextConfig = {
|
|||||||
];
|
];
|
||||||
},
|
},
|
||||||
images: {
|
images: {
|
||||||
|
unoptimized: true,
|
||||||
formats: ['image/webp'],
|
formats: ['image/webp'],
|
||||||
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
|
deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048],
|
||||||
remotePatterns: [
|
remotePatterns: [
|
||||||
{
|
{
|
||||||
protocol: 'https',
|
protocol: 'https',
|
||||||
|
|||||||
@@ -113,7 +113,7 @@
|
|||||||
"prepare": "husky",
|
"prepare": "husky",
|
||||||
"preinstall": "npx only-allow pnpm"
|
"preinstall": "npx only-allow pnpm"
|
||||||
},
|
},
|
||||||
"version": "2.3.22-rc.8",
|
"version": "2.3.22-rc.21",
|
||||||
"pnpm": {
|
"pnpm": {
|
||||||
"onlyBuiltDependencies": [
|
"onlyBuiltDependencies": [
|
||||||
"@parcel/watcher",
|
"@parcel/watcher",
|
||||||
|
|||||||
BIN
public/media/1-1.webp
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
public/media/1-2.webp
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
public/media/1-3.webp
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
public/media/1-4-1024x576.webp
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/media/1-4-600x338.webp
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
public/media/1-4-768x432.webp
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/media/1-4.webp
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
public/media/1-5-1024x576.webp
Normal file
|
After Width: | Height: | Size: 18 KiB |
BIN
public/media/1-5-600x338.webp
Normal file
|
After Width: | Height: | Size: 8.9 KiB |
BIN
public/media/1-5-768x432.webp
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/media/1-5.webp
Normal file
|
After Width: | Height: | Size: 61 KiB |
BIN
public/media/1.webp
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
public/media/1234adws21312-scaled-1024x768.jpg
Normal file
|
After Width: | Height: | Size: 144 KiB |
BIN
public/media/1234adws21312-scaled-600x450.jpg
Normal file
|
After Width: | Height: | Size: 59 KiB |
BIN
public/media/1234adws21312-scaled-768x576.jpg
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
public/media/1234adws21312-scaled.jpg
Normal file
|
After Width: | Height: | Size: 668 KiB |
BIN
public/media/1694273920124-copy-1.webp
Normal file
|
After Width: | Height: | Size: 212 KiB |
BIN
public/media/1694273920124-copy-2-1.webp
Normal file
|
After Width: | Height: | Size: 149 KiB |
BIN
public/media/1694273920124-copy-2-2.webp
Normal file
|
After Width: | Height: | Size: 149 KiB |
BIN
public/media/1694273920124-copy-2-3.webp
Normal file
|
After Width: | Height: | Size: 149 KiB |
BIN
public/media/1694273920124-copy-2.webp
Normal file
|
After Width: | Height: | Size: 149 KiB |
BIN
public/media/1694273920124-copy-3.webp
Normal file
|
After Width: | Height: | Size: 212 KiB |
BIN
public/media/1694273920124-copy.webp
Normal file
|
After Width: | Height: | Size: 212 KiB |
BIN
public/media/1759325528650-1.webp
Normal file
|
After Width: | Height: | Size: 316 KiB |
BIN
public/media/1759325528650-2.webp
Normal file
|
After Width: | Height: | Size: 316 KiB |
BIN
public/media/1759325528650-3.webp
Normal file
|
After Width: | Height: | Size: 316 KiB |
BIN
public/media/1759325528650-4-1024x769.webp
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
public/media/1759325528650-4-600x450.webp
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
public/media/1759325528650-4-768x577.webp
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
public/media/1759325528650-4.webp
Normal file
|
After Width: | Height: | Size: 278 KiB |
BIN
public/media/1759325528650-5-1024x769.webp
Normal file
|
After Width: | Height: | Size: 93 KiB |
BIN
public/media/1759325528650-5-600x450.webp
Normal file
|
After Width: | Height: | Size: 40 KiB |
BIN
public/media/1759325528650-5-768x577.webp
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
public/media/1759325528650-5.webp
Normal file
|
After Width: | Height: | Size: 278 KiB |
BIN
public/media/1759325528650.webp
Normal file
|
After Width: | Height: | Size: 316 KiB |
BIN
public/media/3-1.webp
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
public/media/3-2.webp
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
public/media/3-3.webp
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
public/media/3.webp
Normal file
|
After Width: | Height: | Size: 92 KiB |
BIN
public/media/5-1.webp
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
public/media/5-2.webp
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
public/media/5-3.webp
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
public/media/5-4-1024x576.webp
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
public/media/5-4-600x338.webp
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
public/media/5-4-768x432.webp
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/media/5-4.webp
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
public/media/5-5-1024x576.webp
Normal file
|
After Width: | Height: | Size: 19 KiB |
BIN
public/media/5-5-600x338.webp
Normal file
|
After Width: | Height: | Size: 9.0 KiB |
BIN
public/media/5-5-768x432.webp
Normal file
|
After Width: | Height: | Size: 12 KiB |
BIN
public/media/5-5.webp
Normal file
|
After Width: | Height: | Size: 72 KiB |
BIN
public/media/5.webp
Normal file
|
After Width: | Height: | Size: 90 KiB |
BIN
public/media/AGB KLZ 3-2026.pdf
Normal file
BIN
public/media/AGB-KLZ-1-2026.pdf
Normal file
BIN
public/media/AVB-KLZ-4-2026.pdf
Normal file
BIN
public/media/DSC07387-Large-600x400-1.webp
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
public/media/DSC07387-Large-600x400-2.webp
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
public/media/DSC07387-Large-600x400-3.webp
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
public/media/DSC07387-Large-600x400.webp
Normal file
|
After Width: | Height: | Size: 26 KiB |
BIN
public/media/DSC07433-Large-600x400-1.webp
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
public/media/DSC07433-Large-600x400-2.webp
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
public/media/DSC07433-Large-600x400-3.webp
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
public/media/DSC07433-Large-600x400.webp
Normal file
|
After Width: | Height: | Size: 25 KiB |
BIN
public/media/DSC07460-Large-600x400-1.webp
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
public/media/DSC07460-Large-600x400-2.webp
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
public/media/DSC07460-Large-600x400-3.webp
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
public/media/DSC07460-Large-600x400.webp
Normal file
|
After Width: | Height: | Size: 37 KiB |
BIN
public/media/DSC07469-Large-600x400-1.webp
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
public/media/DSC07469-Large-600x400-2.webp
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
public/media/DSC07469-Large-600x400-3.webp
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
public/media/DSC07469-Large-600x400.webp
Normal file
|
After Width: | Height: | Size: 34 KiB |
BIN
public/media/DSC07539-Large-600x400-1.webp
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
public/media/DSC07539-Large-600x400-2.webp
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
public/media/DSC07539-Large-600x400-3.webp
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
public/media/DSC07539-Large-600x400.webp
Normal file
|
After Width: | Height: | Size: 38 KiB |
BIN
public/media/DSC07655-Large-1.webp
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
public/media/DSC07655-Large-2.webp
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
public/media/DSC07655-Large-3.webp
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
public/media/DSC07655-Large.webp
Normal file
|
After Width: | Height: | Size: 76 KiB |
BIN
public/media/DSC07768-Large-1.webp
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
public/media/DSC07768-Large-2.webp
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
public/media/DSC07768-Large-3.webp
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
public/media/DSC07768-Large.webp
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
public/media/DSC07963-Large-1.webp
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/media/DSC07963-Large-2.webp
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/media/DSC07963-Large-3.webp
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/media/DSC07963-Large.webp
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
public/media/DSC08036-Large-1.webp
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
public/media/DSC08036-Large-2.webp
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
public/media/DSC08036-Large-3.webp
Normal file
|
After Width: | Height: | Size: 75 KiB |
BIN
public/media/DSC08036-Large.webp
Normal file
|
After Width: | Height: | Size: 75 KiB |