fix: footer icons and sections, updated content security policy

This commit is contained in:
Jeffrey 2026-02-20 13:58:08 +01:00
parent b42d1729be
commit b1abf9209f
9 changed files with 64 additions and 20 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 122 KiB

BIN
media/iso_sbcert.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

BIN
media/uc-sigill-lm.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 366 KiB

View File

@ -1,5 +1,4 @@
import { withPayload } from '@payloadcms/next/withPayload' import { withPayload } from '@payloadcms/next/withPayload'
import redirects from './redirects.js' import redirects from './redirects.js'
const NEXT_PUBLIC_SERVER_URL = process.env.VERCEL_PROJECT_PRODUCTION_URL const NEXT_PUBLIC_SERVER_URL = process.env.VERCEL_PROJECT_PRODUCTION_URL
@ -8,12 +7,33 @@ const NEXT_PUBLIC_SERVER_URL = process.env.VERCEL_PROJECT_PRODUCTION_URL
const ContentSecurityPolicy = ` const ContentSecurityPolicy = `
default-src 'self'; default-src 'self';
script-src 'self' 'unsafe-inline' https://layerandmesh.lime-forms.com https://matomo.layermesh.se https://maps.googleapis.com; script-src 'self' 'unsafe-inline'
style-src 'self' 'unsafe-inline' https://layerandmesh.lime-forms.com https://fonts.googleapis.com; https://layerandmesh.lime-forms.com
img-src 'self' data: blob: https://matomo.layermesh.se https://img.youtube.com https://i.vimeocdn.com https://maps.googleapis.com https://maps.gstatic.com; https://matomo.layermesh.se
font-src 'self' https://fonts.gstatic.com; https://maps.googleapis.com
connect-src 'self' https://matomo.layermesh.se https://layerandmesh.lime-forms.com https://maps.googleapis.com; https://maps.gstatic.com;
frame-src 'self' https://www.youtube.com https://www.youtube-nocookie.com https://player.vimeo.com https://www.google.com https://maps.google.com; style-src 'self' 'unsafe-inline'
https://layerandmesh.lime-forms.com;
img-src 'self' data: blob:
https://matomo.layermesh.se
https://img.youtube.com
https://*.vimeocdn.com
https://maps.googleapis.com
https://maps.gstatic.com;
font-src 'self';
connect-src 'self'
https://matomo.layermesh.se
https://layerandmesh.lime-forms.com
https://maps.googleapis.com
https://*.googleapis.com;
frame-src 'self'
https://www.youtube.com
https://www.youtube-nocookie.com
https://player.vimeo.com
https://www.google.com
https://maps.google.com;
worker-src 'self';
media-src 'self' https://player.vimeo.com;
object-src 'none'; object-src 'none';
base-uri 'self'; base-uri 'self';
form-action 'self' https://layerandmesh.lime-forms.com; form-action 'self' https://layerandmesh.lime-forms.com;
@ -26,6 +46,10 @@ const securityHeaders = [
key: 'Content-Security-Policy', key: 'Content-Security-Policy',
value: ContentSecurityPolicy.replace(/\s{2,}/g, ' ').trim(), value: ContentSecurityPolicy.replace(/\s{2,}/g, ' ').trim(),
}, },
{
key: 'Strict-Transport-Security',
value: 'max-age=63072000; includeSubDomains',
},
{ {
key: 'X-Frame-Options', key: 'X-Frame-Options',
value: 'SAMEORIGIN', value: 'SAMEORIGIN',
@ -40,7 +64,7 @@ const securityHeaders = [
}, },
{ {
key: 'Permissions-Policy', key: 'Permissions-Policy',
value: 'camera=(), microphone=(), geolocation=()', value: 'camera=(), microphone=(), geolocation=(), payment=(), usb=(), accelerometer=(), gyroscope=()',
}, },
] ]
@ -52,9 +76,8 @@ const nextConfig = {
}, },
images: { images: {
remotePatterns: [ remotePatterns: [
...[NEXT_PUBLIC_SERVER_URL /* 'https://example.com' */].map((item) => { ...[NEXT_PUBLIC_SERVER_URL].map((item) => {
const url = new URL(item) const url = new URL(item)
return { return {
hostname: url.hostname, hostname: url.hostname,
protocol: url.protocol.replace(':', ''), protocol: url.protocol.replace(':', ''),
@ -68,7 +91,6 @@ const nextConfig = {
'.js': ['.ts', '.tsx', '.js', '.jsx'], '.js': ['.ts', '.tsx', '.js', '.jsx'],
'.mjs': ['.mts', '.mjs'], '.mjs': ['.mts', '.mjs'],
} }
return webpackConfig return webpackConfig
}, },
reactStrictMode: true, reactStrictMode: true,
@ -83,4 +105,4 @@ const nextConfig = {
}, },
} }
export default withPayload(nextConfig, { devBundleServerPackages: false }) export default withPayload(nextConfig, { devBundleServerPackages: false })

View File

@ -63,7 +63,7 @@ export async function Footer() {
</Link> </Link>
{certMarks.length > 0 && ( {certMarks.length > 0 && (
<div className="flex items-center gap-4 flex-wrap"> <div className="hidden sm:flex items-center gap-5 flex-wrap">
{certMarks.map((mark, i) => { {certMarks.map((mark, i) => {
const media = mark.image as Media | null | undefined const media = mark.image as Media | null | undefined
if (!media) return null if (!media) return null
@ -71,7 +71,7 @@ export async function Footer() {
<FDImage <FDImage
media={media} media={media}
size="thumbnail" size="thumbnail"
className="h-12 w-auto object-contain" className="h-16 w-auto object-contain"
fallbackAlt={mark.alt || ''} fallbackAlt={mark.alt || ''}
/> />
) )
@ -81,14 +81,12 @@ export async function Footer() {
href={mark.linkUrl} href={mark.linkUrl}
target="_blank" target="_blank"
rel="noopener noreferrer" rel="noopener noreferrer"
className="opacity-80 hover:opacity-100 transition-opacity" className="transition-opacity hover:opacity-80"
> >
{imgEl} {imgEl}
</a> </a>
) : ( ) : (
<span key={i} className="opacity-80"> <span key={i}>{imgEl}</span>
{imgEl}
</span>
) )
})} })}
</div> </div>
@ -125,7 +123,31 @@ export async function Footer() {
{/* Left + center: copyright text and legal nav links */} {/* Left + center: copyright text and legal nav links */}
<div className="flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-6 flex-wrap"> <div className="flex flex-col sm:flex-row sm:items-center gap-3 sm:gap-6 flex-wrap">
<p className="font-joey text-white/60 text-sm whitespace-nowrap">{bottomLeft}</p> {/* Mobile-only cert marks — shown above copyright, hidden on sm+ (handled in top bar) */}
{certMarks.length > 0 && (
<div className="flex sm:hidden items-center gap-4 flex-wrap mb-1">
{certMarks.map((mark, i) => {
const media = mark.image as Media | null | undefined
if (!media) return null
const imgEl = (
<FDImage
media={media}
size="thumbnail"
className="h-14 w-auto object-contain"
fallbackAlt={mark.alt || ''}
/>
)
return mark.linkUrl ? (
<a key={i} href={mark.linkUrl} target="_blank" rel="noopener noreferrer">
{imgEl}
</a>
) : (
<span key={i}>{imgEl}</span>
)
})}
</div>
)}
<p className="font-joey text-white/60 text-base whitespace-nowrap">{bottomLeft}</p>
{navItems.length > 0 && ( {navItems.length > 0 && (
<nav className="flex items-center gap-4 flex-wrap"> <nav className="flex items-center gap-4 flex-wrap">
{navItems.map(({ link }, i) => ( {navItems.map(({ link }, i) => (

File diff suppressed because one or more lines are too long