gwoe-antragspruefer/app/templates/v2/screens/scorecard.html
Dotty Dotter 4734e89522 fix: Matrix groesser (110px Cells statt 88px) — fuellt mehr Vertikalraum
Vorige Version hatte ~110 px Slack zwischen Matrix-Legende und Begruendung.
Matrix-Cells von 88×88 auf 110×110 hochgezogen, Label-Spalte 130→150 px,
Symbol-Schrift 19→24 pt, Zeilen-Header 36→40 px Hoehe.

Resultat: Matrix-Grid jetzt ca. 700×586 px (vorher 570×476). Slack im
matrix-block (flex-grow) deutlich kleiner, Card visueller dichter
gefuellt.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 13:34:02 +02:00

454 lines
14 KiB
HTML
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.

{# scorecard.html — Social-Card-Render-Template
Drei Formate, ueber `width`/`height` parametrisiert:
- 1200×630 (og) — LinkedIn/Twitter-OG; horizontal mit Matrix rechts
- 1080×1080 (square) — Instagram klassisch; vertikal gestackt
- 1080×1350 (portrait) — Instagram 4:5 Hochformat (Default Feed);
Title + grosser Score + grosse Matrix + Begruendung
`is_portrait` und `is_og` schalten Layout-Varianten in den CSS-Switches.
#}
{% set is_og = (width >= 1200) %}
{% set is_portrait = (height > width) %}
{% set is_square = (not is_og and not is_portrait) %}
<!DOCTYPE html>
<html lang="de">
<head>
<meta charset="UTF-8">
<title>Scorecard — {{ assessment.title }}</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
html, body { font-family: 'Source Sans Pro', 'Helvetica Neue', sans-serif; }
body {
width: {{ width }}px;
height: {{ height }}px;
background: linear-gradient(135deg, #f8faf2 0%, #e9ede0 100%);
color: #1f1f1f;
overflow: hidden;
position: relative;
}
.card {
width: 100%;
height: 100%;
padding: {% if is_og %}28px 38px{% elif is_portrait %}34px 38px 24px{% else %}26px 30px{% endif %};
display: flex;
flex-direction: column;
justify-content: space-between;
position: relative;
}
.header {
display: flex;
justify-content: space-between;
align-items: baseline;
border-bottom: 2px solid #009DA5;
padding-bottom: 10px;
}
.kicker {
font-family: 'Source Code Pro', monospace;
font-size: {% if is_portrait %}13pt{% else %}12pt{% endif %};
color: #009DA5;
text-transform: uppercase;
letter-spacing: 0.1em;
}
.meta {
font-family: 'Source Code Pro', monospace;
font-size: 11pt;
color: #555;
}
/* ── og + square: zweispaltig (og) bzw. einspaltig (square) ── */
.body-grid {
flex: 1;
display: grid;
grid-template-columns: {% if is_og %}1.4fr 1fr{% else %}1fr{% endif %};
gap: 28px;
margin-top: 22px;
}
.left-col h1 {
font-size: {% if is_og %}28pt{% else %}22pt{% endif %};
line-height: 1.18;
margin-bottom: 14px;
color: #1f1f1f;
font-weight: 700;
}
.left-col .fraktionen {
font-family: 'Source Code Pro', monospace;
font-size: 11pt;
color: #555;
margin-bottom: 18px;
}
.left-col .fraktionen .pill {
display: inline-block;
padding: 2px 10px;
background: rgba(136, 158, 51, 0.15);
color: #44570a;
border-radius: 3px;
margin-right: 5px;
}
.left-col .verdict {
font-size: 14pt;
color: #1a7f37;
font-weight: 600;
margin-bottom: 12px;
}
.left-col .summary {
font-size: 12pt;
line-height: 1.55;
color: #333;
display: -webkit-box;
-webkit-line-clamp: {% if is_og %}6{% else %}4{% endif %};
-webkit-box-orient: vertical;
overflow: hidden;
}
.right-col {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-start;
padding-top: 8px;
}
.score-big {
font-family: 'Source Code Pro', monospace;
font-size: {% if is_og %}88pt{% else %}72pt{% endif %};
font-weight: 700;
line-height: 1;
color: {{ score_color }};
margin-bottom: 4px;
}
.score-label {
font-family: 'Source Code Pro', monospace;
font-size: 11pt;
text-transform: uppercase;
letter-spacing: 0.1em;
color: #555;
margin-bottom: 16px;
}
/* ── Matrix-Zellen (alle Formate gleiche Klassen) ── */
.matrix .cell {
border-radius: 2px;
display: flex;
align-items: center;
justify-content: center;
font-family: 'Source Code Pro', monospace;
font-size: {% if is_portrait %}24pt{% elif is_og %}10pt{% else %}10pt{% endif %};
font-weight: 700;
}
.cell.r-pp { background: #889E33; color: #fff; }
.cell.r-p { background: #cddaa1; color: #44570a; }
/* r-0: kraeftiger Grauton — gegen den weissen Matrix-Frame muss er
deutlich abstehen, sonst sieht das Grid loechrig aus. */
.cell.r-0 { background: #b8b8b2; color: #4a4a44; }
.cell.r-n { background: #efc9c3; color: #931515; }
.cell.r-nn { background: #9A2A2A; color: #fff; }
/* ── og/square Matrix-Container ── */
.matrix-compact {
display: grid;
grid-template-columns: repeat(5, 1fr);
grid-template-rows: repeat(5, 1fr);
gap: 2px;
width: {% if is_og %}220px{% else %}180px{% endif %};
aspect-ratio: 1 / 1;
}
/* ── portrait Layout — ausbalancierte vertikale Komposition.
Statt justify-content: space-between (verteilt Slack-Raum unkontrol-
liert) absorbiert .portrait-matrix-block per flex-grow:1 die ueber-
schuessige Hoehe. Matrix bleibt visuell zentriert, andere Sektionen
in natuerlicher Groesse oben/unten. */
.portrait-body {
flex: 1;
display: flex;
flex-direction: column;
gap: 14px;
margin-top: 16px;
padding-bottom: 32px;
}
.portrait-title {
font-size: 26pt;
line-height: 1.18;
color: #1f1f1f;
font-weight: 700;
/* Begrenze Titel auf 3 Zeilen — sonst dominiert er die Karte. */
display: -webkit-box;
-webkit-line-clamp: 3;
-webkit-box-orient: vertical;
overflow: hidden;
}
.portrait-fraktionen {
font-family: 'Source Code Pro', monospace;
font-size: 13pt;
color: #555;
}
.portrait-fraktionen .pill {
display: inline-block;
padding: 4px 14px;
background: rgba(136, 158, 51, 0.18);
color: #44570a;
border-radius: 3px;
margin-right: 6px;
font-weight: 700;
}
/* ── Score-Hero: full-width Farbband, dominanter visueller Anker ── */
.portrait-score-hero {
display: flex;
align-items: center;
gap: 32px;
padding: 22px 30px;
background:
{% if score_color == '#1a7f37' %}#e8eed1
{% elif score_color == '#bf6c10' %}#f4e6cf
{% else %}#f1dcda{% endif %};
border-left: 8px solid {{ score_color }};
border-radius: 4px;
}
.portrait-score-num {
font-family: 'Source Code Pro', monospace;
font-size: 132pt;
font-weight: 700;
line-height: 0.85;
color: {{ score_color }};
flex-shrink: 0;
letter-spacing: -0.04em;
}
.portrait-score-num .slash {
font-size: 60pt;
opacity: 0.5;
letter-spacing: 0;
}
.portrait-score-side {
display: flex;
flex-direction: column;
gap: 10px;
flex: 1;
}
.portrait-score-label {
font-family: 'Source Code Pro', monospace;
font-size: 12pt;
text-transform: uppercase;
letter-spacing: 0.14em;
color: #555;
}
.portrait-verdict {
font-size: 30pt;
color: {{ score_color }};
font-weight: 700;
line-height: 1.08;
}
/* ── Matrix-Block: Grid mit beschrifteten Achsen, vertikal zentriert.
flex-grow:1 absorbiert allen verbleibenden Slack der Karte. */
.portrait-matrix-block {
flex-grow: 1;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 12px;
}
.portrait-matrix-grid {
/* 5 Werte als Spalten + 1 Spalte fuer die Zeilen-Labels.
Cells 110×110, Label-Spalte 150 → 700×586 px Gesamt. */
display: grid;
grid-template-columns: 150px repeat(5, 110px);
grid-template-rows: 40px repeat(5, 110px);
gap: 5px;
align-items: stretch;
}
.portrait-matrix-grid .col-label {
font-family: 'Source Code Pro', monospace;
font-size: 9.5pt;
font-weight: 700;
color: #009DA5;
text-transform: uppercase;
letter-spacing: 0.04em;
display: flex;
align-items: end;
justify-content: center;
padding-bottom: 4px;
text-align: center;
line-height: 1.1;
}
.portrait-matrix-grid .row-label {
font-family: 'Source Code Pro', monospace;
font-size: 10pt;
font-weight: 700;
color: #009DA5;
text-transform: uppercase;
letter-spacing: 0.04em;
display: flex;
align-items: center;
padding-right: 8px;
line-height: 1.15;
}
.portrait-matrix-grid .corner {
/* leere Ecke oben links */
}
.portrait-legend-row {
display: flex;
flex-wrap: wrap;
justify-content: center;
gap: 8px 18px;
font-family: 'Source Code Pro', monospace;
font-size: 10.5pt;
color: #444;
margin-top: 4px;
}
.portrait-legend-row .legend-item {
display: inline-flex;
align-items: center;
gap: 6px;
}
.portrait-legend-row .swatch {
width: 18px; height: 18px; border-radius: 2px; flex-shrink: 0;
}
.portrait-summary {
font-size: 13pt;
line-height: 1.5;
color: #333;
display: -webkit-box;
-webkit-line-clamp: 5;
-webkit-box-orient: vertical;
overflow: hidden;
border-top: 1px solid rgba(0,0,0,0.08);
padding-top: 14px;
flex-shrink: 0;
}
.portrait-summary-label {
display: block;
font-family: 'Source Code Pro', monospace;
font-size: 10pt;
text-transform: uppercase;
letter-spacing: 0.12em;
color: #009DA5;
font-weight: 700;
margin-bottom: 6px;
}
/* ── Footer ── */
.footer {
position: absolute;
bottom: {% if is_og %}12px{% elif is_portrait %}16px{% else %}10px{% endif %};
left: {% if is_og %}38px{% elif is_portrait %}38px{% else %}30px{% endif %};
right: {% if is_og %}38px{% elif is_portrait %}38px{% else %}30px{% endif %};
display: flex;
justify-content: space-between;
align-items: center;
font-family: 'Source Code Pro', monospace;
font-size: 9pt;
color: #888;
border-top: 1px solid rgba(0,0,0,0.1);
padding-top: 6px;
}
.footer .brand { color: #009DA5; font-weight: 700; letter-spacing: 0.06em; }
</style>
</head>
<body>
<div class="card">
<div class="header">
<div class="kicker">GWÖ-Bewertung · {{ bundesland }} · {{ assessment.drucksache }}</div>
<div class="meta">{{ datum }}</div>
</div>
{% if is_portrait %}
<div class="portrait-body">
<div class="portrait-title">{{ assessment.title|truncate(160, end="…") }}</div>
{% if fraktionen %}
<div class="portrait-fraktionen">
Eingebracht von
{% for f in fraktionen %}<span class="pill">{{ f }}</span>{% endfor %}
</div>
{% endif %}
<div class="portrait-score-hero">
<div class="portrait-score-num">{{ "%.1f"|format(assessment.gwoe_score) }}<span class="slash">/10</span></div>
<div class="portrait-score-side">
<div class="portrait-score-label">Gemeinwohl-Score</div>
<div class="portrait-verdict">{{ assessment.empfehlung.value }}</div>
</div>
</div>
<div class="portrait-matrix-block">
<div class="portrait-matrix-grid matrix">
{# Top row: leere Ecke + 5 Werte-Spalten-Labels #}
<div class="corner"></div>
<div class="col-label">Würde</div>
<div class="col-label">Solidari­tät</div>
<div class="col-label">Nach­haltig­keit</div>
<div class="col-label">Gerech­tigkeit</div>
<div class="col-label">Trans­parenz</div>
{# Folgende 5 Zeilen: Beruehrungsgruppen-Label + 5 Cells #}
{% set rows = [
('A', 'Lieferant:­innen'),
('B', 'Finanzen'),
('C', 'Verwal­tung'),
('D', 'Bürger:­innen'),
('E', 'Gesell­schaft & Natur'),
] %}
{% for r, r_label in rows %}
<div class="row-label">{{ r }} · {{ r_label }}</div>
{% for c in ['1','2','3','4','5'] %}
{% set cell = matrix_lookup.get(r ~ c, {}) %}
{% set rt = cell.get('rating', 0) %}
<div class="cell {% if rt >= 4 %}r-pp{% elif rt >= 1 %}r-p{% elif rt == 0 %}r-0{% elif rt <= -4 %}r-nn{% else %}r-n{% endif %}">{% if rt >= 4 %}++{% elif rt >= 1 %}+{% elif rt == 0 %}·{% elif rt <= -4 %}{% else %}{% endif %}</div>
{% endfor %}
{% endfor %}
</div>
<div class="portrait-legend-row">
<span class="legend-item"><span class="swatch" style="background:#889E33;"></span>++ stark fördernd</span>
<span class="legend-item"><span class="swatch" style="background:#cddaa1;"></span>+ fördernd</span>
<span class="legend-item"><span class="swatch" style="background:#d8d8d2;"></span>○ neutral</span>
<span class="legend-item"><span class="swatch" style="background:#efc9c3;"></span> widersprechend</span>
<span class="legend-item"><span class="swatch" style="background:#9A2A2A;"></span> stark widerspr.</span>
</div>
</div>
<div class="portrait-summary">
<span class="portrait-summary-label">Begründung</span>
{{ assessment.gwoe_begruendung|truncate(440, end="…") }}
</div>
</div>
{% else %}
<div class="body-grid">
<div class="left-col">
<h1>{{ assessment.title|truncate(100, end="…") }}</h1>
{% if fraktionen %}
<div class="fraktionen">
{% for f in fraktionen %}<span class="pill">{{ f }}</span>{% endfor %}
</div>
{% endif %}
<div class="verdict">{{ assessment.empfehlung.value }}</div>
<div class="summary">{{ assessment.gwoe_begruendung|truncate(420, end="…") }}</div>
</div>
<div class="right-col">
<div class="score-big">{{ "%.1f"|format(assessment.gwoe_score) }}</div>
<div class="score-label">GWÖ-Score · 010</div>
<div class="matrix-compact matrix">
{% for r in ['A','B','C','D','E'] %}
{% for c in ['1','2','3','4','5'] %}
{% set cell = matrix_lookup.get(r ~ c, {}) %}
{% set rt = cell.get('rating', 0) %}
<div class="cell {% if rt >= 4 %}r-pp{% elif rt >= 1 %}r-p{% elif rt == 0 %}r-0{% elif rt <= -4 %}r-nn{% else %}r-n{% endif %}">{% if rt >= 4 %}++{% elif rt >= 1 %}+{% elif rt == 0 %}·{% elif rt <= -4 %}{% else %}{% endif %}</div>
{% endfor %}
{% endfor %}
</div>
</div>
</div>
{% endif %}
<div class="footer">
<span><span class="brand">gwoe.toppyr.de</span> · automatische Gemeinwohl-Bilanzierung</span>
<span>{{ assessment.drucksache }}</span>
</div>
</div>
</body>
</html>