Stadionzeitung: Ergebnis- und Gegnerseite neu gestaltet
Ergebnisse laufen als Anzeigetafel (Wappen und Name je Seite, Ergebnis gross in der Mitte) statt als Listenzeilen. Verworfen und im CSS begruendet: Tore am rechten Blattrand, getoente Karten je Spiel, roter Strich unter der Mannschaftszeile, graue Verlierer-Ziffern. Vereinsnamen einzeilig, sonst ausgepunktet. Die Gegnerseite bekommt ein symmetrisches Duell, eine aus den Tabellenwerten abgeleitete Aufmacher-Zeile, den Vergleich als Balkenpaar aus der Mitte und einen Tabellenausschnitt ueber die geteilte league-table-Komponente. Die Balkenlaengen kommen aus Klassen in 5-Prozent-Stufen, nie aus einem style-Attribut (die CSP verwirft inline styles stillschweigend). Das Mitglied-werden-Poster sagt jetzt "Werde Teil unseres Vereins". Alle geaenderten Seiten auf A5-Hoehe nachgemessen, kein Ueberstand. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Hv9SGDu3m3diiPrW9Fmj3v
This commit is contained in:
@@ -40,19 +40,28 @@ $matchcenterLink = club_domain('matchcenter');
|
||||
<section class="stadionzeitung-ergebnisse__team">
|
||||
<h3 class="stadionzeitung-ergebnisse__team-name"><?= e(trim(implode(' · ', array_filter([(string) ($group['team_name'] ?? ''), (string) ($group['league'] ?? '')])))) ?></h3>
|
||||
<?php foreach ($group['matches'] ?? [] as $match): ?>
|
||||
<div class="stadionzeitung-ergebnisse__match">
|
||||
<p class="stadionzeitung-ergebnisse__meta">
|
||||
<?= e(trim((string) ($match['date_display'] ?? ''))) ?><?php if (($match['competition'] ?? 'Liga') !== 'Liga'): ?> · <?= e((string) $match['competition']) ?><?php endif; ?>
|
||||
</p>
|
||||
<?php foreach ([['club' => $match['home'] ?? '', 'crest' => $match['home_crest'] ?? null, 'goals' => $match['goals_home'] ?? null],
|
||||
['club' => $match['away'] ?? '', 'crest' => $match['away_crest'] ?? null, 'goals' => $match['goals_away'] ?? null]] as $side): ?>
|
||||
<div class="stadionzeitung-ergebnisse__row<?= str_contains((string) $side['club'], 'TSV 08 Kulmbach') ? ' stadionzeitung-ergebnisse__row--own' : '' ?>">
|
||||
<?php
|
||||
$sides = [
|
||||
['club' => $match['home'] ?? '', 'crest' => $match['home_crest'] ?? null, 'goals' => $match['goals_home'] ?? null],
|
||||
['club' => $match['away'] ?? '', 'crest' => $match['away_crest'] ?? null, 'goals' => $match['goals_away'] ?? null],
|
||||
];
|
||||
?>
|
||||
<article class="stadionzeitung-ergebnisse__match">
|
||||
<?php foreach ($sides as $i => $side): ?>
|
||||
<?php if ($i === 1): ?>
|
||||
<div class="stadionzeitung-ergebnisse__score">
|
||||
<p class="stadionzeitung-ergebnisse__result">
|
||||
<span class="stadionzeitung-ergebnisse__goals"><?= e((string) $sides[0]['goals']) ?></span><span class="stadionzeitung-ergebnisse__colon">:</span><span class="stadionzeitung-ergebnisse__goals"><?= e((string) $sides[1]['goals']) ?></span>
|
||||
</p>
|
||||
<p class="stadionzeitung-ergebnisse__meta"><?= e(trim((string) ($match['date_display'] ?? ''))) ?><?php if (($match['competition'] ?? 'Liga') !== 'Liga'): ?> · <?= e((string) $match['competition']) ?><?php endif; ?></p>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
<div class="stadionzeitung-ergebnisse__side<?= str_contains((string) $side['club'], 'TSV 08 Kulmbach') ? ' stadionzeitung-ergebnisse__side--own' : '' ?>">
|
||||
<?php component('crest', ['src' => $side['crest'], 'name' => (string) $side['club']]); ?>
|
||||
<span class="stadionzeitung-ergebnisse__club"><?= e((string) $side['club']) ?></span>
|
||||
<span class="stadionzeitung-ergebnisse__goals"><?= e((string) $side['goals']) ?></span>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
</article>
|
||||
<?php endforeach; ?>
|
||||
</section>
|
||||
<?php endforeach; ?>
|
||||
|
||||
@@ -18,15 +18,29 @@ $teamName = trim((string) ($info['team_name'] ?? ''));
|
||||
$fixture = is_array($info['fixture'] ?? null) ? $info['fixture'] : [];
|
||||
$own = is_array($info['own'] ?? null) ? $info['own'] : null;
|
||||
$opponent = is_array($info['opponent_stats'] ?? null) ? $info['opponent_stats'] : null;
|
||||
$opponentName = (string) ($info['opponent'] ?? 'Unser Gegner');
|
||||
// Gedruckt wird der Anzeigename (Kurzname der Ausgabe, wenn gepflegt) — die
|
||||
// BFV-Schreibweise „(SG1) TSV Wirsberg I/ FC Kupferberg I" ist ein Suchschlüssel,
|
||||
// keine Überschrift.
|
||||
$ausschnitt = is_array($info['table_excerpt'] ?? null) ? $info['table_excerpt'] : [];
|
||||
$opponentName = (string) ($info['opponent_display'] ?? $info['opponent'] ?? 'Unser Gegner');
|
||||
|
||||
// Links steht IMMER unsere Mannschaft (Klasse --own hebt sie hervor), rechts der
|
||||
// Gegner. Bei einem Heimspiel ist das ohnehin die Reihenfolge Heim/Gast; fährt die
|
||||
// Mannschaft aber auswärts, trüge die Hervorhebung sonst der gegnerische Verein.
|
||||
$istHeim = !empty($fixture['is_home']);
|
||||
$ownName = (string) (($istHeim ? ($fixture['home'] ?? null) : ($fixture['away'] ?? null)) ?? 'TSV 08 Kulmbach');
|
||||
$ownCrest = ($istHeim ? ($fixture['home_crest'] ?? null) : ($fixture['away_crest'] ?? null)) ?? $own['crest'] ?? null;
|
||||
$gegnerLabel = $opponentName;
|
||||
$gegnerCrest = ($istHeim ? ($fixture['away_crest'] ?? null) : ($fixture['home_crest'] ?? null)) ?? $opponent['crest'] ?? null;
|
||||
|
||||
$kickoff = (string) ($fixture['kickoff'] ?? '');
|
||||
$kickoffDate = $kickoff !== '' ? substr($kickoff, 0, 10) : '';
|
||||
$kickoffTime = $kickoff !== '' ? substr($kickoff, 11, 5) : '';
|
||||
|
||||
// Der Tabellenplatz steht NICHT in der Balkenliste: klein ist dort besser,
|
||||
// ein Balken läge genau falsch herum. Er gehört nach oben neben die Wappen.
|
||||
// „Spiele“ fällt ebenfalls raus (für beide gleich, steht in der Kopfzeile).
|
||||
$rows = [
|
||||
['label' => 'Tabellenplatz', 'key' => 'rank'],
|
||||
['label' => 'Spiele', 'key' => 'played'],
|
||||
['label' => 'Siege', 'key' => 'won'],
|
||||
['label' => 'Unentschieden', 'key' => 'drawn'],
|
||||
['label' => 'Niederlagen', 'key' => 'lost'],
|
||||
@@ -41,6 +55,57 @@ $format = static function (?array $row, string $key, bool $signed = false): stri
|
||||
$value = (int) $row[$key];
|
||||
return $signed && $value > 0 ? '+' . $value : (string) $value;
|
||||
};
|
||||
|
||||
// Balkenlänge als KLASSE, nie als style-Attribut: die CSP verwirft inline
|
||||
// styles stillschweigend (siehe CLAUDE.md), der Balken wäre in Produktion
|
||||
// unsichtbar. Deshalb 5-Prozent-Stufen, die components.css ausdefiniert.
|
||||
$barStep = static function (?array $row, ?array $other, string $key): int {
|
||||
if ($row === null) {
|
||||
return 0;
|
||||
}
|
||||
$value = abs((int) ($row[$key] ?? 0));
|
||||
$scale = max($value, $other === null ? 0 : abs((int) ($other[$key] ?? 0)));
|
||||
if ($scale <= 0) {
|
||||
return 0;
|
||||
}
|
||||
return (int) round($value / $scale * 20);
|
||||
};
|
||||
|
||||
// Ein Satz statt einer Zahlenkolonne: derselbe Bestand, nur in Sprache. Die
|
||||
// erste zutreffende Regel gewinnt, damit immer genau eine Aussage dasteht.
|
||||
$zahlwort = static function (int $n): string {
|
||||
$woerter = [0 => 'Kein', 1 => 'Ein', 2 => 'Zwei', 3 => 'Drei', 4 => 'Vier', 5 => 'Fünf',
|
||||
6 => 'Sechs', 7 => 'Sieben', 8 => 'Acht', 9 => 'Neun', 10 => 'Zehn', 11 => 'Elf',
|
||||
12 => 'Zwölf', 13 => 'Dreizehn', 14 => 'Vierzehn', 15 => 'Fünfzehn'];
|
||||
return $woerter[$n] ?? (string) $n;
|
||||
};
|
||||
$aufmacher = '';
|
||||
if ($opponent !== null) {
|
||||
$played = (int) ($opponent['played'] ?? 0);
|
||||
$won = (int) ($opponent['won'] ?? 0);
|
||||
$lost = (int) ($opponent['lost'] ?? 0);
|
||||
$rank = (int) ($opponent['rank'] ?? 0);
|
||||
$points = (int) ($opponent['points'] ?? 0);
|
||||
$diff = (int) ($opponent['goal_diff'] ?? 0);
|
||||
if ($played > 0 && $lost === 0) {
|
||||
$aufmacher = 'Noch ohne Niederlage';
|
||||
} elseif ($played > 0 && $won === 0) {
|
||||
$aufmacher = 'Noch ohne Sieg';
|
||||
} elseif ($played > 0 && $won / $played >= 0.7) {
|
||||
$aufmacher = $zahlwort($won) . ' Siege aus ' . mb_strtolower($zahlwort($played)) . ' Spielen';
|
||||
} elseif ($rank === 1) {
|
||||
$aufmacher = 'Tabellenführer mit ' . $points . ' Punkten';
|
||||
} elseif ($played > 0 && $diff <= -8) {
|
||||
$aufmacher = $zahlwort(abs($diff)) . ' Tore mehr kassiert als geschossen';
|
||||
} elseif ($played > 0 && $diff >= 8) {
|
||||
$aufmacher = $zahlwort($diff) . ' Tore mehr geschossen als kassiert';
|
||||
} elseif ($played > 0) {
|
||||
$aufmacher = $points . ' Punkte aus ' . $played . ' Spielen';
|
||||
}
|
||||
}
|
||||
$spieltagInfo = $opponent !== null && (int) ($opponent['played'] ?? 0) > 0
|
||||
? 'Stand nach ' . (int) $opponent['played'] . ' Spieltagen'
|
||||
: '';
|
||||
?>
|
||||
<div class="stadionzeitung-gegner">
|
||||
<header class="stadionzeitung-page__head">
|
||||
@@ -53,25 +118,26 @@ $format = static function (?array $row, string $key, bool $signed = false): stri
|
||||
<span class="stadionzeitung-page__rule" aria-hidden="true"></span>
|
||||
|
||||
<div class="stadionzeitung-gegner__duell">
|
||||
<div class="stadionzeitung-gegner__club stadionzeitung-gegner__club--own">
|
||||
<?php component('crest', ['src' => $fixture['home_crest'] ?? $own['crest'] ?? null, 'name' => $fixture['home'] ?? 'TSV 08 Kulmbach']); ?>
|
||||
<strong><?= e((string) ($fixture['home'] ?? 'TSV 08 Kulmbach')) ?></strong>
|
||||
<div class="stadionzeitung-gegner__club">
|
||||
<?php component('crest', ['src' => $ownCrest, 'name' => $ownName]); ?>
|
||||
<span class="stadionzeitung-gegner__club-name"><?= e($ownName) ?></span>
|
||||
</div>
|
||||
<div class="stadionzeitung-gegner__match">
|
||||
<span>gegen</span>
|
||||
<div class="stadionzeitung-gegner__vs">
|
||||
<p class="stadionzeitung-gegner__vs-wort"><?= $istHeim ? 'gegen' : 'bei' ?></p>
|
||||
<?php if ($kickoffDate !== ''): ?>
|
||||
<strong><?= e(german_date($kickoffDate)) ?></strong>
|
||||
<?php endif; ?>
|
||||
<?php if ($kickoffTime !== ''): ?>
|
||||
<span><?= e($kickoffTime) ?> Uhr</span>
|
||||
<p class="stadionzeitung-gegner__anstoss"><?= e(german_date($kickoffDate)) ?><?php if ($kickoffTime !== ''): ?><br><?= e($kickoffTime) ?> Uhr<?php endif; ?></p>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
<div class="stadionzeitung-gegner__club">
|
||||
<?php component('crest', ['src' => $fixture['away_crest'] ?? $opponent['crest'] ?? null, 'name' => $fixture['away'] ?? $opponentName]); ?>
|
||||
<strong><?= e((string) ($fixture['away'] ?? $opponentName)) ?></strong>
|
||||
<?php component('crest', ['src' => $gegnerCrest, 'name' => $gegnerLabel]); ?>
|
||||
<span class="stadionzeitung-gegner__club-name"><?= e($gegnerLabel) ?></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<?php if ($aufmacher !== ''): ?>
|
||||
<p class="stadionzeitung-gegner__aufmacher"><?= e($aufmacher) ?></p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($own !== null && $opponent !== null): ?>
|
||||
<div class="stadionzeitung-gegner__vergleich" role="table" aria-label="Tabellenvergleich">
|
||||
<div class="stadionzeitung-gegner__vergleich-head" role="row">
|
||||
@@ -79,18 +145,33 @@ $format = static function (?array $row, string $key, bool $signed = false): stri
|
||||
Markup — das Kürzel des Marktschorgast-Gegners wäre in jeder Ausgabe
|
||||
mitgedruckt worden. Die Überschrift nennt den Gegner ohnehin. */ ?>
|
||||
<span role="columnheader">Wir</span>
|
||||
<span role="columnheader">Vergleich</span>
|
||||
<span role="columnheader"><?= e($spieltagInfo !== '' ? $spieltagInfo : 'Vergleich') ?></span>
|
||||
<span role="columnheader">Gegner</span>
|
||||
</div>
|
||||
<?php foreach ($rows as $row): ?>
|
||||
<div class="stadionzeitung-gegner__vergleich-row" role="row">
|
||||
<strong role="cell"><?= e($format($own, (string) $row['key'], (bool) ($row['signed'] ?? false))) ?></strong>
|
||||
<span role="rowheader"><?= e((string) $row['label']) ?></span>
|
||||
<strong role="cell"><?= e($format($opponent, (string) $row['key'], (bool) ($row['signed'] ?? false))) ?></strong>
|
||||
<strong class="stadionzeitung-gegner__wert" role="cell"><?= e($format($own, (string) $row['key'], (bool) ($row['signed'] ?? false))) ?></strong>
|
||||
<span class="stadionzeitung-gegner__balken stadionzeitung-gegner__balken--own" aria-hidden="true"><span class="stadionzeitung-gegner__balken-fuellung stadionzeitung-gegner__balken-fuellung--s<?= $barStep($own, $opponent, (string) $row['key']) ?>"></span></span>
|
||||
<span class="stadionzeitung-gegner__label" role="rowheader"><?= e((string) $row['label']) ?></span>
|
||||
<span class="stadionzeitung-gegner__balken" aria-hidden="true"><span class="stadionzeitung-gegner__balken-fuellung stadionzeitung-gegner__balken-fuellung--s<?= $barStep($opponent, $own, (string) $row['key']) ?>"></span></span>
|
||||
<strong class="stadionzeitung-gegner__wert" role="cell"><?= e($format($opponent, (string) $row['key'], (bool) ($row['signed'] ?? false))) ?></strong>
|
||||
</div>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php else: ?>
|
||||
<p class="stadionzeitung-gegner__empty">Die aktuellen Vergleichsdaten waren beim letzten Abruf nicht verfügbar.</p>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php /* Der Tabellenplatz stand kurz als zwei lose Ziffern oben im Duell und
|
||||
wirkte dort unruhig (Felix, 19.09.2026). Hier zeigt ihn der Ausschnitt
|
||||
im Zusammenhang: wer steht zwischen uns und ihnen. Gerendert mit der
|
||||
geteilten league-table-Komponente, nie mit einer zweiten Tabelle. */ ?>
|
||||
<?php if ($ausschnitt !== []): ?>
|
||||
<div class="stadionzeitung-gegner__ausschnitt">
|
||||
<p class="stadionzeitung-gegner__ausschnitt-titel">Die Tabelle dazwischen</p>
|
||||
<?php foreach ($ausschnitt as $block): ?>
|
||||
<?php component('league-table', ['table' => $block, 'caption' => 'Tabellenausschnitt ' . (string) ($info['league'] ?? '')]); ?>
|
||||
<?php endforeach; ?>
|
||||
</div>
|
||||
<?php endif; ?>
|
||||
</div>
|
||||
|
||||
@@ -4,7 +4,7 @@ declare(strict_types=1);
|
||||
|
||||
/**
|
||||
* Mitglied-werden-Seite der Stadionzeitung (Live-Seite) — Poster statt Liste:
|
||||
* riesige zentrierte Coolvetica-Headline („Werde Teil des Nullachters", das
|
||||
* riesige zentrierte Coolvetica-Headline („Werde Teil unseres Vereins", das
|
||||
* letzte Wort in Vereinsrot), darunter Claim-Satz und Abteilungszeile,
|
||||
* unten der geteilte CTA-Fuß mit QR auf /mitglied-werden. Fester Baustein
|
||||
* jeder Ausgabe (bin/stadionzeitung-add.php plant sie nach den
|
||||
@@ -25,8 +25,8 @@ $mitgliedLink = club_domain('mitglied-werden');
|
||||
<img class="stadionzeitung-mitglied__logo" src="<?= e(asset('img/logo.svg')) ?>" alt="" width="75" height="97">
|
||||
<h2 class="stadionzeitung-mitglied__headline">
|
||||
<span class="stadionzeitung-mitglied__line">Werde Teil</span>
|
||||
<span class="stadionzeitung-mitglied__line">des</span>
|
||||
<span class="stadionzeitung-mitglied__line stadionzeitung-mitglied__line--accent">Nullachters</span>
|
||||
<span class="stadionzeitung-mitglied__line">unseres</span>
|
||||
<span class="stadionzeitung-mitglied__line stadionzeitung-mitglied__line--accent">Vereins</span>
|
||||
</h2>
|
||||
<p class="stadionzeitung-mitglied__claim">Seit 1908 in Kulmbach zuhause. Egal ob auf dem Platz, in der Halle oder am Spielfeldrand: bei uns ist Platz für dich.</p>
|
||||
<p class="stadionzeitung-mitglied__departments">Fußball · Jugend · Turnen</p>
|
||||
|
||||
Reference in New Issue
Block a user