world-game/src/components/layout/Footer.svelte
isUnknown 44af8a9b4e add privacy page as standalone SPA view outside slide navigation
- New Kirby template/blueprint/JSON for privacy page (confidentialite slug)
- Standalone page state in slides store + router handling for non-nav pages
- Privacy.svelte view with background image, text blocks, footer
- Centralize vertical lines in App.svelte as fixed elements with per-slide visibility
- Footer privacy link language-aware (FR/EN)
- Portfolio mockup fix: read from default language for consistent EN display
- menu.php: add privacy page to Kirby panel navigation

refs #44

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-30 18:43:35 +02:00

260 lines
5.9 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<script>
import { site } from '@state/site.svelte'
import { locale } from '@state/locale.svelte'
import { t } from '@i18n'
const logo = $derived(site.logo)
const title = $derived(site.title || 'World Game')
const contact = $derived(site.contact || {})
const socials = $derived(contact.socials ?? [])
const year = new Date().getFullYear()
let email = $state('')
let status = $state(null)
async function handleSubscribe(e) {
e.preventDefault()
if (!email) return
try {
const res = await fetch('/newsletter.json', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email })
})
if (res.ok) {
status = { type: 'success', message: t('newsletter_success') }
email = ''
} else {
status = { type: 'error', message: t('newsletter_error') }
}
} catch {
status = { type: 'error', message: t('newsletter_error') }
}
}
</script>
<footer class="page-scrollable-footer">
<div class="footer-main">
<!-- Logo -->
{#if logo}
<div class="footer-brand">
<img src={logo} alt={title} />
</div>
{/if}
<!-- Location -->
{#if contact.address}
<div class="footer-col">
<p class="footer-label">{t('location')}</p>
<a href="https://www.google.com/search?q=78+Rue+de+Provence,+75009+Paris&shndl=40&source=sh/x/loc/geo/m1/1&kgs=6f7d56e06f71cc73" target="_blank">
<address>{@html contact.address}</address>
</a>
</div>
{/if}
<!-- Contact -->
{#if contact.email}
<div class="footer-col">
<p class="footer-label">{t('contact')}</p>
<a href="mailto:{contact.email}">{contact.email}</a>
</div>
{/if}
<!-- Follow us -->
{#if socials.length > 0}
<div class="footer-col">
<p class="footer-label">{t('follow_us')}</p>
<ul class="footer-socials" role="list">
{#each socials as social}
<li>
<a href={social.url} target="_blank" rel="noopener noreferrer" aria-label={social.label}>
{#if social.picto}
{@html social.picto}
{:else}
{social.label}
{/if}
</a>
</li>
{/each}
</ul>
</div>
{/if}
</div>
<!-- Copyright bar -->
<div class="footer-bottom">
<p>{t('copyright', { year })}</p>
<div class="footer-divider" aria-hidden="true"></div>
<a href="/cookies">{t('cookies')}</a>
<div class="footer-divider" aria-hidden="true"></div>
{#if contact.legalNotice}
<a href={contact.legalNotice} target="_blank" rel="noopener noreferrer">{t('legal')}</a>
{/if}
<div class="footer-divider" aria-hidden="true"></div>
<a href={locale.current === 'en' ? '/en/confidentialite' : '/confidentialite'}>{t('privacy')}</a>
</div>
</footer>
<style>
footer {
width: 100vw;
background: #0d0e22;
z-index: 2;
}
:global(.collection .page-scrollable-footer) {
margin-left: -12.4vw;
margin-top: 5rem;
}
:global(.article-wrapper .page-scrollable-footer) {
margin-left: -2.95vw;
}
/* --- Main row --- */
.footer-main {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24px 45px;
border-top: 1px solid rgba(255, 255, 255, 0.5);
}
/* --- Brand --- */
.footer-brand img {
width: 60px;
}
/* --- Info columns --- */
.footer-col {
display: flex;
flex-direction: column;
gap: 0.5rem;
width: 9rem;
text-align: left;
margin-bottom: .5rem;
}
.footer-label {
color: #fff;
font-family: "Danzza", sans-serif;
font-size: var(--font-size-paragraph);
font-weight: 500;
text-transform: uppercase;
line-height: 1.2;
}
.footer-col address,
.footer-col address a,
.footer-col a {
color: #4dfca1;
font-family: "Danzza", sans-serif;
font-size: var(--font-size-paragraph-small);
font-style: normal;
line-height: 20px;
text-decoration: none;
}
/* --- Socials --- */
.footer-socials {
display: flex;
gap: 24px;
list-style: none;
}
.footer-socials a {
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
}
.footer-socials :global(svg) {
width: 24px;
height: 24px;
}
/* --- Copyright bar --- */
.footer-bottom {
display: flex;
align-items: center;
justify-content: center;
gap: 24px;
padding: 24px 45px;
border-top: 1px solid rgba(255, 255, 255, 0.5);
}
.footer-bottom p,
.footer-bottom a {
color: #fff;
font-family: "Danzza", sans-serif;
font-size: 16px;
font-weight: 400;
text-transform: uppercase;
text-decoration: none;
}
.footer-divider {
width: 1px;
height: 24px;
background: #4dfca1;
}
/* --- Mobile (≤700px) --- */
@media (max-width: 700px) {
.footer-main {
flex-direction: column;
align-items: flex-start;
gap: 20px;
padding: 16px 20px;
}
.footer-bottom {
flex-direction: column;
gap: 16px;
text-align: center;
padding: 16px 20px;
}
.footer-divider {
width: 7%;
height: 2px;
}
:global(.collection .page-scrollable-footer) {
margin-left: -6.2vw;
}
:global(.about .page-scrollable-footer) {
margin-left: 0;
}
:global(.article-wrapper .page-scrollable-footer) {
margin-left: -5.3vw;
}
}
/* --- Tablet (701px912px) --- */
@media (min-width: 701px) and (max-width: 912px) {
.footer-main {
flex-direction: column;
align-items: flex-start;
gap: 20px;
padding: 20px 30px;
}
.footer-bottom {
flex-direction: column;
gap: 16px;
text-align: center;
padding: 20px 30px;
}
.footer-divider {
width: 100%;
height: 1px;
}
}
</style>