Files
geograsim/App/pages/lehrplan.php
T
Adminator f1b6fc0373 Lehrplan: Speicher-Snapshot — viewport-fit=cover + 100dvh + Status
- iPad-Verbesserungen vom Linter:
  meta viewport bekommt viewport-fit=cover (Safari Notch)
  body min-height nutzt 100dvh statt 100vh (dynamic viewport)
- _status.md: Save-Punkt fuer Crash-Recovery dokumentiert
- Inbox-Check: keine offenen Nachrichten an lehrplan

DB-Bilanz live: module_info 11, kompetenzen 11,
kompetenz_modules 21, lehrplan_anchors 108

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-29 07:57:56 +02:00

731 lines
31 KiB
PHP
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
<?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';
require_once __DIR__ . '/../php/lib/Country.php';
$db = getDB();
// Filter: Default ist das aktuelle Landes-Setting (Kaskade), per URL überschreibbar
$allowedCountries = ['AT', 'DE', 'CH', 'LI'];
if (isset($_GET['country'])) {
$qCountry = strtoupper(trim($_GET['country']));
if (!in_array($qCountry, $allowedCountries, true) && $qCountry !== 'ALL') {
$qCountry = Country::current();
}
} else {
$qCountry = Country::current();
}
$qTheme = preg_replace('/[^a-z0-9_-]/', '', strtolower($_GET['theme'] ?? ''));
$qModule = preg_replace('/[^a-z0-9_-]/', '', strtolower($_GET['module'] ?? ''));
// Wenn `?module=X` gesetzt: Metadaten des Moduls laden (fuer Banner + Titel)
$moduleFocus = null;
if ($qModule !== '') {
$stmt = $db->prepare('SELECT module_id, title, icon, card_image, status, page_url FROM module_info WHERE module_id = ?');
$stmt->execute([$qModule]);
$moduleFocus = $stmt->fetch() ?: null;
if (!$moduleFocus) $qModule = ''; // ungueltiger Slug
}
// Kompetenzen laden — optional gefiltert auf Modul-Zuordnungen
if ($qModule !== '') {
$stmt = $db->prepare('
SELECT k.id, k.key_slug, k.title, k.description, k.category, k.age_min, k.age_max, k.sort_order
FROM kompetenzen k
INNER JOIN kompetenz_modules km ON km.kompetenz_id = k.id
WHERE km.module_id = ?
GROUP BY k.id
ORDER BY k.sort_order, k.title
');
$stmt->execute([$qModule]);
$kompetenzen = $stmt->fetchAll();
} else {
$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, viewport-fit=cover">
<title><?= $moduleFocus ? 'Lehrplan-Bezug: ' . esc($moduleFocus['title']) : 'Lehrplan-Übersicht' ?> — Lehrplan geograsim.at</title>
<meta name="description" content="<?= $moduleFocus ? 'Lehrplan-Verankerung der Simulation ' . esc($moduleFocus['title']) . ' in Österreich, Deutschland, Schweiz und Liechtenstein.' : '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: 100dvh; 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; }
/* ===== Modul-Fokus-Banner (bei ?module=X) ===== */
.lp-focus-banner {
background: var(--ggs-fjord-light); color: var(--ggs-fjord-dark);
border-bottom: 1px solid var(--ggs-border);
padding: .9rem 1.5rem;
display: flex; align-items: center; gap: 1rem;
flex-wrap: wrap;
}
.lp-focus-icon { font-size: 1.8rem; line-height: 1; }
.lp-focus-text { flex: 1; min-width: 200px; display: flex; flex-direction: column; gap: 2px; }
.lp-focus-text strong { font-size: 1rem; font-weight: 800; }
.lp-focus-sub { font-size: .82rem; color: var(--ggs-text-muted); }
.lp-focus-action {
display: inline-flex; align-items: center; min-height: 36px;
padding: .4rem .9rem; border-radius: var(--ggs-radius-pill);
background: var(--ggs-white); color: var(--ggs-fjord-dark);
border: 1px solid var(--ggs-fjord);
font-size: .82rem; font-weight: 600; text-decoration: none;
transition: all .2s var(--ggs-ease);
}
.lp-focus-action:hover { background: var(--ggs-fjord); color: var(--ggs-white); }
/* ===== 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; ?>
<?php include __DIR__ . '/_partials/country_picker.php'; ?>
<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>
<?php if ($moduleFocus): ?>
<!-- Modul-Fokus-Banner: der Link kommt aus einer Simulation oder Modul-Detailseite -->
<div class="lp-focus-banner">
<span class="lp-focus-icon"><?= esc($moduleFocus['icon']) ?></span>
<div class="lp-focus-text">
<strong>Lehrplan-Bezug für <?= esc($moduleFocus['title']) ?></strong>
<span class="lp-focus-sub">Gefiltert auf die Kompetenzbereiche dieser Simulation.</span>
</div>
<?php if (!empty($moduleFocus['page_url'])): ?>
<a href="<?= $bp ?>/<?= esc($moduleFocus['page_url']) ?>" class="lp-focus-action">← Zurück zur Simulation</a>
<?php endif; ?>
<a href="<?= $bp ?>/pages/lehrplan.php" class="lp-focus-action">Alle Module anzeigen</a>
</div>
<?php endif; ?>
<!-- HERO -->
<section class="lp-hero">
<h1><?= $moduleFocus ? 'Lehrplan-Bezug: ' . esc($moduleFocus['title']) : 'Lehrplan-Übersicht' ?></h1>
<p>
<?php if ($moduleFocus): ?>
Diese Ansicht zeigt alle Kompetenzbereiche, die die Simulation
<strong><?= esc($moduleFocus['title']) ?></strong> abdeckt — fachlich,
fächerübergreifend und mit Anknüpfungspunkten in weiteren Fächern.
<?php else: ?>
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.
<?php endif; ?>
</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"><?= $moduleFocus ? 'Kompetenzen für dieses Modul' : 'Kompetenzbereiche' ?></span></div>
<div class="lp-hero-stat"><span class="lp-hero-stat-num"><?= $totalAnchors ?></span><span class="lp-hero-stat-lbl">Lehrplan-Anker gesamt</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
landes­unabhä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>
<?php include __DIR__ . '/_partials/country_footer.php'; ?>
</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>