233 lines
11 KiB
PHP
233 lines
11 KiB
PHP
<?php
|
||
declare(strict_types=1);
|
||
require_once __DIR__ . '/functions.php';
|
||
$all = load_events();
|
||
$upcoming = array_values(array_filter($all, 'is_upcoming'));
|
||
usort($upcoming, 'by_datetime_asc');
|
||
$jsonld = render_event_jsonld_graph($upcoming);
|
||
?>
|
||
<!doctype html>
|
||
<html lang="de">
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<title>Programm | <?= h(SITE_NAME) ?></title>
|
||
<meta name="description" content="Aktuelle Vorträge, Lesungen und Multivisionsshows in Walschleben – <?= h(SITE_NAME) ?>. Termine, Inhalte, Tickets.">
|
||
<link rel="canonical" href="<?= h(BASE_URL) ?>/programm.php">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<meta property="og:title" content="Programm | <?= h(SITE_NAME) ?>">
|
||
<meta property="og:description" content="Aktuelle Vorträge, Lesungen und Multivisionsshows – Termine & Infos.">
|
||
<meta property="og:type" content="website">
|
||
<meta property="og:url" content="<?= h(BASE_URL) ?>/programm.php">
|
||
<meta property="og:image" content="<?= h(BASE_URL . DEFAULT_OG_IMAGE) ?>">
|
||
<meta name="twitter:card" content="summary_large_image">
|
||
<script type="application/ld+json"><?= $jsonld ?></script>
|
||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
|
||
<link rel="stylesheet" href="/assets/styles.css">
|
||
<link rel="icon" href="/assets/logo.png" sizes="32x32" type="image/png">
|
||
<style>
|
||
/* ===== Poster Cards (perfektionistischer Look) ===== */
|
||
.poster-card{
|
||
border:1px solid #e9edf3;
|
||
border-radius:1.1rem;
|
||
box-shadow:0 8px 28px rgba(0,0,0,.06);
|
||
transition:transform .18s ease, box-shadow .18s ease, border-color .18s ease;
|
||
background:#fff;
|
||
}
|
||
.poster-card:hover{ transform:translateY(-2px); box-shadow:0 12px 38px rgba(0,0,0,.10); cursor:pointer; }
|
||
.poster-media{
|
||
padding:1rem 1rem 0; display:flex; justify-content:center; align-items:center;
|
||
}
|
||
.poster-img{
|
||
display:block; margin:0 auto;
|
||
width:auto; height:auto; max-width:100%;
|
||
/* Bild bestimmt die Kachelhöhe harmonisch – skaliert, aber nie beschnitten */
|
||
max-height: clamp(220px, 32vh, 420px);
|
||
object-fit:contain;
|
||
border-radius:.75rem;
|
||
box-shadow:0 6px 18px rgba(0,0,0,.08);
|
||
}
|
||
.poster-body{
|
||
text-align:center; padding:1rem 1.25rem 1.25rem;
|
||
}
|
||
.poster-date{ text-transform:uppercase; letter-spacing:.06em; }
|
||
.poster-title{ font-weight:800; line-height:1.15; margin:.25rem 0 .35rem; }
|
||
.poster-sub{ color:var(--muted); }
|
||
.poster-desc{ margin:.75rem auto 0; color:#344654; max-width:46ch; }
|
||
.poster-meta{ margin-top:.9rem; gap:.5rem; justify-content:center }
|
||
.poster-meta .badge{ font-weight:600 }
|
||
.top-ribbon{
|
||
position:absolute; top:12px; left:12px; background:var(--brand); color:#fff;
|
||
font-weight:700; padding:.28rem .55rem; border-radius:.5rem; font-size:.8rem;
|
||
box-shadow:0 6px 14px rgba(23,137,202,.25);
|
||
}
|
||
/* Modal Bild schön groß & mittig */
|
||
.modal-img{ max-height:90vh; width:auto; height:auto; object-fit:contain; display:block; margin:0 auto; }
|
||
.modal-body p{ white-space:pre-line; }
|
||
/* Tastatur-Focus sichtbar */
|
||
.poster-card:focus{ outline:3px solid rgba(23,137,202,.35); outline-offset:2px; }
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<nav class="navbar navbar-expand-lg bg-body-tertiary border-bottom sticky-top">
|
||
<div class="container">
|
||
<a class="navbar-brand brand-logo" href="index.html">
|
||
<img src="/assets/logo.png" alt="Walschleber KulTour Logo" height="34" width="34">
|
||
<span><?= h(SITE_NAME) ?></span>
|
||
</a>
|
||
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#nav"><span class="navbar-toggler-icon"></span></button>
|
||
<div id="nav" class="collapse navbar-collapse">
|
||
<ul class="navbar-nav ms-auto">
|
||
<li class="nav-item"><a class="nav-link active" href="programm.php">Programm</a></li>
|
||
<li class="nav-item"><a class="nav-link" href="vergangene-vortraege.php">Vergangene Vorträge</a></li>
|
||
<li class="nav-item"><a class="nav-link" href="ueber-uns.html">Über uns</a></li>
|
||
<li class="nav-item"><a class="nav-link" href="kontakt.html">Kontakt</a></li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
</nav>
|
||
|
||
<header class="py-5 bg-light border-bottom">
|
||
<div class="container">
|
||
<h1 class="display-6 mb-2">Programm</h1>
|
||
<p class="text-muted mb-0">Alle kommenden Termine auf einen Blick.</p>
|
||
</div>
|
||
</header>
|
||
|
||
<main class="container my-4">
|
||
<?php if (!$upcoming): ?>
|
||
<div class="alert alert-info">Derzeit sind keine zukünftigen Termine eingetragen.</div>
|
||
<?php else: ?>
|
||
<div class="d-flex justify-content-between align-items-center mb-3">
|
||
<div class="small text-muted">Sortierung: demnächst zuerst</div>
|
||
<a class="btn btn-brand btn-sm" href="vergangene-vortraege.php">Zum Archiv</a>
|
||
</div>
|
||
|
||
<div class="row g-4">
|
||
<?php foreach ($upcoming as $e): $mid = 'event-'.h($e['id']); ?>
|
||
<div class="col-md-6 col-lg-4">
|
||
<article class="poster-card position-relative h-100"
|
||
data-bs-toggle="modal" data-bs-target="#<?= $mid ?>"
|
||
tabindex="0" role="button" aria-label="Details zu <?= h($e['title']) ?> öffnen">
|
||
|
||
<?php if (!empty($e['image'])): ?>
|
||
<div class="poster-media">
|
||
<img class="poster-img" src="<?= h($e['image']) ?>" alt="<?= h($e['title']) ?>">
|
||
</div>
|
||
<?php endif; ?>
|
||
|
||
<div class="poster-body">
|
||
<span class="top-ribbon">Kommender Termin</span>
|
||
<div class="small text-muted poster-date mb-2">
|
||
<?= date('d.m.Y', strtotime($e['date'])) ?> · <?= h($e['time'] ?: '19:30') ?>
|
||
</div>
|
||
|
||
<h2 class="h5 poster-title"><?= h($e['title']) ?></h2>
|
||
<?php if (!empty($e['speaker'])): ?>
|
||
<div class="poster-sub small mb-2">mit <?= h($e['speaker']) ?></div>
|
||
<?php endif; ?>
|
||
|
||
<?php if (!empty($e['description'])): ?>
|
||
<p class="poster-desc"><?= nl2br(h(mb_strimwidth($e['description'], 0, 220, '…'))) ?></p>
|
||
<?php endif; ?>
|
||
|
||
<div class="d-flex flex-wrap poster-meta">
|
||
<?php if (!empty($e['venue_name'])): ?>
|
||
<span class="badge text-bg-light border"><?= h($e['venue_name']) ?></span>
|
||
<?php endif; ?>
|
||
<?php if (!empty($e['price'])): ?>
|
||
<span class="badge badge-accent">Eintritt: <?= h($e['price']) ?></span>
|
||
<?php endif; ?>
|
||
<?php if (!empty($e['doors_open'])): ?>
|
||
<span class="badge text-bg-light border">Einlass: <?= h($e['doors_open']) ?></span>
|
||
<?php endif; ?>
|
||
<?php if (!empty($e['tags'])): ?>
|
||
<span class="badge text-bg-light border"><?= h($e['tags']) ?></span>
|
||
<?php endif; ?>
|
||
</div>
|
||
|
||
<?php
|
||
$ticketUrl = null;
|
||
if (!empty($e['tickets_info']) && preg_match('~https?://[^\s]+~', $e['tickets_info'], $m)) {
|
||
$ticketUrl = $m[0];
|
||
}
|
||
?>
|
||
<?php if ($ticketUrl): ?>
|
||
<div class="mt-3">
|
||
<a class="btn btn-brand btn-sm" href="<?= h($ticketUrl) ?>" target="_blank" rel="noopener" onclick="event.stopPropagation()">Tickets sichern</a>
|
||
</div>
|
||
<?php elseif (!empty($e['tickets_info'])): ?>
|
||
<div class="mt-3 small text-muted"><strong>Tickets:</strong> <?= h($e['tickets_info']) ?></div>
|
||
<?php endif; ?>
|
||
|
||
<a href="#<?= $mid ?>" class="stretched-link" data-bs-toggle="modal" data-bs-target="#<?= $mid ?>" aria-hidden="true" tabindex="-1"></a>
|
||
</div>
|
||
</article>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
</div>
|
||
|
||
<!-- Modals -->
|
||
<?php foreach ($upcoming as $e): $mid = 'event-'.h($e['id']); ?>
|
||
<div class="modal fade" id="<?= $mid ?>" tabindex="-1" aria-hidden="true">
|
||
<div class="modal-dialog modal-xl modal-dialog-centered modal-fullscreen-sm-down">
|
||
<div class="modal-content">
|
||
<div class="modal-header">
|
||
<h2 class="modal-title h4 mb-0"><?= h($e['title']) ?></h2>
|
||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="Schließen"></button>
|
||
</div>
|
||
<div class="modal-body">
|
||
<?php if (!empty($e['image'])): ?>
|
||
<img src="<?= h($e['image']) ?>" alt="<?= h($e['title']) ?>" class="modal-img mb-3">
|
||
<?php endif; ?>
|
||
|
||
<p class="text-muted small mb-2">
|
||
<?= date('d.m.Y', strtotime($e['date'])) ?> · <?= h($e['time'] ?: '19:30') ?>
|
||
<?php if (!empty($e['speaker'])): ?> · mit <?= h($e['speaker']) ?><?php endif; ?>
|
||
</p>
|
||
|
||
<?php if (!empty($e['venue_name']) || !empty($e['venue_address'])): ?>
|
||
<p class="mb-2 text-center"><strong><?= h($e['venue_name']) ?></strong><?= !empty($e['venue_address']) ? ' · '.h($e['venue_address']) : '' ?></p>
|
||
<?php endif; ?>
|
||
|
||
<?php if (!empty($e['price']) || !empty($e['doors_open'])): ?>
|
||
<p class="mb-2 text-center">
|
||
<?php if (!empty($e['price'])): ?><span class="badge badge-accent">Eintritt: <?= h($e['price']) ?></span> <?php endif; ?>
|
||
<?php if (!empty($e['doors_open'])): ?><span class="badge text-bg-light border">Einlass: <?= h($e['doors_open']) ?></span><?php endif; ?>
|
||
</p>
|
||
<?php endif; ?>
|
||
|
||
<?php if (!empty($e['tags'])): ?>
|
||
<p class="mb-2 text-center"><span class="badge text-bg-light border"><?= h($e['tags']) ?></span></p>
|
||
<?php endif; ?>
|
||
|
||
<div class="poster-desc">
|
||
<?= render_rich_text($e['description'] ?? '') ?>
|
||
</div>
|
||
|
||
<?php if (!empty($ticketUrl)): ?>
|
||
<div class="text-center"><a class="btn btn-brand" href="<?= h($ticketUrl) ?>" target="_blank" rel="noopener">Tickets sichern</a></div>
|
||
<?php elseif (!empty($e['tickets_info'])): ?>
|
||
<div class="small text-center"><strong>Tickets:</strong> <?= h($e['tickets_info']) ?></div>
|
||
<?php endif; ?>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<?php endforeach; ?>
|
||
|
||
<?php endif; ?>
|
||
</main>
|
||
|
||
<footer class="py-4 border-top mt-5">
|
||
<div class="container small d-flex gap-3 flex-wrap align-items-center">
|
||
<span>© <?= date('Y') ?> <?= h(SITE_NAME) ?></span>
|
||
<span class="separator">•</span><a href="impressum.html">Impressum</a>
|
||
<span class="separator">•</span><a href="datenschutz.html">Datenschutz</a>
|
||
</div>
|
||
</footer>
|
||
|
||
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
|
||
</body>
|
||
</html>
|