Lehrplan: 4 Modul-Detailseiten + lehrplan.php + simulationen.php + 78 Anker
- Gemeinsamer Partial App/pages/_partials/modul_detail.php - modul-klima/fluss/heli/stadt als 3-Zeilen-Wrapper - lehrplan.php: Filter Land/Thema/Typ, TOC, URL-Persistenz - simulationen.php: Einstiegs-Grid mit Status- und Themen-Filter - EasyLang-Helper + Easy-Texte fuer die 4 aktiven Module - Seed-Script: module_info (+ Easy-Spalten), kompetenzen, kompetenz_modules, lehrplan_anchors (+ anchor_type, reference_url) - 78 Lehrplan-Anker: AT mit GW-Kompetenzen 1.9/2.1/4.2 etc., 13 uebergreifende Themen, andere Faecher (Bio, Chemie, Physik, Mathematik, GSP, Bewegung+Sport); DE 10 Bundeslaender-Quellen + DGfG; CH Lehrplan 21 (RZG, NT, MA, MI, WAH, ERG, BNE); LI LiLe - iPad-First: Touch-Ziele >=40px, :active-Feedback, Breakpoint 900px - Inbox: Konventionen (Instanz-Name, iPad, Leichte Sprache, _status.md, Git-Commits) umgesetzt Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,657 @@
|
||||
<?php
|
||||
/**
|
||||
* Modul-Detailseiten — gemeinsamer Partial
|
||||
* -----------------------------------------
|
||||
* Wird von modul-klima.php, modul-fluss.php, modul-heli.php, modul-stadt.php
|
||||
* eingebunden. Erwartet im includierenden Scope:
|
||||
*
|
||||
* $MODULE_ID string — module_info.module_id (z.B. 'klima')
|
||||
*
|
||||
* Quellen:
|
||||
* - module_info (Metadaten, inkl. Easy-Varianten)
|
||||
* - glossar + glossar_modules (Begriffe)
|
||||
* - kompetenzen + kompetenz_modules
|
||||
* + lehrplan_anchors (Lehrplan-Bezug, gruppiert nach Typ)
|
||||
* - EasyLang-Helper (pickt Normal- oder Easy-Text)
|
||||
*
|
||||
* Design: iPad Landscape (1180×820) als Hauptreferenz (siehe
|
||||
* module-interface.md Abschnitt 4c).
|
||||
*/
|
||||
|
||||
if (!isset($MODULE_ID) || !is_string($MODULE_ID)) {
|
||||
http_response_code(500);
|
||||
echo 'modul_detail.php: $MODULE_ID fehlt.';
|
||||
exit;
|
||||
}
|
||||
|
||||
require_once __DIR__ . '/../../php/config/app.php';
|
||||
require_once __DIR__ . '/../../php/config/db.php';
|
||||
require_once __DIR__ . '/../../php/lib/EasyLang.php';
|
||||
|
||||
$db = getDB();
|
||||
|
||||
$module = $db->prepare('SELECT * FROM module_info WHERE module_id = ?');
|
||||
$module->execute([$MODULE_ID]);
|
||||
$module = $module->fetch();
|
||||
|
||||
if (!$module) {
|
||||
http_response_code(404);
|
||||
echo 'Modul nicht gefunden.';
|
||||
exit;
|
||||
}
|
||||
|
||||
// Easy-Varianten per Helper (Fallback: Normalform)
|
||||
$shortDesc = EasyLang::pick($module['short_desc'] ?? null, $module['short_desc_easy'] ?? null);
|
||||
$longDesc = EasyLang::pick($module['long_desc'] ?? null, $module['long_desc_easy'] ?? null);
|
||||
$goalsRaw = EasyLang::pick($module['learning_goals'] ?? null, $module['learning_goals_easy'] ?? null);
|
||||
|
||||
// Lernziele — JSON-Array mit Strings
|
||||
$learningGoals = [];
|
||||
if ($goalsRaw) {
|
||||
$decoded = json_decode($goalsRaw, true);
|
||||
if (is_array($decoded)) $learningGoals = $decoded;
|
||||
}
|
||||
|
||||
// Glossar-Begriffe zum Modul
|
||||
$stmt = $db->prepare('
|
||||
SELECT g.key_slug, g.title, g.short, g.category
|
||||
FROM glossar g
|
||||
INNER JOIN glossar_modules m ON m.glossar_id = g.id
|
||||
WHERE m.module_id = ?
|
||||
ORDER BY g.title
|
||||
');
|
||||
$stmt->execute([$MODULE_ID]);
|
||||
$glossarTerms = $stmt->fetchAll();
|
||||
|
||||
// Kompetenzen + Lehrplan-Anker
|
||||
$stmt = $db->prepare('
|
||||
SELECT k.id, k.key_slug, k.title, k.description, k.category,
|
||||
km.strength, km.level
|
||||
FROM kompetenzen k
|
||||
INNER JOIN kompetenz_modules km ON km.kompetenz_id = k.id
|
||||
WHERE km.module_id = ?
|
||||
ORDER BY km.strength, k.sort_order, k.title
|
||||
');
|
||||
$stmt->execute([$MODULE_ID]);
|
||||
$kompetenzen = $stmt->fetchAll();
|
||||
|
||||
$ankerStmt = $db->prepare("
|
||||
SELECT anchor_type, country, region, school_type, grade, subject,
|
||||
title, reference, reference_url, quote
|
||||
FROM lehrplan_anchors
|
||||
WHERE kompetenz_id = ?
|
||||
ORDER BY FIELD(anchor_type, 'fach', 'uebergreifendes_thema', 'anderes_fach'),
|
||||
country, sort_order
|
||||
");
|
||||
foreach ($kompetenzen as &$k) {
|
||||
$ankerStmt->execute([$k['id']]);
|
||||
$rows = $ankerStmt->fetchAll();
|
||||
$k['anchors_fach'] = array_values(array_filter($rows, fn($r) => $r['anchor_type'] === 'fach'));
|
||||
$k['anchors_uebergreifend'] = array_values(array_filter($rows, fn($r) => $r['anchor_type'] === 'uebergreifendes_thema'));
|
||||
$k['anchors_andere_faecher'] = array_values(array_filter($rows, fn($r) => $r['anchor_type'] === 'anderes_fach'));
|
||||
}
|
||||
unset($k);
|
||||
|
||||
$bp = BASE_PATH;
|
||||
$api = BASE_URL . '/php/api/glossar.php';
|
||||
|
||||
$pageTitle = htmlspecialchars($module['title']) . ' — Lehrplan geograsim.at';
|
||||
$easyActive = EasyLang::isActive();
|
||||
|
||||
if (!function_exists('esc')) {
|
||||
function esc(?string $s): string { return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
|
||||
}
|
||||
|
||||
$countryMeta = [
|
||||
'AT' => ['flag' => '🇦🇹', 'label' => 'Österreich'],
|
||||
'DE' => ['flag' => '🇩🇪', 'label' => 'Deutschland'],
|
||||
'CH' => ['flag' => '🇨🇭', 'label' => 'Schweiz'],
|
||||
'LI' => ['flag' => '🇱🇮', 'label' => 'Liechtenstein'],
|
||||
];
|
||||
?><!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title><?= $pageTitle ?></title>
|
||||
<meta name="description" content="<?= esc($module['subtitle']) ?> — Lernziele, Begriffe und Lehrplan-Bezug für die GeoGraSim-Simulation <?= esc($module['title']) ?>.">
|
||||
<link rel="icon" type="image/svg+xml" href="<?= $bp ?>/favicon.svg">
|
||||
<link rel="icon" type="image/png" href="<?= $bp ?>/favicon-96x96.png" sizes="96x96">
|
||||
<link rel="stylesheet" href="<?= $bp ?>/assets/fonts/inter.css">
|
||||
<link rel="stylesheet" href="<?= $bp ?>/assets/css/design-system.css">
|
||||
<style>
|
||||
/* Seite ist scrollbar — Design-System-Body ist overflow:hidden, das ueberschreiben wir hier */
|
||||
body { overflow: auto; height: auto; min-height: 100vh; background: var(--ggs-bg); }
|
||||
|
||||
/* ===== Topbar (identisch zu glossar.php) ===== */
|
||||
.md-topbar {
|
||||
background: var(--ggs-white);
|
||||
padding: .55rem 1.2rem;
|
||||
display: flex; align-items: center; gap: .8rem;
|
||||
border-bottom: 1px solid var(--ggs-border);
|
||||
position: sticky; top: 0; z-index: 50;
|
||||
}
|
||||
.md-topbar a { text-decoration: none; }
|
||||
.md-topbar-brand { display: flex; align-items: center; gap: .5rem; }
|
||||
.md-topbar-brand img.md-text { height: 40px; display: block; }
|
||||
.md-topbar .spacer { flex: 1; }
|
||||
.md-btn-sm {
|
||||
/* iPad-Touch: min 40px Höhe; padding angepasst */
|
||||
padding: .5rem .85rem; border: 1px solid var(--ggs-border);
|
||||
border-radius: var(--ggs-radius-sm); background: var(--ggs-white);
|
||||
font-size: .78rem; font-weight: 600; cursor: pointer;
|
||||
color: var(--ggs-text); font-family: inherit; text-decoration: none;
|
||||
display: inline-flex; align-items: center; min-height: 40px;
|
||||
}
|
||||
.md-btn-sm:hover { background: var(--ggs-fjord-light); color: var(--ggs-fjord-dark); border-color: var(--ggs-fjord); }
|
||||
.md-btn-sm:active { transform: scale(0.97); }
|
||||
.md-btn-sm.md-btn-primary { background: var(--ggs-fjord); color: var(--ggs-white); border-color: var(--ggs-fjord); }
|
||||
.md-btn-sm.md-btn-primary:hover { background: var(--ggs-fjord-dark); }
|
||||
|
||||
.md-easy-pill {
|
||||
background: var(--ggs-moss-light); color: var(--ggs-moss-dark);
|
||||
border-color: var(--ggs-moss); font-weight: 700;
|
||||
}
|
||||
|
||||
/* ===== Breadcrumb ===== */
|
||||
.md-breadcrumb {
|
||||
max-width: 1100px; margin: 0 auto; padding: .9rem 1.2rem .2rem;
|
||||
font-size: .82rem; color: var(--ggs-text-muted);
|
||||
}
|
||||
.md-breadcrumb a { color: var(--ggs-fjord); text-decoration: none; }
|
||||
.md-breadcrumb a:hover { text-decoration: underline; }
|
||||
.md-breadcrumb .sep { margin: 0 .4rem; opacity: .5; }
|
||||
|
||||
/* ===== Hero ===== */
|
||||
.md-hero {
|
||||
max-width: 1100px; margin: 0 auto; padding: 1.5rem 1.2rem 2rem;
|
||||
display: grid; grid-template-columns: minmax(220px, 340px) 1fr;
|
||||
gap: 2rem; align-items: center;
|
||||
}
|
||||
.md-hero-card {
|
||||
background: var(--ggs-white); border-radius: var(--ggs-radius-lg);
|
||||
overflow: hidden; box-shadow: var(--ggs-shadow-md);
|
||||
border: 1px solid var(--ggs-border);
|
||||
}
|
||||
.md-hero-card img {
|
||||
width: 100%; aspect-ratio: 1; object-fit: cover; display: block;
|
||||
}
|
||||
.md-hero-text h1 {
|
||||
font-size: clamp(1.8rem, 4vw, 2.6rem);
|
||||
font-weight: 900; letter-spacing: -.03em;
|
||||
color: var(--ggs-fjord-dark);
|
||||
display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
|
||||
margin-bottom: .4rem;
|
||||
}
|
||||
.md-hero-icon { font-size: 1.3em; }
|
||||
.md-hero-subtitle {
|
||||
font-size: 1.1rem; color: var(--ggs-text); line-height: 1.5;
|
||||
margin-bottom: 1.4rem;
|
||||
}
|
||||
.md-hero-cta { display: flex; flex-wrap: wrap; gap: .6rem; }
|
||||
.md-cta-btn {
|
||||
/* iPad-Touch: generous padding, min 44px */
|
||||
display: inline-flex; align-items: center; gap: .5rem;
|
||||
padding: .85rem 1.5rem; border-radius: var(--ggs-radius-sm);
|
||||
font-weight: 700; font-size: 1rem; text-decoration: none;
|
||||
border: 1.5px solid transparent; transition: all .2s var(--ggs-ease);
|
||||
min-height: 48px;
|
||||
}
|
||||
.md-cta-primary { background: var(--ggs-fjord); color: var(--ggs-white); }
|
||||
.md-cta-primary:hover { background: var(--ggs-fjord-dark); transform: translateY(-1px); }
|
||||
.md-cta-primary:active { transform: scale(0.98); }
|
||||
.md-cta-secondary {
|
||||
background: var(--ggs-white); color: var(--ggs-fjord);
|
||||
border-color: var(--ggs-fjord);
|
||||
}
|
||||
.md-cta-secondary:hover { background: var(--ggs-fjord-light); }
|
||||
.md-cta-secondary:active { transform: scale(0.98); }
|
||||
|
||||
/* ===== Meta-Bar (Dauer, Alter, Status) ===== */
|
||||
.md-meta {
|
||||
max-width: 1100px; margin: 0 auto 2rem; padding: 0 1.2rem;
|
||||
display: flex; flex-wrap: wrap; gap: .6rem;
|
||||
}
|
||||
.md-meta-item {
|
||||
display: inline-flex; align-items: center; gap: .4rem;
|
||||
background: var(--ggs-white); border: 1px solid var(--ggs-border);
|
||||
border-radius: var(--ggs-radius-pill);
|
||||
padding: .5rem .95rem;
|
||||
font-size: .88rem; color: var(--ggs-text);
|
||||
min-height: 36px;
|
||||
}
|
||||
.md-meta-icon { font-size: 1rem; }
|
||||
.md-meta-label { color: var(--ggs-text-muted); font-weight: 600; }
|
||||
.md-meta-value { font-weight: 700; }
|
||||
.md-status-pill { padding: 2px 10px; border-radius: var(--ggs-radius-pill); font-size: .72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; }
|
||||
.md-status-aktiv { background: var(--ggs-moss-light); color: var(--ggs-moss-dark); }
|
||||
.md-status-beta { background: var(--ggs-sand); color: #7a6540; }
|
||||
.md-status-geplant { background: var(--ggs-fjord-light); color: var(--ggs-fjord-dark); }
|
||||
|
||||
/* ===== Sections ===== */
|
||||
.md-section {
|
||||
max-width: 1100px; margin: 0 auto 2.5rem; padding: 0 1.2rem;
|
||||
}
|
||||
.md-section-title {
|
||||
font-size: .75rem; font-weight: 800; text-transform: uppercase;
|
||||
letter-spacing: .12em; color: var(--ggs-fjord);
|
||||
margin-bottom: .4rem;
|
||||
}
|
||||
.md-section-h2 {
|
||||
font-size: 1.7rem; font-weight: 900; color: var(--ggs-fjord-dark);
|
||||
letter-spacing: -.02em; margin-bottom: .9rem;
|
||||
}
|
||||
.md-section-text {
|
||||
font-size: 1rem; line-height: 1.7; color: var(--ggs-text); max-width: 780px;
|
||||
}
|
||||
|
||||
/* Lernziele-Grid */
|
||||
.md-goals {
|
||||
display: grid; grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||||
gap: 1rem; margin-top: 1rem;
|
||||
}
|
||||
.md-goal {
|
||||
background: var(--ggs-white); border: 1px solid var(--ggs-border);
|
||||
border-left: 4px solid var(--ggs-moss);
|
||||
border-radius: var(--ggs-radius-md); padding: 1rem 1.2rem;
|
||||
font-size: .95rem; line-height: 1.5;
|
||||
}
|
||||
.md-goal-num {
|
||||
display: inline-block; font-size: .72rem; font-weight: 800;
|
||||
color: var(--ggs-moss-dark); background: var(--ggs-moss-light);
|
||||
padding: 2px 8px; border-radius: var(--ggs-radius-pill);
|
||||
margin-bottom: .4rem;
|
||||
}
|
||||
|
||||
/* Glossar-Chips */
|
||||
.md-glossar {
|
||||
display: flex; flex-wrap: wrap; gap: .45rem; margin-top: 1rem;
|
||||
}
|
||||
.md-glossar-chip {
|
||||
/* iPad-Touch: min 40px Höhe */
|
||||
display: inline-flex; align-items: center; gap: .3rem;
|
||||
background: var(--ggs-white); border: 1px solid var(--ggs-border);
|
||||
border-radius: var(--ggs-radius-pill);
|
||||
padding: .5rem 1rem; min-height: 40px;
|
||||
font-size: .88rem; font-weight: 600; color: var(--ggs-fjord-dark);
|
||||
text-decoration: none; transition: all .2s var(--ggs-ease);
|
||||
cursor: pointer;
|
||||
}
|
||||
.md-glossar-chip:hover, .md-glossar-chip:active {
|
||||
background: var(--ggs-fjord-light); border-color: var(--ggs-fjord);
|
||||
}
|
||||
.md-glossar-empty { color: var(--ggs-text-muted); font-size: .9rem; font-style: italic; }
|
||||
.md-glossar-link {
|
||||
display: inline-block; margin-top: 1rem;
|
||||
font-size: .9rem; color: var(--ggs-fjord); text-decoration: none; font-weight: 600;
|
||||
padding: .4rem 0; /* vergrossertes Touch-Ziel ohne Rahmen */
|
||||
}
|
||||
.md-glossar-link:hover { text-decoration: underline; }
|
||||
|
||||
/* Lehrplan-Bezug */
|
||||
.md-lehrplan-filters {
|
||||
display: flex; flex-wrap: wrap; gap: .5rem; margin: 1rem 0 1.2rem;
|
||||
}
|
||||
.md-chip {
|
||||
/* iPad-Touch: min 40x40 */
|
||||
display: inline-flex; align-items: center; gap: .3rem;
|
||||
padding: .55rem 1rem; border-radius: var(--ggs-radius-pill);
|
||||
background: var(--ggs-white); border: 1.5px solid var(--ggs-border);
|
||||
font-size: .88rem; font-weight: 600; color: var(--ggs-text-muted);
|
||||
cursor: pointer; transition: all .2s var(--ggs-ease); font-family: inherit;
|
||||
min-height: 40px; min-width: 40px;
|
||||
}
|
||||
.md-chip:hover { border-color: var(--ggs-fjord); color: var(--ggs-fjord); }
|
||||
.md-chip:active { transform: scale(0.96); }
|
||||
.md-chip.active { background: var(--ggs-fjord); color: var(--ggs-white); border-color: var(--ggs-fjord); }
|
||||
|
||||
.md-kompetenz {
|
||||
background: var(--ggs-white); border: 1px solid var(--ggs-border);
|
||||
border-radius: var(--ggs-radius-md); padding: 1.2rem 1.4rem 1rem;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.md-kompetenz-header { display: flex; align-items: baseline; gap: .7rem; flex-wrap: wrap; margin-bottom: .4rem; }
|
||||
.md-kompetenz-title { font-size: 1.15rem; font-weight: 800; color: var(--ggs-fjord-dark); }
|
||||
.md-kompetenz-strength {
|
||||
font-size: .65rem; font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
|
||||
padding: 2px 8px; border-radius: var(--ggs-radius-pill);
|
||||
}
|
||||
.md-strength-primaer { background: var(--ggs-moss-light); color: var(--ggs-moss-dark); }
|
||||
.md-strength-sekundaer { background: var(--ggs-bg-dark); color: var(--ggs-text-muted); }
|
||||
.md-kompetenz-desc {
|
||||
font-size: .9rem; color: var(--ggs-text); line-height: 1.55;
|
||||
margin-bottom: .9rem;
|
||||
}
|
||||
.md-anchors { display: grid; gap: .55rem; }
|
||||
.md-anchor {
|
||||
display: grid; grid-template-columns: 28px 1fr; gap: .6rem; align-items: start;
|
||||
font-size: .9rem; padding: .6rem .8rem;
|
||||
background: var(--ggs-bg); border-radius: var(--ggs-radius-sm);
|
||||
border-left: 3px solid var(--ggs-fjord-light);
|
||||
}
|
||||
.md-anchor-flag { font-size: 1.2rem; line-height: 1.3; text-align: center; }
|
||||
.md-anchor-meta { font-size: .75rem; color: var(--ggs-text-muted); margin-bottom: .15rem; }
|
||||
.md-anchor-title { font-weight: 700; color: var(--ggs-fjord-dark); margin-bottom: .2rem; }
|
||||
.md-anchor-quote { font-size: .88rem; color: var(--ggs-text); font-style: italic; line-height: 1.45; }
|
||||
.md-anchor-ref {
|
||||
font-size: .72rem; color: var(--ggs-text-muted);
|
||||
margin-top: .3rem;
|
||||
}
|
||||
.md-anchor-link {
|
||||
color: var(--ggs-fjord); text-decoration: none; font-weight: 600;
|
||||
border-bottom: 1px dotted var(--ggs-fjord); transition: color .2s var(--ggs-ease);
|
||||
padding: 2px 0; /* Touch-Ziel leicht vergroessern */
|
||||
}
|
||||
.md-anchor-link:hover { color: var(--ggs-fjord-dark); border-bottom-style: solid; }
|
||||
|
||||
/* Anker-Gruppen (Fach / Fächerübergreifend / Andere Fächer) */
|
||||
.md-anchor-group { margin-top: .9rem; }
|
||||
.md-anchor-group:first-child { margin-top: 0; }
|
||||
.md-anchor-group-head {
|
||||
display: flex; align-items: baseline; gap: .6rem;
|
||||
margin: .2rem 0 .5rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
.md-anchor-group-title {
|
||||
font-size: .74rem; font-weight: 800; text-transform: uppercase;
|
||||
letter-spacing: .1em; color: var(--ggs-fjord-dark);
|
||||
}
|
||||
.md-anchor-group-sub {
|
||||
font-size: .72rem; color: var(--ggs-text-muted); font-weight: 500;
|
||||
}
|
||||
.md-anchor--fach { border-left-color: var(--ggs-fjord); }
|
||||
.md-anchor--uebergreifendes_thema { border-left-color: var(--ggs-moss); background: var(--ggs-moss-light); }
|
||||
.md-anchor--anderes_fach { border-left-color: var(--ggs-sand-dark); background: var(--ggs-bg-dark); }
|
||||
.md-anchor--uebergreifendes_thema .md-anchor-flag,
|
||||
.md-anchor--anderes_fach .md-anchor-flag { opacity: .65; }
|
||||
.md-anchor.hidden { display: none; }
|
||||
.md-lehrplan-note {
|
||||
font-size: .82rem; color: var(--ggs-text-muted); font-style: italic;
|
||||
margin-top: 1rem; max-width: 780px;
|
||||
}
|
||||
|
||||
/* ===== Footer ===== */
|
||||
.md-footer {
|
||||
text-align: center; padding: 2rem 1rem 2.5rem;
|
||||
font-size: .78rem; color: var(--ggs-text-muted);
|
||||
border-top: 1px solid var(--ggs-border);
|
||||
background: var(--ggs-white);
|
||||
margin-top: 2rem;
|
||||
}
|
||||
.md-footer img { height: 36px; opacity: .5; margin-bottom: .5rem; }
|
||||
.md-footer a { color: var(--ggs-fjord); text-decoration: none; padding: .3rem .2rem; }
|
||||
.md-footer a:hover { text-decoration: underline; }
|
||||
|
||||
/* Responsive — Design-System-konforme Breakpoints 900 / 1200 */
|
||||
@media (max-width: 900px) {
|
||||
.md-hero { grid-template-columns: 1fr; gap: 1rem; padding-top: 1rem; }
|
||||
.md-hero-card { max-width: 260px; margin: 0 auto; }
|
||||
.md-hero-text { text-align: center; }
|
||||
.md-hero-cta { justify-content: center; }
|
||||
.md-meta { justify-content: center; }
|
||||
.md-section-h2 { font-size: 1.35rem; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Einheitlicher Header -->
|
||||
<header class="md-topbar">
|
||||
<a href="<?= $bp ?>/index.html" class="md-topbar-brand" aria-label="GeoGraSim Startseite">
|
||||
<img class="md-text" src="<?= $bp ?>/assets/img/geograsim_t.svg" alt="GeoGraSim">
|
||||
</a>
|
||||
<span class="spacer"></span>
|
||||
<?php if ($easyActive): ?>
|
||||
<span class="md-btn-sm md-easy-pill" title="Leichte Sprache ist aktiv">🟢 Leichte Sprache</span>
|
||||
<?php endif; ?>
|
||||
<a href="<?= $bp ?>/pages/simulationen.php" class="md-btn-sm">Alle Simulationen</a>
|
||||
<a href="<?= $bp ?>/pages/lehrplan.php" class="md-btn-sm">Lehrplan</a>
|
||||
<a href="<?= $bp ?>/glossar" class="md-btn-sm">Glossar</a>
|
||||
<a href="<?= $bp ?>/login.html" class="md-btn-sm md-btn-primary">Anmelden</a>
|
||||
</header>
|
||||
|
||||
<nav class="md-breadcrumb" aria-label="Brotkrumen">
|
||||
<a href="<?= $bp ?>/index.html">Start</a>
|
||||
<span class="sep">›</span>
|
||||
<a href="<?= $bp ?>/pages/simulationen.php">Simulationen</a>
|
||||
<span class="sep">›</span>
|
||||
<span><?= esc($module['title']) ?></span>
|
||||
</nav>
|
||||
|
||||
<!-- HERO -->
|
||||
<section class="md-hero">
|
||||
<div class="md-hero-card">
|
||||
<img src="<?= $bp ?>/assets/img/<?= esc($module['card_image']) ?>" alt="<?= esc($module['title']) ?>">
|
||||
</div>
|
||||
<div class="md-hero-text">
|
||||
<h1><span class="md-hero-icon"><?= esc($module['icon']) ?></span><?= esc($module['title']) ?></h1>
|
||||
<p class="md-hero-subtitle"><?= esc($shortDesc ?? $module['subtitle']) ?></p>
|
||||
<div class="md-hero-cta">
|
||||
<?php if ($module['status'] === 'aktiv' || $module['status'] === 'beta'): ?>
|
||||
<a href="<?= $bp ?>/<?= esc($module['play_url']) ?>" class="md-cta-btn md-cta-primary">
|
||||
▶ Jetzt probieren
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<a href="<?= $bp ?>/login.html" class="md-cta-btn md-cta-primary">
|
||||
🔒 In Entwicklung — Newsletter
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<a href="#lehrplan-bezug" class="md-cta-btn md-cta-secondary">
|
||||
📘 Lehrplan-Bezug
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- META -->
|
||||
<div class="md-meta">
|
||||
<?php if (!empty($module['duration_min'])): ?>
|
||||
<span class="md-meta-item">
|
||||
<span class="md-meta-icon">⏱️</span>
|
||||
<span class="md-meta-label">Dauer:</span>
|
||||
<span class="md-meta-value">ca. <?= (int)$module['duration_min'] ?> Min</span>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($module['age_min']) && !empty($module['age_max'])): ?>
|
||||
<span class="md-meta-item">
|
||||
<span class="md-meta-icon">👧</span>
|
||||
<span class="md-meta-label">Alter:</span>
|
||||
<span class="md-meta-value"><?= (int)$module['age_min'] ?>–<?= (int)$module['age_max'] ?> Jahre</span>
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
<span class="md-meta-item">
|
||||
<span class="md-meta-icon">🏷️</span>
|
||||
<span class="md-meta-label">Modul-ID:</span>
|
||||
<code style="font-size: .82rem;"><?= esc($module['module_id']) ?></code>
|
||||
</span>
|
||||
<span class="md-meta-item">
|
||||
<span class="md-meta-label">Status:</span>
|
||||
<span class="md-status-pill md-status-<?= esc($module['status']) ?>"><?= esc($module['status']) ?></span>
|
||||
</span>
|
||||
<span class="md-meta-item">
|
||||
<span class="md-meta-icon">📚</span>
|
||||
<span class="md-meta-label">Begriffe im Glossar:</span>
|
||||
<span class="md-meta-value"><?= count($glossarTerms) ?></span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- WAS LERNST DU -->
|
||||
<?php if (!empty($learningGoals)): ?>
|
||||
<section class="md-section">
|
||||
<div class="md-section-title">Was lernst du?</div>
|
||||
<h2 class="md-section-h2">Lernziele dieses Moduls</h2>
|
||||
<div class="md-goals">
|
||||
<?php foreach ($learningGoals as $i => $g): ?>
|
||||
<div class="md-goal">
|
||||
<span class="md-goal-num"><?= str_pad((string)($i+1), 2, '0', STR_PAD_LEFT) ?></span>
|
||||
<div><?= esc($g) ?></div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- WIE FUNKTIONIERT'S -->
|
||||
<?php if (!empty($longDesc)): ?>
|
||||
<section class="md-section">
|
||||
<div class="md-section-title">Wie funktioniert's?</div>
|
||||
<h2 class="md-section-h2">Die Simulation</h2>
|
||||
<p class="md-section-text"><?= nl2br(esc($longDesc)) ?></p>
|
||||
</section>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- BEGRIFFE AUS DEM GLOSSAR -->
|
||||
<section class="md-section">
|
||||
<div class="md-section-title">Begriffe, die du kennenlernst</div>
|
||||
<h2 class="md-section-h2">Glossar-Einträge zu diesem Modul</h2>
|
||||
<?php if (empty($glossarTerms)): ?>
|
||||
<p class="md-glossar-empty">Noch keine Begriffe zugeordnet — das Glossar wächst laufend weiter.</p>
|
||||
<?php else: ?>
|
||||
<div class="md-glossar">
|
||||
<?php foreach ($glossarTerms as $t): ?>
|
||||
<a class="md-glossar-chip"
|
||||
href="<?= $bp ?>/glossar?term=<?= urlencode($t['key_slug']) ?>"
|
||||
title="<?= esc($t['short']) ?>">
|
||||
<?= esc($t['title']) ?>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<a class="md-glossar-link" href="<?= $bp ?>/glossar?module=<?= urlencode($MODULE_ID) ?>">
|
||||
→ Alle <?= count($glossarTerms) ?> Begriffe im Glossar ansehen
|
||||
</a>
|
||||
</section>
|
||||
|
||||
<!-- LEHRPLAN-BEZUG -->
|
||||
<section class="md-section" id="lehrplan-bezug">
|
||||
<div class="md-section-title">Für Lehrpersonen</div>
|
||||
<h2 class="md-section-h2">Lehrplan-Bezug</h2>
|
||||
<p class="md-section-text">
|
||||
Dieses Modul bedient die folgenden didaktischen Kompetenzbereiche. Für jeden
|
||||
Bereich haben wir typische Verankerungen in den Lehrplänen für
|
||||
Österreich, Deutschland, Schweiz und Liechtenstein recherchiert — sie
|
||||
helfen, das Modul an den eigenen Lehrplan anzudocken.
|
||||
</p>
|
||||
|
||||
<div class="md-lehrplan-filters" id="md-filter-row">
|
||||
<button type="button" class="md-chip active" data-country="">🌐 Alle Länder</button>
|
||||
<?php foreach ($countryMeta as $c => $info): ?>
|
||||
<button type="button" class="md-chip" data-country="<?= esc($c) ?>">
|
||||
<?= $info['flag'] ?> <?= esc($info['label']) ?>
|
||||
</button>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<?php if (empty($kompetenzen)): ?>
|
||||
<p class="md-glossar-empty">Noch keine Kompetenzen zugeordnet.</p>
|
||||
<?php else: ?>
|
||||
<?php foreach ($kompetenzen as $k): ?>
|
||||
<article class="md-kompetenz">
|
||||
<header class="md-kompetenz-header">
|
||||
<div class="md-kompetenz-title"><?= esc($k['title']) ?></div>
|
||||
<span class="md-kompetenz-strength md-strength-<?= esc($k['strength']) ?>">
|
||||
<?= $k['strength'] === 'primaer' ? 'Primär' : 'Sekundär' ?>
|
||||
</span>
|
||||
</header>
|
||||
<?php if (!empty($k['description'])): ?>
|
||||
<p class="md-kompetenz-desc"><?= esc($k['description']) ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php
|
||||
$groups = [
|
||||
['anchors_fach', 'Fachbezug', 'Direkte Zuordnung im Fach-Lehrplan'],
|
||||
['anchors_uebergreifend', 'Fächerübergreifende Themen', 'Verbindliche Querschnittsthemen des Lehrplans'],
|
||||
['anchors_andere_faecher', 'Anknüpfungspunkte in anderen Fächern', 'Wo das Thema zusätzlich auftaucht'],
|
||||
];
|
||||
foreach ($groups as [$key, $heading, $sub]):
|
||||
$list = $k[$key] ?? [];
|
||||
if (empty($list)) continue;
|
||||
?>
|
||||
<div class="md-anchor-group">
|
||||
<div class="md-anchor-group-head">
|
||||
<span class="md-anchor-group-title"><?= esc($heading) ?></span>
|
||||
<span class="md-anchor-group-sub"><?= esc($sub) ?></span>
|
||||
</div>
|
||||
<div class="md-anchors">
|
||||
<?php foreach ($list as $a): ?>
|
||||
<?php $cm = $countryMeta[$a['country']] ?? ['flag' => '🏳️', 'label' => $a['country']]; ?>
|
||||
<div class="md-anchor md-anchor--<?= esc($a['anchor_type']) ?>"
|
||||
data-country="<?= esc($a['country']) ?>">
|
||||
<div class="md-anchor-flag" title="<?= esc($cm['label']) ?>"><?= $cm['flag'] ?></div>
|
||||
<div>
|
||||
<div class="md-anchor-meta">
|
||||
<strong><?= esc($a['country']) ?></strong>
|
||||
<?= $a['region'] ? ' · ' . esc($a['region']) : '' ?>
|
||||
<?= $a['school_type'] ? ' · ' . esc($a['school_type']) : '' ?>
|
||||
<?= $a['grade'] ? ' · ' . esc($a['grade']) : '' ?>
|
||||
<?= $a['subject'] ? ' · ' . esc($a['subject']) : '' ?>
|
||||
</div>
|
||||
<div class="md-anchor-title"><?= esc($a['title']) ?></div>
|
||||
<?php if (!empty($a['quote'])): ?>
|
||||
<div class="md-anchor-quote">„<?= esc($a['quote']) ?>"</div>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($a['reference'])): ?>
|
||||
<div class="md-anchor-ref">
|
||||
Quelle:
|
||||
<?php if (!empty($a['reference_url'])): ?>
|
||||
<a href="<?= esc($a['reference_url']) ?>"
|
||||
target="_blank" rel="noopener noreferrer"
|
||||
class="md-anchor-link"><?= esc($a['reference']) ?> ↗</a>
|
||||
<?php else: ?>
|
||||
<?= esc($a['reference']) ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</article>
|
||||
<?php endforeach; ?>
|
||||
<?php endif; ?>
|
||||
|
||||
<p class="md-lehrplan-note">
|
||||
Die Anker sind beispielhaft ausgewählt — bitte mit dem konkreten
|
||||
Jahres- oder Semesterplan abgleichen. Vollständige Lehrplan-Übersicht:
|
||||
<a href="<?= $bp ?>/pages/lehrplan.php">Lehrplan-Seite</a>.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<!-- Footer -->
|
||||
<footer class="md-footer">
|
||||
<img src="<?= $bp ?>/assets/img/bildLogo.png" alt="">
|
||||
<div>
|
||||
Lehrplan geograsim.at ·
|
||||
<a href="<?= $bp ?>/pages/simulationen.php">Simulationen</a> ·
|
||||
<a href="<?= $bp ?>/pages/lehrplan.php">Lehrplan</a> ·
|
||||
<a href="<?= $bp ?>/glossar">Glossar</a> ·
|
||||
<a href="<?= $bp ?>/ueber.html">Über</a> ·
|
||||
<a href="<?= $bp ?>/impressum.html">Impressum</a> ·
|
||||
<a href="<?= $bp ?>/datenschutz.html">Datenschutz</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// Länder-Filter für Lehrplan-Anker — übergreifende Themen + andere Fächer bleiben sichtbar (Land-agnostisch)
|
||||
(function () {
|
||||
const filterRow = document.getElementById('md-filter-row');
|
||||
if (!filterRow) return;
|
||||
filterRow.addEventListener('click', (e) => {
|
||||
const btn = e.target.closest('.md-chip');
|
||||
if (!btn) return;
|
||||
filterRow.querySelectorAll('.md-chip').forEach(c => c.classList.remove('active'));
|
||||
btn.classList.add('active');
|
||||
const country = btn.dataset.country;
|
||||
document.querySelectorAll('.md-anchor').forEach(a => {
|
||||
// Anker mit einem Land: nur zeigen wenn passt. Anker ohne Land oder mit allen Laendern: immer zeigen.
|
||||
const hide = country && a.dataset.country && a.dataset.country !== country;
|
||||
a.classList.toggle('hidden', !!hide);
|
||||
});
|
||||
});
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,658 @@
|
||||
<?php
|
||||
/**
|
||||
* Lehrplan-Übersicht
|
||||
* -------------------
|
||||
* Gesammelte Lehrplan-Verankerungen für alle Simulationen, gruppiert nach
|
||||
* didaktischem Kompetenzbereich, mit Filter für Land (AT/DE/CH/LI), Thema
|
||||
* und Anker-Typ.
|
||||
*
|
||||
* URL: http://localhost/geograsim/App/lehrplan (Front-Controller)
|
||||
* ?country=AT vorgewählter Länder-Filter (Default: AT)
|
||||
* ?theme=klima vorgewählter Themen-Filter
|
||||
* ?module=fluss vorgewähltes Modul
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../php/config/app.php';
|
||||
require_once __DIR__ . '/../php/config/db.php';
|
||||
require_once __DIR__ . '/../php/lib/EasyLang.php';
|
||||
|
||||
$db = getDB();
|
||||
|
||||
// Filter aus URL (sanft, nur Whitelist-Werte)
|
||||
$allowedCountries = ['AT', 'DE', 'CH', 'LI'];
|
||||
$qCountry = strtoupper(trim($_GET['country'] ?? 'AT'));
|
||||
if (!in_array($qCountry, $allowedCountries, true) && $qCountry !== 'ALL') {
|
||||
$qCountry = 'AT'; // Default
|
||||
}
|
||||
|
||||
$qTheme = preg_replace('/[^a-z0-9_-]/', '', strtolower($_GET['theme'] ?? ''));
|
||||
$qModule = preg_replace('/[^a-z0-9_-]/', '', strtolower($_GET['module'] ?? ''));
|
||||
|
||||
// Alle Kompetenzen mit Zuordnungen + Ankern laden
|
||||
$kompetenzen = $db->query('
|
||||
SELECT id, key_slug, title, description, category, age_min, age_max, sort_order
|
||||
FROM kompetenzen
|
||||
ORDER BY sort_order, title
|
||||
')->fetchAll();
|
||||
|
||||
// Module je Kompetenz
|
||||
$modStmt = $db->prepare('
|
||||
SELECT mi.module_id, mi.title, mi.icon, mi.card_image, mi.status, mi.page_url,
|
||||
km.strength
|
||||
FROM kompetenz_modules km
|
||||
INNER JOIN module_info mi ON mi.module_id = km.module_id
|
||||
WHERE km.kompetenz_id = ?
|
||||
ORDER BY FIELD(km.strength, "primaer", "sekundaer"), mi.sort_order
|
||||
');
|
||||
|
||||
// Anker je Kompetenz
|
||||
$ankerStmt = $db->prepare("
|
||||
SELECT anchor_type, country, region, school_type, grade, subject,
|
||||
title, reference, reference_url, quote
|
||||
FROM lehrplan_anchors
|
||||
WHERE kompetenz_id = ?
|
||||
ORDER BY FIELD(anchor_type, 'fach', 'uebergreifendes_thema', 'anderes_fach'),
|
||||
country, sort_order
|
||||
");
|
||||
|
||||
foreach ($kompetenzen as &$k) {
|
||||
$modStmt->execute([$k['id']]);
|
||||
$k['modules'] = $modStmt->fetchAll();
|
||||
|
||||
$ankerStmt->execute([$k['id']]);
|
||||
$rows = $ankerStmt->fetchAll();
|
||||
$k['anchors_fach'] = array_values(array_filter($rows, fn($r) => $r['anchor_type'] === 'fach'));
|
||||
$k['anchors_uebergreifend'] = array_values(array_filter($rows, fn($r) => $r['anchor_type'] === 'uebergreifendes_thema'));
|
||||
$k['anchors_andere_faecher'] = array_values(array_filter($rows, fn($r) => $r['anchor_type'] === 'anderes_fach'));
|
||||
$k['anchors_all'] = $rows;
|
||||
}
|
||||
unset($k);
|
||||
|
||||
// Zählungen (global) für Filter-Badges
|
||||
$totalAnchors = (int) $db->query('SELECT COUNT(*) FROM lehrplan_anchors')->fetchColumn();
|
||||
$countsByCountry = [];
|
||||
foreach ($db->query("SELECT country, COUNT(*) AS n FROM lehrplan_anchors GROUP BY country") as $r) {
|
||||
$countsByCountry[$r['country']] = (int)$r['n'];
|
||||
}
|
||||
$countsByType = [];
|
||||
foreach ($db->query("SELECT anchor_type, COUNT(*) AS n FROM lehrplan_anchors GROUP BY anchor_type") as $r) {
|
||||
$countsByType[$r['anchor_type']] = (int)$r['n'];
|
||||
}
|
||||
|
||||
$bp = BASE_PATH;
|
||||
$easyActive = EasyLang::isActive();
|
||||
|
||||
function esc(?string $s): string { return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
|
||||
|
||||
$countryMeta = [
|
||||
'AT' => ['flag' => '🇦🇹', 'label' => 'Österreich'],
|
||||
'DE' => ['flag' => '🇩🇪', 'label' => 'Deutschland'],
|
||||
'CH' => ['flag' => '🇨🇭', 'label' => 'Schweiz'],
|
||||
'LI' => ['flag' => '🇱🇮', 'label' => 'Liechtenstein'],
|
||||
];
|
||||
|
||||
$categoryMeta = [
|
||||
'klima' => ['label' => 'Klima'],
|
||||
'natur' => ['label' => 'Naturgefahren'],
|
||||
'raum' => ['label' => 'Raum'],
|
||||
'gesellschaft' => ['label' => 'Gesellschaft'],
|
||||
];
|
||||
?><!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Lehrplan-Übersicht — Lehrplan geograsim.at</title>
|
||||
<meta name="description" content="Alle Lehrplan-Verankerungen der GeoGraSim-Simulationen für Österreich, Deutschland, Schweiz und Liechtenstein — gruppiert nach didaktischem Kompetenzbereich.">
|
||||
<link rel="icon" type="image/svg+xml" href="<?= $bp ?>/favicon.svg">
|
||||
<link rel="icon" type="image/png" href="<?= $bp ?>/favicon-96x96.png" sizes="96x96">
|
||||
<link rel="stylesheet" href="<?= $bp ?>/assets/fonts/inter.css">
|
||||
<link rel="stylesheet" href="<?= $bp ?>/assets/css/design-system.css">
|
||||
<style>
|
||||
body { overflow: auto; height: auto; min-height: 100vh; background: var(--ggs-bg); }
|
||||
|
||||
/* ===== Topbar (wie modul-detail) ===== */
|
||||
.lp-topbar {
|
||||
background: var(--ggs-white);
|
||||
padding: .55rem 1.2rem;
|
||||
display: flex; align-items: center; gap: .8rem;
|
||||
border-bottom: 1px solid var(--ggs-border);
|
||||
position: sticky; top: 0; z-index: 50;
|
||||
}
|
||||
.lp-topbar a { text-decoration: none; }
|
||||
.lp-topbar-brand img { height: 40px; display: block; }
|
||||
.lp-topbar .spacer { flex: 1; }
|
||||
.lp-btn-sm {
|
||||
padding: .5rem .85rem; border: 1px solid var(--ggs-border);
|
||||
border-radius: var(--ggs-radius-sm); background: var(--ggs-white);
|
||||
font-size: .78rem; font-weight: 600; cursor: pointer;
|
||||
color: var(--ggs-text); font-family: inherit; text-decoration: none;
|
||||
display: inline-flex; align-items: center; min-height: 40px;
|
||||
}
|
||||
.lp-btn-sm:hover { background: var(--ggs-fjord-light); color: var(--ggs-fjord-dark); border-color: var(--ggs-fjord); }
|
||||
.lp-btn-sm.lp-btn-primary { background: var(--ggs-fjord); color: var(--ggs-white); border-color: var(--ggs-fjord); }
|
||||
.lp-easy-pill { background: var(--ggs-moss-light); color: var(--ggs-moss-dark); border-color: var(--ggs-moss); font-weight: 700; }
|
||||
|
||||
/* ===== Hero ===== */
|
||||
.lp-hero {
|
||||
background: linear-gradient(135deg, var(--ggs-fjord-dark) 0%, var(--ggs-fjord) 100%);
|
||||
color: var(--ggs-white);
|
||||
padding: 2.5rem 1.5rem 3rem;
|
||||
text-align: center;
|
||||
}
|
||||
.lp-hero h1 {
|
||||
font-size: clamp(1.8rem, 4vw, 2.6rem);
|
||||
font-weight: 900; letter-spacing: -.03em;
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
.lp-hero p { font-size: 1.02rem; color: rgba(255,255,255,.88); max-width: 640px; margin: 0 auto 1.4rem; }
|
||||
.lp-hero-stats { display: flex; justify-content: center; gap: 2rem; flex-wrap: wrap; margin-top: 1.2rem; }
|
||||
.lp-hero-stat { text-align: center; }
|
||||
.lp-hero-stat-num { font-size: 1.8rem; font-weight: 900; display: block; }
|
||||
.lp-hero-stat-lbl { font-size: .72rem; opacity: .8; text-transform: uppercase; letter-spacing: .08em; }
|
||||
|
||||
/* ===== Filter-Bar ===== */
|
||||
.lp-filters {
|
||||
max-width: 1100px; margin: -1.4rem auto 1.5rem;
|
||||
padding: 1rem 1.2rem; background: var(--ggs-white);
|
||||
border-radius: var(--ggs-radius-lg); box-shadow: var(--ggs-shadow-lg);
|
||||
border: 1px solid var(--ggs-border);
|
||||
position: relative; z-index: 5;
|
||||
display: flex; flex-direction: column; gap: .9rem;
|
||||
}
|
||||
.lp-filter-row {
|
||||
display: flex; align-items: center; flex-wrap: wrap; gap: .5rem;
|
||||
}
|
||||
.lp-filter-label {
|
||||
font-size: .72rem; font-weight: 800; text-transform: uppercase;
|
||||
letter-spacing: .08em; color: var(--ggs-text-muted);
|
||||
min-width: 60px;
|
||||
}
|
||||
.lp-chip {
|
||||
display: inline-flex; align-items: center; gap: .3rem;
|
||||
padding: .55rem 1rem; border-radius: var(--ggs-radius-pill);
|
||||
background: var(--ggs-white); border: 1.5px solid var(--ggs-border);
|
||||
font-size: .88rem; font-weight: 600; color: var(--ggs-text-muted);
|
||||
cursor: pointer; transition: all .2s var(--ggs-ease); font-family: inherit;
|
||||
min-height: 40px; min-width: 40px;
|
||||
}
|
||||
.lp-chip:hover { border-color: var(--ggs-fjord); color: var(--ggs-fjord); }
|
||||
.lp-chip:active { transform: scale(0.96); }
|
||||
.lp-chip.active { background: var(--ggs-fjord); color: var(--ggs-white); border-color: var(--ggs-fjord); }
|
||||
.lp-chip-badge {
|
||||
font-size: .7rem; padding: 1px 6px; border-radius: var(--ggs-radius-pill);
|
||||
background: var(--ggs-bg-dark); color: var(--ggs-text-muted);
|
||||
font-weight: 700;
|
||||
}
|
||||
.lp-chip.active .lp-chip-badge {
|
||||
background: rgba(255,255,255,.22); color: var(--ggs-white);
|
||||
}
|
||||
|
||||
/* ===== Kompetenz-Karten ===== */
|
||||
.lp-wrap { max-width: 1100px; margin: 0 auto 3rem; padding: 0 1.2rem; }
|
||||
.lp-results-info {
|
||||
font-size: .82rem; color: var(--ggs-text-muted);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.lp-kompetenz {
|
||||
background: var(--ggs-white); border: 1px solid var(--ggs-border);
|
||||
border-radius: var(--ggs-radius-md);
|
||||
padding: 1.4rem 1.6rem;
|
||||
margin-bottom: 1.2rem;
|
||||
scroll-margin-top: 80px;
|
||||
}
|
||||
.lp-kompetenz-head {
|
||||
display: flex; flex-wrap: wrap; align-items: flex-start;
|
||||
gap: .8rem; margin-bottom: .5rem;
|
||||
}
|
||||
.lp-kompetenz-title {
|
||||
font-size: 1.3rem; font-weight: 900; color: var(--ggs-fjord-dark);
|
||||
letter-spacing: -.02em;
|
||||
}
|
||||
.lp-kompetenz-cat {
|
||||
font-size: .68rem; font-weight: 700; text-transform: uppercase; letter-spacing: .08em;
|
||||
background: var(--ggs-fjord-light); color: var(--ggs-fjord-dark);
|
||||
padding: 3px 10px; border-radius: var(--ggs-radius-pill);
|
||||
}
|
||||
.lp-kompetenz-age {
|
||||
font-size: .75rem; color: var(--ggs-text-muted);
|
||||
background: var(--ggs-bg-dark); padding: 3px 10px;
|
||||
border-radius: var(--ggs-radius-pill);
|
||||
}
|
||||
.lp-kompetenz-desc {
|
||||
font-size: .93rem; line-height: 1.55; color: var(--ggs-text);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
/* Modul-Cards innerhalb einer Kompetenz */
|
||||
.lp-modules-label {
|
||||
font-size: .72rem; font-weight: 800; text-transform: uppercase;
|
||||
letter-spacing: .1em; color: var(--ggs-text-muted);
|
||||
margin: .4rem 0 .5rem;
|
||||
}
|
||||
.lp-modules {
|
||||
display: flex; flex-wrap: wrap; gap: .6rem; margin-bottom: 1.2rem;
|
||||
}
|
||||
.lp-module-card {
|
||||
display: inline-flex; align-items: center; gap: .55rem;
|
||||
padding: .55rem .95rem .55rem .7rem;
|
||||
background: var(--ggs-white); border: 1.5px solid var(--ggs-border);
|
||||
border-radius: var(--ggs-radius-md);
|
||||
text-decoration: none; color: var(--ggs-text);
|
||||
transition: all .2s var(--ggs-ease);
|
||||
min-height: 44px;
|
||||
}
|
||||
.lp-module-card:hover, .lp-module-card:active {
|
||||
border-color: var(--ggs-fjord); background: var(--ggs-fjord-light);
|
||||
transform: translateY(-1px);
|
||||
}
|
||||
.lp-module-icon { font-size: 1.3rem; line-height: 1; }
|
||||
.lp-module-name { font-size: .92rem; font-weight: 700; color: var(--ggs-fjord-dark); }
|
||||
.lp-module-strength {
|
||||
font-size: .62rem; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
|
||||
padding: 1px 6px; border-radius: var(--ggs-radius-sm);
|
||||
background: var(--ggs-moss-light); color: var(--ggs-moss-dark);
|
||||
}
|
||||
.lp-module-strength.sekundaer { background: var(--ggs-bg-dark); color: var(--ggs-text-muted); }
|
||||
.lp-module-status {
|
||||
font-size: .6rem; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
|
||||
padding: 1px 5px; border-radius: var(--ggs-radius-sm);
|
||||
}
|
||||
.lp-module-status.aktiv { background: var(--ggs-moss); color: var(--ggs-white); }
|
||||
.lp-module-status.beta { background: var(--ggs-sand-dark); color: var(--ggs-white); }
|
||||
.lp-module-status.geplant { background: var(--ggs-fjord); color: var(--ggs-white); opacity: .7; }
|
||||
|
||||
/* Anker — wie modul-detail */
|
||||
.lp-anchor-group { margin-top: .8rem; }
|
||||
.lp-anchor-group-head {
|
||||
display: flex; align-items: baseline; gap: .6rem;
|
||||
margin: .4rem 0 .5rem; flex-wrap: wrap;
|
||||
}
|
||||
.lp-anchor-group-title {
|
||||
font-size: .72rem; font-weight: 800; text-transform: uppercase;
|
||||
letter-spacing: .1em; color: var(--ggs-fjord-dark);
|
||||
}
|
||||
.lp-anchor-group-sub {
|
||||
font-size: .72rem; color: var(--ggs-text-muted); font-weight: 500;
|
||||
}
|
||||
.lp-anchors { display: grid; gap: .55rem; }
|
||||
.lp-anchor {
|
||||
display: grid; grid-template-columns: 28px 1fr; gap: .6rem; align-items: start;
|
||||
font-size: .9rem; padding: .6rem .8rem;
|
||||
background: var(--ggs-bg); border-radius: var(--ggs-radius-sm);
|
||||
border-left: 3px solid var(--ggs-fjord-light);
|
||||
}
|
||||
.lp-anchor-flag { font-size: 1.2rem; line-height: 1.3; text-align: center; }
|
||||
.lp-anchor-meta { font-size: .75rem; color: var(--ggs-text-muted); margin-bottom: .15rem; }
|
||||
.lp-anchor-title { font-weight: 700; color: var(--ggs-fjord-dark); margin-bottom: .2rem; }
|
||||
.lp-anchor-quote { font-size: .88rem; color: var(--ggs-text); font-style: italic; line-height: 1.45; }
|
||||
.lp-anchor-ref { font-size: .72rem; color: var(--ggs-text-muted); margin-top: .3rem; }
|
||||
.lp-anchor-link {
|
||||
color: var(--ggs-fjord); text-decoration: none; font-weight: 600;
|
||||
border-bottom: 1px dotted var(--ggs-fjord); padding: 2px 0;
|
||||
}
|
||||
.lp-anchor-link:hover { color: var(--ggs-fjord-dark); border-bottom-style: solid; }
|
||||
.lp-anchor--fach { border-left-color: var(--ggs-fjord); }
|
||||
.lp-anchor--uebergreifendes_thema { border-left-color: var(--ggs-moss); background: var(--ggs-moss-light); }
|
||||
.lp-anchor--anderes_fach { border-left-color: var(--ggs-sand-dark); background: var(--ggs-bg-dark); }
|
||||
.lp-anchor--uebergreifendes_thema .lp-anchor-flag,
|
||||
.lp-anchor--anderes_fach .lp-anchor-flag { opacity: .65; }
|
||||
.lp-anchor.hidden { display: none; }
|
||||
|
||||
.lp-kompetenz-empty {
|
||||
padding: 1rem 0; color: var(--ggs-text-muted); font-style: italic;
|
||||
font-size: .9rem;
|
||||
}
|
||||
|
||||
.lp-hint {
|
||||
font-size: .82rem; color: var(--ggs-text-muted); font-style: italic;
|
||||
margin-top: 1.5rem; max-width: 780px;
|
||||
}
|
||||
|
||||
.lp-toc {
|
||||
background: var(--ggs-white); border: 1px solid var(--ggs-border);
|
||||
border-radius: var(--ggs-radius-md);
|
||||
padding: 1rem 1.2rem;
|
||||
margin-bottom: 1.2rem;
|
||||
}
|
||||
.lp-toc-title {
|
||||
font-size: .72rem; font-weight: 800; text-transform: uppercase;
|
||||
letter-spacing: .1em; color: var(--ggs-text-muted);
|
||||
margin-bottom: .5rem;
|
||||
}
|
||||
.lp-toc-list { display: flex; flex-wrap: wrap; gap: .4rem; }
|
||||
.lp-toc-item {
|
||||
display: inline-flex; align-items: center; gap: .3rem;
|
||||
padding: .35rem .75rem; border-radius: var(--ggs-radius-pill);
|
||||
background: var(--ggs-fjord-light); color: var(--ggs-fjord-dark);
|
||||
font-size: .82rem; font-weight: 600; text-decoration: none;
|
||||
transition: background .2s var(--ggs-ease);
|
||||
min-height: 32px;
|
||||
}
|
||||
.lp-toc-item:hover { background: var(--ggs-fjord); color: var(--ggs-white); }
|
||||
.lp-toc-count { font-size: .7rem; opacity: .7; }
|
||||
|
||||
/* Footer */
|
||||
.lp-footer {
|
||||
text-align: center; padding: 2rem 1rem 2.5rem;
|
||||
font-size: .78rem; color: var(--ggs-text-muted);
|
||||
border-top: 1px solid var(--ggs-border);
|
||||
background: var(--ggs-white);
|
||||
margin-top: 2rem;
|
||||
}
|
||||
.lp-footer img { height: 36px; opacity: .5; margin-bottom: .5rem; }
|
||||
.lp-footer a { color: var(--ggs-fjord); text-decoration: none; padding: .3rem .2rem; }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.lp-filter-row { flex-direction: column; align-items: flex-start; gap: .3rem; }
|
||||
.lp-filter-label { min-width: 0; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="lp-topbar">
|
||||
<a href="<?= $bp ?>/index.html" class="lp-topbar-brand" aria-label="GeoGraSim Startseite">
|
||||
<img src="<?= $bp ?>/assets/img/geograsim_t.svg" alt="GeoGraSim">
|
||||
</a>
|
||||
<span class="spacer"></span>
|
||||
<?php if ($easyActive): ?>
|
||||
<span class="lp-btn-sm lp-easy-pill">🟢 Leichte Sprache</span>
|
||||
<?php endif; ?>
|
||||
<a href="<?= $bp ?>/pages/simulationen.php" class="lp-btn-sm">Simulationen</a>
|
||||
<a href="<?= $bp ?>/glossar" class="lp-btn-sm">Glossar</a>
|
||||
<a href="<?= $bp ?>/login.html" class="lp-btn-sm lp-btn-primary">Anmelden</a>
|
||||
</header>
|
||||
|
||||
<!-- HERO -->
|
||||
<section class="lp-hero">
|
||||
<h1>Lehrplan-Übersicht</h1>
|
||||
<p>
|
||||
Alle GeoGraSim-Simulationen sind auf didaktische Kompetenzbereiche
|
||||
abgebildet. Für jeden Bereich zeigen wir, wo er in den Lehrplänen
|
||||
für Österreich, Deutschland, Schweiz und Liechtenstein verankert ist —
|
||||
fachlich, fächerübergreifend und mit Anknüpfungspunkten in weiteren Fächern.
|
||||
</p>
|
||||
<div class="lp-hero-stats">
|
||||
<div class="lp-hero-stat"><span class="lp-hero-stat-num"><?= count($kompetenzen) ?></span><span class="lp-hero-stat-lbl">Kompetenzbereiche</span></div>
|
||||
<div class="lp-hero-stat"><span class="lp-hero-stat-num"><?= $totalAnchors ?></span><span class="lp-hero-stat-lbl">Lehrplan-Anker</span></div>
|
||||
<div class="lp-hero-stat"><span class="lp-hero-stat-num">4</span><span class="lp-hero-stat-lbl">Länder</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- FILTER -->
|
||||
<div class="lp-filters" id="lp-filters">
|
||||
<div class="lp-filter-row">
|
||||
<span class="lp-filter-label">Land</span>
|
||||
<button type="button" class="lp-chip<?= $qCountry === 'ALL' ? ' active' : '' ?>" data-filter="country" data-value="ALL">
|
||||
🌐 Alle <span class="lp-chip-badge"><?= $totalAnchors ?></span>
|
||||
</button>
|
||||
<?php foreach ($countryMeta as $c => $info): ?>
|
||||
<button type="button" class="lp-chip<?= $qCountry === $c ? ' active' : '' ?>" data-filter="country" data-value="<?= esc($c) ?>">
|
||||
<?= $info['flag'] ?> <?= esc($info['label']) ?>
|
||||
<?php if (!empty($countsByCountry[$c])): ?>
|
||||
<span class="lp-chip-badge"><?= $countsByCountry[$c] ?></span>
|
||||
<?php endif; ?>
|
||||
</button>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="lp-filter-row">
|
||||
<span class="lp-filter-label">Thema</span>
|
||||
<button type="button" class="lp-chip<?= !$qTheme ? ' active' : '' ?>" data-filter="theme" data-value="">
|
||||
Alle <span class="lp-chip-badge"><?= count($kompetenzen) ?></span>
|
||||
</button>
|
||||
<?php foreach ($kompetenzen as $k): ?>
|
||||
<?php $slug = esc($k['key_slug']); ?>
|
||||
<button type="button" class="lp-chip<?= $qTheme === $k['key_slug'] ? ' active' : '' ?>"
|
||||
data-filter="theme" data-value="<?= $slug ?>">
|
||||
<?= esc($k['title']) ?>
|
||||
</button>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="lp-filter-row">
|
||||
<span class="lp-filter-label">Typ</span>
|
||||
<button type="button" class="lp-chip active" data-filter="type" data-value="">
|
||||
Alle <span class="lp-chip-badge"><?= $totalAnchors ?></span>
|
||||
</button>
|
||||
<button type="button" class="lp-chip" data-filter="type" data-value="fach">
|
||||
Fachbezug <span class="lp-chip-badge"><?= $countsByType['fach'] ?? 0 ?></span>
|
||||
</button>
|
||||
<button type="button" class="lp-chip" data-filter="type" data-value="uebergreifendes_thema">
|
||||
Fächerübergreifend <span class="lp-chip-badge"><?= $countsByType['uebergreifendes_thema'] ?? 0 ?></span>
|
||||
</button>
|
||||
<button type="button" class="lp-chip" data-filter="type" data-value="anderes_fach">
|
||||
Andere Fächer <span class="lp-chip-badge"><?= $countsByType['anderes_fach'] ?? 0 ?></span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="lp-wrap">
|
||||
|
||||
<!-- TOC — Sprung zu Kompetenzen -->
|
||||
<nav class="lp-toc" aria-label="Sprung zu Kompetenz">
|
||||
<div class="lp-toc-title">Kompetenzbereiche auf dieser Seite</div>
|
||||
<div class="lp-toc-list">
|
||||
<?php foreach ($kompetenzen as $k):
|
||||
$total = count($k['anchors_all']); ?>
|
||||
<a class="lp-toc-item" href="#k-<?= esc($k['key_slug']) ?>">
|
||||
<?= esc($k['title']) ?>
|
||||
<span class="lp-toc-count"><?= $total ?></span>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div class="lp-results-info" id="lp-results-info"></div>
|
||||
|
||||
<?php foreach ($kompetenzen as $k): ?>
|
||||
<article class="lp-kompetenz" id="k-<?= esc($k['key_slug']) ?>"
|
||||
data-theme="<?= esc($k['key_slug']) ?>">
|
||||
<header class="lp-kompetenz-head">
|
||||
<div class="lp-kompetenz-title"><?= esc($k['title']) ?></div>
|
||||
<?php if (!empty($k['category']) && isset($categoryMeta[$k['category']])): ?>
|
||||
<span class="lp-kompetenz-cat"><?= esc($categoryMeta[$k['category']]['label']) ?></span>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($k['age_min']) && !empty($k['age_max'])): ?>
|
||||
<span class="lp-kompetenz-age">👧 <?= (int)$k['age_min'] ?>–<?= (int)$k['age_max'] ?> Jahre</span>
|
||||
<?php endif; ?>
|
||||
</header>
|
||||
|
||||
<?php if (!empty($k['description'])): ?>
|
||||
<p class="lp-kompetenz-desc"><?= esc($k['description']) ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Module, die dieser Kompetenz zugeordnet sind -->
|
||||
<?php if (!empty($k['modules'])): ?>
|
||||
<div class="lp-modules-label">Simulationen zu diesem Bereich</div>
|
||||
<div class="lp-modules">
|
||||
<?php foreach ($k['modules'] as $m): ?>
|
||||
<a class="lp-module-card" href="<?= $bp ?>/<?= esc($m['page_url']) ?>">
|
||||
<span class="lp-module-icon"><?= esc($m['icon']) ?></span>
|
||||
<span class="lp-module-name"><?= esc($m['title']) ?></span>
|
||||
<span class="lp-module-strength <?= esc($m['strength']) ?>">
|
||||
<?= $m['strength'] === 'primaer' ? 'Primär' : 'Sekundär' ?>
|
||||
</span>
|
||||
<span class="lp-module-status <?= esc($m['status']) ?>"><?= esc($m['status']) ?></span>
|
||||
</a>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<!-- Anker-Gruppen -->
|
||||
<?php
|
||||
$groups = [
|
||||
['anchors_fach', 'Fachbezug', 'Direkte Zuordnung im Fach-Lehrplan'],
|
||||
['anchors_uebergreifend', 'Fächerübergreifende Themen', 'Verbindliche Querschnittsthemen des Lehrplans'],
|
||||
['anchors_andere_faecher', 'Anknüpfungspunkte in anderen Fächern', 'Wo das Thema zusätzlich auftaucht'],
|
||||
];
|
||||
foreach ($groups as [$key, $heading, $sub]):
|
||||
$list = $k[$key] ?? [];
|
||||
if (empty($list)) continue;
|
||||
?>
|
||||
<div class="lp-anchor-group" data-group-type="<?= esc($key) ?>">
|
||||
<div class="lp-anchor-group-head">
|
||||
<span class="lp-anchor-group-title"><?= esc($heading) ?></span>
|
||||
<span class="lp-anchor-group-sub"><?= esc($sub) ?></span>
|
||||
</div>
|
||||
<div class="lp-anchors">
|
||||
<?php foreach ($list as $a): ?>
|
||||
<?php $cm = $countryMeta[$a['country']] ?? ['flag' => '🏳️', 'label' => $a['country']]; ?>
|
||||
<div class="lp-anchor lp-anchor--<?= esc($a['anchor_type']) ?>"
|
||||
data-country="<?= esc($a['country']) ?>"
|
||||
data-anchor-type="<?= esc($a['anchor_type']) ?>">
|
||||
<div class="lp-anchor-flag" title="<?= esc($cm['label']) ?>"><?= $cm['flag'] ?></div>
|
||||
<div>
|
||||
<div class="lp-anchor-meta">
|
||||
<strong><?= esc($a['country']) ?></strong>
|
||||
<?= $a['region'] ? ' · ' . esc($a['region']) : '' ?>
|
||||
<?= $a['school_type'] ? ' · ' . esc($a['school_type']) : '' ?>
|
||||
<?= $a['grade'] ? ' · ' . esc($a['grade']) : '' ?>
|
||||
<?= $a['subject'] ? ' · ' . esc($a['subject']) : '' ?>
|
||||
</div>
|
||||
<div class="lp-anchor-title"><?= esc($a['title']) ?></div>
|
||||
<?php if (!empty($a['quote'])): ?>
|
||||
<div class="lp-anchor-quote">„<?= esc($a['quote']) ?>"</div>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($a['reference'])): ?>
|
||||
<div class="lp-anchor-ref">
|
||||
Quelle:
|
||||
<?php if (!empty($a['reference_url'])): ?>
|
||||
<a href="<?= esc($a['reference_url']) ?>" target="_blank"
|
||||
rel="noopener noreferrer" class="lp-anchor-link"><?= esc($a['reference']) ?> ↗</a>
|
||||
<?php else: ?>
|
||||
<?= esc($a['reference']) ?>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<?php if (empty($k['anchors_all'])): ?>
|
||||
<div class="lp-kompetenz-empty">Noch keine Lehrplan-Anker hinterlegt.</div>
|
||||
<?php endif; ?>
|
||||
</article>
|
||||
<?php endforeach; ?>
|
||||
|
||||
<p class="lp-hint">
|
||||
Die Anker sind Beispiele — bitte mit dem konkreten Jahresplan abgleichen.
|
||||
Für Länder ausserhalb des eingestellten Fokus zeigen wir vorerst
|
||||
Referenz-Anker; fächerübergreifende Themen und andere Fächer bleiben
|
||||
landesunabhängig sichtbar.
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<footer class="lp-footer">
|
||||
<img src="<?= $bp ?>/assets/img/bildLogo.png" alt="">
|
||||
<div>
|
||||
Lehrplan geograsim.at ·
|
||||
<a href="<?= $bp ?>/pages/simulationen.php">Simulationen</a> ·
|
||||
<a href="<?= $bp ?>/glossar">Glossar</a> ·
|
||||
<a href="<?= $bp ?>/ueber.html">Über</a> ·
|
||||
<a href="<?= $bp ?>/impressum.html">Impressum</a> ·
|
||||
<a href="<?= $bp ?>/datenschutz.html">Datenschutz</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
// Filter-Logik: Land / Thema / Typ
|
||||
(function () {
|
||||
const filterBar = document.getElementById('lp-filters');
|
||||
const resultsInfo = document.getElementById('lp-results-info');
|
||||
|
||||
const state = {
|
||||
country: <?= json_encode($qCountry) ?>,
|
||||
theme: <?= json_encode($qTheme) ?>,
|
||||
type: '',
|
||||
};
|
||||
|
||||
// Chips aktiv-Zustand beim Laden setzen (falls URL-Filter vorgewählt)
|
||||
function activate(row, value) {
|
||||
filterBar.querySelectorAll(`.lp-chip[data-filter="${row}"]`).forEach(c => {
|
||||
c.classList.toggle('active', c.dataset.value === value);
|
||||
});
|
||||
}
|
||||
|
||||
filterBar.addEventListener('click', (e) => {
|
||||
const btn = e.target.closest('.lp-chip');
|
||||
if (!btn) return;
|
||||
const filter = btn.dataset.filter;
|
||||
const value = btn.dataset.value;
|
||||
state[filter] = value;
|
||||
activate(filter, value);
|
||||
apply();
|
||||
// URL aktualisieren, ohne Reload (damit Deep-Links funktionieren)
|
||||
const params = new URLSearchParams();
|
||||
if (state.country && state.country !== 'ALL') params.set('country', state.country);
|
||||
if (state.theme) params.set('theme', state.theme);
|
||||
const q = params.toString();
|
||||
history.replaceState(null, '', q ? '?' + q : location.pathname);
|
||||
});
|
||||
|
||||
function apply() {
|
||||
let shownKomp = 0;
|
||||
let shownAnchors = 0;
|
||||
document.querySelectorAll('.lp-kompetenz').forEach(k => {
|
||||
// Thema-Filter
|
||||
if (state.theme && k.dataset.theme !== state.theme) {
|
||||
k.style.display = 'none';
|
||||
return;
|
||||
}
|
||||
k.style.display = '';
|
||||
shownKomp++;
|
||||
|
||||
// Anker-Filter: Land + Typ
|
||||
k.querySelectorAll('.lp-anchor').forEach(a => {
|
||||
let hide = false;
|
||||
// Land: nur Anker mit Land vs gewaehltem Land einblenden; uebergr. + andere_fach sind landes-agnostisch, aber haben country=AT o.ae. als Default
|
||||
if (state.country && state.country !== 'ALL') {
|
||||
const anchorCountry = a.dataset.country;
|
||||
const type = a.dataset.anchorType;
|
||||
// Bei Fach-Ankern strikt auf Land filtern; bei übergreifenden und andere_fach Anker nur AT-Anker zeigen (wo das Lehrplan-System uebergreifend AT ist).
|
||||
// Alternative: Wenn country != AT gewaehlt, zeigen wir uebergreifende + andere_fach weiter,
|
||||
// aber nur Fach-Anker werden gefiltert — so bleibt das Lehrplan-Prinzip sichtbar.
|
||||
if (type === 'fach') {
|
||||
if (anchorCountry && anchorCountry !== state.country) hide = true;
|
||||
} else {
|
||||
// Fuer uebergreifend/andere_fach: zeige nur die, deren country matched (meist AT)
|
||||
if (anchorCountry && anchorCountry !== state.country) hide = true;
|
||||
}
|
||||
}
|
||||
if (state.type && a.dataset.anchorType !== state.type) hide = true;
|
||||
|
||||
a.classList.toggle('hidden', hide);
|
||||
if (!hide) shownAnchors++;
|
||||
});
|
||||
|
||||
// Wenn in einer Kompetenz keine Anker sichtbar sind, Kompetenz ausblenden
|
||||
const visibleInKomp = k.querySelectorAll('.lp-anchor:not(.hidden)').length;
|
||||
if (visibleInKomp === 0 && (state.country !== 'ALL' || state.type)) {
|
||||
k.style.display = 'none';
|
||||
shownKomp--;
|
||||
}
|
||||
|
||||
// Auch Anker-Gruppen ausblenden, wenn sie leer sind
|
||||
k.querySelectorAll('.lp-anchor-group').forEach(g => {
|
||||
const v = g.querySelectorAll('.lp-anchor:not(.hidden)').length;
|
||||
g.style.display = v === 0 ? 'none' : '';
|
||||
});
|
||||
});
|
||||
|
||||
// Ergebnis-Info aktualisieren
|
||||
const parts = [`${shownAnchors} Anker sichtbar`];
|
||||
if (shownKomp !== <?= count($kompetenzen) ?>) parts.push(`${shownKomp} Kompetenzbereich(e)`);
|
||||
resultsInfo.textContent = parts.join(' · ');
|
||||
}
|
||||
|
||||
// Initial
|
||||
apply();
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
/**
|
||||
* Modul-Detailseite: Flussmanagement
|
||||
* URL: http://localhost/geograsim/App/modul-fluss
|
||||
*/
|
||||
$MODULE_ID = 'fluss';
|
||||
require __DIR__ . '/_partials/modul_detail.php';
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
/**
|
||||
* Modul-Detailseite: Heli-Navigation
|
||||
* URL: http://localhost/geograsim/App/modul-heli
|
||||
*/
|
||||
$MODULE_ID = 'heli';
|
||||
require __DIR__ . '/_partials/modul_detail.php';
|
||||
@@ -0,0 +1,10 @@
|
||||
<?php
|
||||
/**
|
||||
* Modul-Detailseite: Klimawächter
|
||||
* URL: http://localhost/geograsim/App/modul-klima
|
||||
*
|
||||
* Die gesamte Logik + Layout liegt im gemeinsamen Partial. Dadurch bleiben
|
||||
* die vier modul-*.php-Seiten synchron.
|
||||
*/
|
||||
$MODULE_ID = 'klima';
|
||||
require __DIR__ . '/_partials/modul_detail.php';
|
||||
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
/**
|
||||
* Modul-Detailseite: Stadt & Raumplanung
|
||||
* URL: http://localhost/geograsim/App/modul-stadt
|
||||
*/
|
||||
$MODULE_ID = 'stadt';
|
||||
require __DIR__ . '/_partials/modul_detail.php';
|
||||
@@ -0,0 +1,458 @@
|
||||
<?php
|
||||
/**
|
||||
* Simulationen — Einstiegs-Grid
|
||||
* -----------------------------
|
||||
* Übersicht aller Module aus `module_info`. Zwei CTAs pro Card:
|
||||
* "Mehr erfahren" → modul-*.php Detailseite · "Sofort probieren" → play_url.
|
||||
*
|
||||
* URL: http://localhost/geograsim/App/simulationen
|
||||
* ?status=aktiv vorgewählter Status-Filter
|
||||
* ?theme=klima vorgewähltes Kompetenz-Thema
|
||||
*/
|
||||
|
||||
require_once __DIR__ . '/../php/config/app.php';
|
||||
require_once __DIR__ . '/../php/config/db.php';
|
||||
require_once __DIR__ . '/../php/lib/EasyLang.php';
|
||||
|
||||
$db = getDB();
|
||||
|
||||
// Filter aus URL
|
||||
$qStatus = preg_replace('/[^a-z]/', '', strtolower($_GET['status'] ?? ''));
|
||||
$qTheme = preg_replace('/[^a-z0-9_-]/', '', strtolower($_GET['theme'] ?? ''));
|
||||
|
||||
// Alle Module laden
|
||||
$modules = $db->query('
|
||||
SELECT module_id, title, subtitle, short_desc, short_desc_easy,
|
||||
duration_min, age_min, age_max, icon, card_image,
|
||||
page_url, play_url, status, sort_order
|
||||
FROM module_info
|
||||
ORDER BY FIELD(status, "aktiv", "beta", "geplant", "archiv"), sort_order, title
|
||||
')->fetchAll();
|
||||
|
||||
// Kompetenz-Zuordnung je Modul (fuer Theme-Chip + Thema-Filter)
|
||||
$kompMap = [];
|
||||
$kompQ = $db->query('
|
||||
SELECT km.module_id, k.key_slug, k.title, k.category, km.strength
|
||||
FROM kompetenz_modules km
|
||||
INNER JOIN kompetenzen k ON k.id = km.kompetenz_id
|
||||
ORDER BY km.strength, k.sort_order
|
||||
');
|
||||
foreach ($kompQ as $r) {
|
||||
$kompMap[$r['module_id']][] = $r;
|
||||
}
|
||||
|
||||
// Alle moeglichen Themen / Kompetenzen einsammeln (Filter-Chips)
|
||||
$themes = $db->query('SELECT key_slug, title, category FROM kompetenzen ORDER BY sort_order')->fetchAll();
|
||||
|
||||
// Zaehlungen pro Status
|
||||
$counts = ['aktiv' => 0, 'beta' => 0, 'geplant' => 0, 'archiv' => 0];
|
||||
foreach ($modules as $m) {
|
||||
$counts[$m['status']] = ($counts[$m['status']] ?? 0) + 1;
|
||||
}
|
||||
|
||||
$bp = BASE_PATH;
|
||||
$easyActive = EasyLang::isActive();
|
||||
|
||||
function esc(?string $s): string { return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }
|
||||
|
||||
$statusMeta = [
|
||||
'aktiv' => ['label' => 'Aktiv', 'tone' => 'moss', 'icon' => '✓'],
|
||||
'beta' => ['label' => 'Beta', 'tone' => 'sand', 'icon' => '⚠'],
|
||||
'geplant' => ['label' => 'In Entw.', 'tone' => 'fjord', 'icon' => '🔒'],
|
||||
'archiv' => ['label' => 'Archiv', 'tone' => 'muted', 'icon' => '□'],
|
||||
];
|
||||
?><!DOCTYPE html>
|
||||
<html lang="de">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Simulationen — Lehrplan geograsim.at</title>
|
||||
<meta name="description" content="Alle GeoGraSim-Simulationen auf einen Blick — Klimawächter, Flussmanagement, Heli-Navigation, Stadt & Raumplanung und weitere in Entwicklung.">
|
||||
<link rel="icon" type="image/svg+xml" href="<?= $bp ?>/favicon.svg">
|
||||
<link rel="icon" type="image/png" href="<?= $bp ?>/favicon-96x96.png" sizes="96x96">
|
||||
<link rel="stylesheet" href="<?= $bp ?>/assets/fonts/inter.css">
|
||||
<link rel="stylesheet" href="<?= $bp ?>/assets/css/design-system.css">
|
||||
<style>
|
||||
body { overflow: auto; height: auto; min-height: 100vh; background: var(--ggs-bg); }
|
||||
|
||||
/* Topbar identisch zu lehrplan.php */
|
||||
.sp-topbar {
|
||||
background: var(--ggs-white); padding: .55rem 1.2rem;
|
||||
display: flex; align-items: center; gap: .8rem;
|
||||
border-bottom: 1px solid var(--ggs-border);
|
||||
position: sticky; top: 0; z-index: 50;
|
||||
}
|
||||
.sp-topbar a { text-decoration: none; }
|
||||
.sp-topbar-brand img { height: 40px; display: block; }
|
||||
.sp-topbar .spacer { flex: 1; }
|
||||
.sp-btn-sm {
|
||||
padding: .5rem .85rem; border: 1px solid var(--ggs-border);
|
||||
border-radius: var(--ggs-radius-sm); background: var(--ggs-white);
|
||||
font-size: .78rem; font-weight: 600; cursor: pointer;
|
||||
color: var(--ggs-text); font-family: inherit; text-decoration: none;
|
||||
display: inline-flex; align-items: center; min-height: 40px;
|
||||
}
|
||||
.sp-btn-sm:hover { background: var(--ggs-fjord-light); color: var(--ggs-fjord-dark); border-color: var(--ggs-fjord); }
|
||||
.sp-btn-sm.sp-btn-primary { background: var(--ggs-fjord); color: var(--ggs-white); border-color: var(--ggs-fjord); }
|
||||
.sp-easy-pill { background: var(--ggs-moss-light); color: var(--ggs-moss-dark); border-color: var(--ggs-moss); font-weight: 700; }
|
||||
|
||||
/* Hero */
|
||||
.sp-hero {
|
||||
background: linear-gradient(135deg, var(--ggs-fjord-dark) 0%, var(--ggs-fjord) 100%);
|
||||
color: var(--ggs-white); padding: 2.5rem 1.5rem 3rem; text-align: center;
|
||||
}
|
||||
.sp-hero h1 { font-size: clamp(1.8rem, 4vw, 2.6rem); font-weight: 900; letter-spacing: -.03em; margin-bottom: .5rem; }
|
||||
.sp-hero p { font-size: 1.02rem; color: rgba(255,255,255,.88); max-width: 640px; margin: 0 auto 1.4rem; }
|
||||
.sp-hero-stats { display: flex; justify-content: center; gap: 2rem; flex-wrap: wrap; margin-top: 1.2rem; }
|
||||
.sp-hero-stat { text-align: center; }
|
||||
.sp-hero-stat-num { font-size: 1.8rem; font-weight: 900; display: block; }
|
||||
.sp-hero-stat-lbl { font-size: .72rem; opacity: .8; text-transform: uppercase; letter-spacing: .08em; }
|
||||
|
||||
/* Filter-Bar */
|
||||
.sp-filters {
|
||||
max-width: 1200px; margin: -1.4rem auto 1.5rem;
|
||||
padding: 1rem 1.2rem; background: var(--ggs-white);
|
||||
border-radius: var(--ggs-radius-lg); box-shadow: var(--ggs-shadow-lg);
|
||||
border: 1px solid var(--ggs-border);
|
||||
position: relative; z-index: 5;
|
||||
display: flex; flex-direction: column; gap: .9rem;
|
||||
}
|
||||
.sp-filter-row { display: flex; align-items: center; flex-wrap: wrap; gap: .5rem; }
|
||||
.sp-filter-label {
|
||||
font-size: .72rem; font-weight: 800; text-transform: uppercase;
|
||||
letter-spacing: .08em; color: var(--ggs-text-muted); min-width: 60px;
|
||||
}
|
||||
.sp-chip {
|
||||
display: inline-flex; align-items: center; gap: .3rem;
|
||||
padding: .55rem 1rem; border-radius: var(--ggs-radius-pill);
|
||||
background: var(--ggs-white); border: 1.5px solid var(--ggs-border);
|
||||
font-size: .88rem; font-weight: 600; color: var(--ggs-text-muted);
|
||||
cursor: pointer; transition: all .2s var(--ggs-ease); font-family: inherit;
|
||||
min-height: 40px; min-width: 40px;
|
||||
}
|
||||
.sp-chip:hover { border-color: var(--ggs-fjord); color: var(--ggs-fjord); }
|
||||
.sp-chip:active { transform: scale(0.96); }
|
||||
.sp-chip.active { background: var(--ggs-fjord); color: var(--ggs-white); border-color: var(--ggs-fjord); }
|
||||
.sp-chip-badge {
|
||||
font-size: .7rem; padding: 1px 6px; border-radius: var(--ggs-radius-pill);
|
||||
background: var(--ggs-bg-dark); color: var(--ggs-text-muted); font-weight: 700;
|
||||
}
|
||||
.sp-chip.active .sp-chip-badge { background: rgba(255,255,255,.22); color: var(--ggs-white); }
|
||||
|
||||
/* Grid */
|
||||
.sp-wrap { max-width: 1200px; margin: 0 auto 3rem; padding: 0 1.2rem; }
|
||||
.sp-results-info {
|
||||
font-size: .82rem; color: var(--ggs-text-muted);
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.sp-grid {
|
||||
display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
|
||||
gap: 1.4rem;
|
||||
}
|
||||
|
||||
/* Modul-Card */
|
||||
.sp-card {
|
||||
background: var(--ggs-white); border: 1px solid var(--ggs-border);
|
||||
border-radius: var(--ggs-radius-lg); overflow: hidden;
|
||||
display: flex; flex-direction: column;
|
||||
transition: transform .25s var(--ggs-ease), box-shadow .25s var(--ggs-ease),
|
||||
border-color .25s var(--ggs-ease);
|
||||
box-shadow: var(--ggs-shadow-sm);
|
||||
}
|
||||
.sp-card:hover { transform: translateY(-3px); box-shadow: var(--ggs-shadow-md);
|
||||
border-color: var(--ggs-fjord-light); }
|
||||
.sp-card.sp-card-geplant { opacity: .88; }
|
||||
.sp-card-media {
|
||||
aspect-ratio: 16 / 10; overflow: hidden; position: relative;
|
||||
background: var(--ggs-fjord-light);
|
||||
}
|
||||
.sp-card-media img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
||||
.sp-card-badge {
|
||||
position: absolute; top: 10px; right: 10px;
|
||||
font-size: .7rem; font-weight: 700; padding: 4px 10px;
|
||||
border-radius: var(--ggs-radius-pill); letter-spacing: .04em;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
.sp-card-badge.moss { background: var(--ggs-moss); color: var(--ggs-white); }
|
||||
.sp-card-badge.sand { background: var(--ggs-sand-dark); color: var(--ggs-white); }
|
||||
.sp-card-badge.fjord { background: var(--ggs-fjord); color: var(--ggs-white); opacity: .85; }
|
||||
.sp-card-badge.muted { background: var(--ggs-text-muted); color: var(--ggs-white); }
|
||||
|
||||
.sp-card-body {
|
||||
padding: 1rem 1.2rem 1.2rem; display: flex; flex-direction: column; gap: .6rem;
|
||||
flex: 1;
|
||||
}
|
||||
.sp-card-title {
|
||||
font-size: 1.2rem; font-weight: 800; color: var(--ggs-fjord-dark);
|
||||
letter-spacing: -.02em; display: flex; align-items: center; gap: .4rem;
|
||||
line-height: 1.2;
|
||||
}
|
||||
.sp-card-icon { font-size: 1.4rem; line-height: 1; }
|
||||
.sp-card-subtitle {
|
||||
font-size: .88rem; color: var(--ggs-text); line-height: 1.5;
|
||||
flex: 1;
|
||||
}
|
||||
.sp-card-meta {
|
||||
display: flex; flex-wrap: wrap; gap: .4rem;
|
||||
margin-top: .3rem;
|
||||
}
|
||||
.sp-card-meta-item {
|
||||
font-size: .72rem; color: var(--ggs-text-muted);
|
||||
background: var(--ggs-bg); padding: 3px 9px;
|
||||
border-radius: var(--ggs-radius-pill);
|
||||
display: inline-flex; align-items: center; gap: .25rem;
|
||||
}
|
||||
.sp-card-themes {
|
||||
display: flex; flex-wrap: wrap; gap: .3rem;
|
||||
margin-top: .2rem;
|
||||
}
|
||||
.sp-card-theme {
|
||||
font-size: .7rem; color: var(--ggs-fjord-dark);
|
||||
background: var(--ggs-fjord-light); padding: 2px 8px;
|
||||
border-radius: var(--ggs-radius-pill); font-weight: 600;
|
||||
}
|
||||
.sp-card-cta {
|
||||
display: flex; gap: .5rem; flex-wrap: wrap; margin-top: .4rem;
|
||||
}
|
||||
.sp-btn {
|
||||
display: inline-flex; align-items: center; justify-content: center;
|
||||
gap: .35rem; padding: .7rem 1rem;
|
||||
border-radius: var(--ggs-radius-sm);
|
||||
font-weight: 700; font-size: .88rem;
|
||||
text-decoration: none; border: 1.5px solid transparent;
|
||||
transition: all .2s var(--ggs-ease); min-height: 44px;
|
||||
flex: 1; text-align: center;
|
||||
}
|
||||
.sp-btn-primary { background: var(--ggs-fjord); color: var(--ggs-white); }
|
||||
.sp-btn-primary:hover { background: var(--ggs-fjord-dark); }
|
||||
.sp-btn-primary:active { transform: scale(0.98); }
|
||||
.sp-btn-secondary {
|
||||
background: var(--ggs-white); color: var(--ggs-fjord);
|
||||
border-color: var(--ggs-fjord);
|
||||
}
|
||||
.sp-btn-secondary:hover { background: var(--ggs-fjord-light); }
|
||||
.sp-btn-secondary:active { transform: scale(0.98); }
|
||||
.sp-btn-disabled {
|
||||
background: var(--ggs-bg); color: var(--ggs-text-muted);
|
||||
border-color: var(--ggs-border); cursor: not-allowed;
|
||||
}
|
||||
|
||||
.sp-empty {
|
||||
grid-column: 1 / -1;
|
||||
text-align: center; padding: 3rem 1rem;
|
||||
color: var(--ggs-text-muted); font-style: italic;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.sp-footer {
|
||||
text-align: center; padding: 2rem 1rem 2.5rem;
|
||||
font-size: .78rem; color: var(--ggs-text-muted);
|
||||
border-top: 1px solid var(--ggs-border);
|
||||
background: var(--ggs-white); margin-top: 2rem;
|
||||
}
|
||||
.sp-footer img { height: 36px; opacity: .5; margin-bottom: .5rem; }
|
||||
.sp-footer a { color: var(--ggs-fjord); text-decoration: none; padding: .3rem .2rem; }
|
||||
|
||||
@media (max-width: 900px) {
|
||||
.sp-filter-row { flex-direction: column; align-items: flex-start; gap: .3rem; }
|
||||
.sp-filter-label { min-width: 0; }
|
||||
.sp-grid { grid-template-columns: 1fr; }
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<header class="sp-topbar">
|
||||
<a href="<?= $bp ?>/index.html" class="sp-topbar-brand" aria-label="GeoGraSim Startseite">
|
||||
<img src="<?= $bp ?>/assets/img/geograsim_t.svg" alt="GeoGraSim">
|
||||
</a>
|
||||
<span class="spacer"></span>
|
||||
<?php if ($easyActive): ?>
|
||||
<span class="sp-btn-sm sp-easy-pill">🟢 Leichte Sprache</span>
|
||||
<?php endif; ?>
|
||||
<a href="<?= $bp ?>/pages/lehrplan.php" class="sp-btn-sm">Lehrplan</a>
|
||||
<a href="<?= $bp ?>/glossar" class="sp-btn-sm">Glossar</a>
|
||||
<a href="<?= $bp ?>/login.html" class="sp-btn-sm sp-btn-primary">Anmelden</a>
|
||||
</header>
|
||||
|
||||
<section class="sp-hero">
|
||||
<h1>Alle Simulationen</h1>
|
||||
<p>
|
||||
Zu jedem Modul gibt es eine Detailseite mit Lernzielen, Glossar-Begriffen
|
||||
und Lehrplan-Bezug für Österreich, Deutschland, Schweiz und Liechtenstein.
|
||||
Die aktiven Module lassen sich sofort ausprobieren.
|
||||
</p>
|
||||
<div class="sp-hero-stats">
|
||||
<div class="sp-hero-stat"><span class="sp-hero-stat-num"><?= count($modules) ?></span><span class="sp-hero-stat-lbl">Module</span></div>
|
||||
<div class="sp-hero-stat"><span class="sp-hero-stat-num"><?= $counts['aktiv'] ?></span><span class="sp-hero-stat-lbl">Aktiv</span></div>
|
||||
<div class="sp-hero-stat"><span class="sp-hero-stat-num"><?= $counts['geplant'] ?></span><span class="sp-hero-stat-lbl">In Entwicklung</span></div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<div class="sp-filters" id="sp-filters">
|
||||
<div class="sp-filter-row">
|
||||
<span class="sp-filter-label">Status</span>
|
||||
<button type="button" class="sp-chip<?= !$qStatus ? ' active' : '' ?>" data-filter="status" data-value="">
|
||||
Alle <span class="sp-chip-badge"><?= count($modules) ?></span>
|
||||
</button>
|
||||
<?php foreach (['aktiv','beta','geplant'] as $s):
|
||||
if (!($counts[$s] ?? 0)) continue; ?>
|
||||
<button type="button" class="sp-chip<?= $qStatus === $s ? ' active' : '' ?>"
|
||||
data-filter="status" data-value="<?= esc($s) ?>">
|
||||
<?= esc($statusMeta[$s]['label']) ?>
|
||||
<span class="sp-chip-badge"><?= (int)$counts[$s] ?></span>
|
||||
</button>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
|
||||
<div class="sp-filter-row">
|
||||
<span class="sp-filter-label">Thema</span>
|
||||
<button type="button" class="sp-chip<?= !$qTheme ? ' active' : '' ?>" data-filter="theme" data-value="">
|
||||
Alle
|
||||
</button>
|
||||
<?php foreach ($themes as $t): ?>
|
||||
<button type="button" class="sp-chip<?= $qTheme === $t['key_slug'] ? ' active' : '' ?>"
|
||||
data-filter="theme" data-value="<?= esc($t['key_slug']) ?>">
|
||||
<?= esc($t['title']) ?>
|
||||
</button>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sp-wrap">
|
||||
<div class="sp-results-info" id="sp-results-info"></div>
|
||||
|
||||
<div class="sp-grid" id="sp-grid">
|
||||
<?php foreach ($modules as $m):
|
||||
$shortText = EasyLang::pick($m['short_desc'] ?? null, $m['short_desc_easy'] ?? null);
|
||||
$displayText = $shortText ?: ($m['subtitle'] ?? '');
|
||||
$status = $m['status'];
|
||||
$statusInfo = $statusMeta[$status] ?? $statusMeta['beta'];
|
||||
$themesForModule = $kompMap[$m['module_id']] ?? [];
|
||||
$themeSlugs = array_map(fn($r) => $r['key_slug'], $themesForModule);
|
||||
$themeAttr = implode(',', $themeSlugs);
|
||||
$canPlay = in_array($status, ['aktiv','beta'], true);
|
||||
?>
|
||||
<article class="sp-card sp-card-<?= esc($status) ?>"
|
||||
data-status="<?= esc($status) ?>"
|
||||
data-themes="<?= esc($themeAttr) ?>">
|
||||
<div class="sp-card-media">
|
||||
<?php if (!empty($m['card_image'])): ?>
|
||||
<img src="<?= $bp ?>/assets/img/<?= esc($m['card_image']) ?>" alt="<?= esc($m['title']) ?>">
|
||||
<?php endif; ?>
|
||||
<span class="sp-card-badge <?= esc($statusInfo['tone']) ?>">
|
||||
<?= esc($statusInfo['icon']) ?> <?= esc($statusInfo['label']) ?>
|
||||
</span>
|
||||
</div>
|
||||
<div class="sp-card-body">
|
||||
<h2 class="sp-card-title">
|
||||
<span class="sp-card-icon"><?= esc($m['icon']) ?></span>
|
||||
<?= esc($m['title']) ?>
|
||||
</h2>
|
||||
<p class="sp-card-subtitle"><?= esc($displayText) ?></p>
|
||||
|
||||
<div class="sp-card-meta">
|
||||
<?php if (!empty($m['duration_min'])): ?>
|
||||
<span class="sp-card-meta-item">⏱ ca. <?= (int)$m['duration_min'] ?> Min</span>
|
||||
<?php endif; ?>
|
||||
<?php if (!empty($m['age_min']) && !empty($m['age_max'])): ?>
|
||||
<span class="sp-card-meta-item">👧 <?= (int)$m['age_min'] ?>–<?= (int)$m['age_max'] ?> J</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
<?php if (!empty($themesForModule)): ?>
|
||||
<div class="sp-card-themes">
|
||||
<?php foreach ($themesForModule as $t): ?>
|
||||
<span class="sp-card-theme" title="Kompetenzbereich"><?= esc($t['title']) ?></span>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
|
||||
<div class="sp-card-cta">
|
||||
<?php if (!empty($m['page_url'])): ?>
|
||||
<a class="sp-btn sp-btn-secondary" href="<?= $bp ?>/<?= esc($m['page_url']) ?>">
|
||||
📖 Mehr erfahren
|
||||
</a>
|
||||
<?php endif; ?>
|
||||
<?php if ($canPlay && !empty($m['play_url'])): ?>
|
||||
<a class="sp-btn sp-btn-primary" href="<?= $bp ?>/<?= esc($m['play_url']) ?>">
|
||||
▶ Sofort probieren
|
||||
</a>
|
||||
<?php else: ?>
|
||||
<span class="sp-btn sp-btn-disabled" aria-disabled="true">
|
||||
🔒 In Entwicklung
|
||||
</span>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
</div>
|
||||
</article>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<footer class="sp-footer">
|
||||
<img src="<?= $bp ?>/assets/img/bildLogo.png" alt="">
|
||||
<div>
|
||||
Lehrplan geograsim.at ·
|
||||
<a href="<?= $bp ?>/pages/lehrplan.php">Lehrplan</a> ·
|
||||
<a href="<?= $bp ?>/glossar">Glossar</a> ·
|
||||
<a href="<?= $bp ?>/ueber.html">Über</a> ·
|
||||
<a href="<?= $bp ?>/impressum.html">Impressum</a> ·
|
||||
<a href="<?= $bp ?>/datenschutz.html">Datenschutz</a>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
(function () {
|
||||
const bar = document.getElementById('sp-filters');
|
||||
const info = document.getElementById('sp-results-info');
|
||||
const cards = document.querySelectorAll('.sp-card');
|
||||
const total = cards.length;
|
||||
|
||||
const state = {
|
||||
status: <?= json_encode($qStatus) ?>,
|
||||
theme: <?= json_encode($qTheme) ?>,
|
||||
};
|
||||
|
||||
bar.addEventListener('click', (e) => {
|
||||
const btn = e.target.closest('.sp-chip');
|
||||
if (!btn) return;
|
||||
const f = btn.dataset.filter;
|
||||
const v = btn.dataset.value;
|
||||
state[f] = v;
|
||||
bar.querySelectorAll(`.sp-chip[data-filter="${f}"]`).forEach(c => {
|
||||
c.classList.toggle('active', c.dataset.value === v);
|
||||
});
|
||||
apply();
|
||||
const params = new URLSearchParams();
|
||||
if (state.status) params.set('status', state.status);
|
||||
if (state.theme) params.set('theme', state.theme);
|
||||
const q = params.toString();
|
||||
history.replaceState(null, '', q ? '?' + q : location.pathname);
|
||||
});
|
||||
|
||||
function apply() {
|
||||
let shown = 0;
|
||||
cards.forEach(c => {
|
||||
let hide = false;
|
||||
if (state.status && c.dataset.status !== state.status) hide = true;
|
||||
if (state.theme) {
|
||||
const ts = (c.dataset.themes || '').split(',').filter(Boolean);
|
||||
if (!ts.includes(state.theme)) hide = true;
|
||||
}
|
||||
c.style.display = hide ? 'none' : '';
|
||||
if (!hide) shown++;
|
||||
});
|
||||
if (shown === total && !state.status && !state.theme) {
|
||||
info.textContent = '';
|
||||
} else {
|
||||
info.textContent = `${shown} von ${total} Modulen sichtbar`;
|
||||
}
|
||||
}
|
||||
|
||||
apply();
|
||||
})();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user