Ein Hinweis, der nur fragt und sonst nichts ändert, ist Theater. Deshalb zwei Hälften, und die zweite ist die wichtigere. Die Frage: serverseitig gerendert, per Formular beantwortet, ohne JavaScript - die öffentlichen Seiten laufen mit script-src 'none', und ein Banner mit Skript wäre das eine, was diese Regel bricht. "Alle akzeptieren" und "Nur notwendige" sind dieselbe Art Knopf, gleich groß, nebeneinander. Die Entscheidung steht in einem eigenen Cookie: Fassung der Frage, gewährte Gruppen, Zeitpunkt. Keine Kennung, kein Serverabgleich, ein halbes Jahr. Kein CSRF-Token, absichtlich: die Anfrage ändert nichts auf dem Server, und ein Token bräuchte für jeden Besucher eine Sitzung - genau das, was ein Einwilligungshinweis vermeiden soll. Geprüft wird stattdessen das Rücksprungziel. Das Zurückhalten: der neue Block "Externer Inhalt" (Dokumentschema v14) lädt ohne Einwilligung nichts - kein iframe, keine Adresse im Markup, frame-src 'none'. Der Platzhalter nennt den Anbieter; sein Knopf gewährt genau diese Gruppe und nimmt die vorher gewährten mit. Erst danach kommt der Host in die Sicherheitsrichtlinie, und nur dieser. Im Editor bleibt es immer ein Platzhalter. Datenschutz und Impressum sind keine Einstellung mehr: fehlen sie, warnt die Verwaltung und legt sie auf Knopfdruck als Gerüst an, mit NOCH AUSZUFÜLLEN an jeder Pflichtangabe. Den Text schreibt das CMS nicht - das ist eine rechtliche Aussage über eine Firma. Verlinkt werden beide aus dem Template, nicht aus dem Fußbereich: ein Pflichtlink, den man löschen kann, ist keiner. Dazu der Fehler, den Umut im Screenshot gesehen hat: die Wortmarke stand als Buchstabenkolonne in einem 60 Pixel breiten Kasten. .site-header war noch ein Flexcontainer aus der Zeit des handgeschriebenen Markups und quetschte die Sektion zusammen, und design.css band jede Designklasse an #main - wo Kopf- und Fußbereich seit ADR 026 nicht mehr liegen. Beides behoben. Neu im Ausbauplan: Statistik (serverseitige, wiedererkennungsfreie Zählung, die deshalb keine Einwilligung braucht), Inhalte und Ladeverhalten prüfen (Blättern überall, minimal laden, Ladebudget), Installer (Schritt für Schritt im Browser, Migration inklusive). 245 PHP-Tests / 976 Assertions unter PHP 8.2.12 und 8.4.22. Der Browsertest fand zwei Fehler von mir: grauer Text auf dunklem Fußbereich mit 2,24:1, und das fixierte Banner verdeckte den Einwilligungsknopf am Seitenende. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
163 lines
15 KiB
CSS
163 lines
15 KiB
CSS
@charset "UTF-8";
|
||
h1,h2,h3,p,a{overflow-wrap:anywhere;hyphens:auto}
|
||
.block-image{margin:0;max-width:100%}.block-image img{max-width:100%;height:auto}
|
||
:root{color-scheme:light;--paper:#f7f8f4;--ink:#182d2b;--muted:#50625d;--line:#d7dfd7;--mint:#e5f0b6;--accent:#315b49;font-family:Arial,Helvetica,sans-serif;color:var(--ink);background:var(--paper);font-synthesis:none}
|
||
*{box-sizing:border-box}body{margin:0}a{color:inherit;text-underline-offset:5px}a:hover{text-decoration-thickness:2px}:focus-visible{outline:3px solid #b85c12;outline-offset:6px}main:focus{outline:none}.shell{width:min(1180px,calc(100% - 64px));margin-inline:auto}.site-header{border-bottom:1px solid var(--line)}.brand{display:inline-flex;align-items:center;gap:5px;font-weight:800;font-size:28px;letter-spacing:-1.3px;text-decoration:none}.brand-icon{width:37px;height:37px;background:var(--ink);color:var(--mint);border-radius:10px;display:grid;place-items:center;margin-right:7px;font-size:27px;letter-spacing:-2px}.brand-light{font-weight:400}.site-header nav{display:flex;gap:32px;align-items:center;font-size:14px;font-weight:600}.site-header nav a{text-decoration:none;padding:12px 0}.site-header nav [aria-current=page]{text-decoration:underline}.site-header nav .nav-link{border:1px solid var(--ink);padding:13px 18px;border-radius:7px}.nav-link span{margin-left:18px}.block-section{padding:70px 0}.block-section:first-child{padding:74px 0 82px}.section-inner>.block-text:first-child{font-size:12px;font-weight:700;letter-spacing:2px;text-transform:uppercase;color:var(--accent);margin:0 0 30px}h1,h2,h3{letter-spacing:-.05em;line-height:1.05;margin:0 0 24px}h1{font-size:clamp(48px,6.6vw,84px);font-weight:500;max-width:900px}h2{font-size:clamp(32px,4vw,49px);font-weight:500;max-width:760px}h3{font-size:26px;letter-spacing:-.035em;line-height:1.2}.block-text{font-size:18px;line-height:1.7;max-width:690px;margin:0 0 23px;color:var(--muted);white-space:pre-line}.button{display:inline-flex;align-items:center;gap:32px;min-height:50px;margin-top:9px;padding:15px 21px;background:var(--ink);color:var(--paper);border-radius:7px;text-decoration:none;font-size:14px;font-weight:600}.button:hover{background:#294b41}.block-row{display:grid;grid-template-columns:repeat(auto-fit,minmax(min(250px,100%),1fr));gap:22px;margin-top:38px;background:transparent!important}.block-column{border:1px solid var(--line);border-radius:12px;padding:30px;min-width:0}.block-column .block-text{font-size:15px}.block-column>.block-text:first-child{font-size:12px;font-weight:700;letter-spacing:1px;color:var(--accent);margin-bottom:34px}.tone-mint{background:var(--mint)}.tone-ink{background:var(--ink);color:var(--paper)}.tone-ink .block-text{color:#dae3da}.tone-ink .section-inner>.block-text:first-child{color:var(--mint)}.tone-ink .button{color:var(--ink);background:var(--mint)}.width-narrow>.section-inner{max-width:800px}.site-footer{border-top:1px solid var(--line)}.site-footer p,.site-footer>span{font-size:12px;color:var(--muted)}.site-footer .brand{font-size:22px}.skip-link{position:fixed;left:16px;top:-100px;z-index:10;background:var(--ink);color:white;padding:16px}.skip-link:focus{top:16px}img{display:block;max-width:100%;height:auto;border-radius:12px}.error-page{padding-block:100px}.error-page h1{max-width:800px}
|
||
@media(max-width:650px){.shell{width:calc(100% - 36px)}.site-header nav{width:100%;justify-content:space-between;gap:12px;font-size:12px}.site-header nav .nav-link{padding:10px}.nav-link span{margin-left:6px}.block-section,.block-section:first-child{padding-block:48px}h1{font-size:49px}.block-text{font-size:16px}.block-column{padding:26px}.site-footer{align-items:flex-start;flex-direction:column;gap:10px}.site-footer p{margin:0}.block-row{gap:16px}}
|
||
|
||
/* Menus. Submenus open with details/summary, so the navigation works without JavaScript. */
|
||
.menu-list{list-style:none;display:flex;flex-wrap:wrap;align-items:center;gap:4px 22px;margin:0;padding:0}
|
||
.menu-list a{text-decoration:none;padding:6px 2px;display:inline-block}
|
||
.menu-list a:hover,.menu-list summary:hover{text-decoration:underline}
|
||
.menu-list [aria-current=page]{font-weight:700;text-decoration:underline;text-underline-offset:6px}
|
||
.menu-branch>summary{cursor:pointer;padding:6px 2px;list-style:none}
|
||
.menu-branch>summary::after{content:' ▾'}
|
||
.menu-branch[open]>summary::after{content:' ▴'}
|
||
.menu-branch-body{display:grid;gap:2px;padding:6px 0 6px 14px}
|
||
.menu-level-2{flex-direction:column;align-items:flex-start;gap:2px}
|
||
|
||
/* Posts */
|
||
.post-list{list-style:none;margin:0;padding:0;display:grid;gap:26px;grid-template-columns:repeat(auto-fill,minmax(300px,1fr))}
|
||
.post-card{display:flex;flex-direction:column;gap:14px}
|
||
.post-image img{width:100%;height:210px;object-fit:cover;border-radius:10px;display:block}
|
||
.post-card h3{margin:0;font-size:19px;line-height:1.35}
|
||
.post-card h3 a{text-decoration:none}
|
||
.post-card h3 a:hover{text-decoration:underline}
|
||
.post-meta,.post-header{display:flex;flex-wrap:wrap;gap:6px 16px;align-items:baseline;font-size:13px;margin:0}
|
||
.post-header{padding-block:22px 0}
|
||
.post-excerpt{margin:0;line-height:1.65}
|
||
.post-terms{display:flex;flex-wrap:wrap;gap:8px;margin:0;font-size:13px}
|
||
.post-empty{padding:26px;border:1px dashed currentColor;border-radius:10px}
|
||
.pagination{display:flex;flex-wrap:wrap;gap:12px 24px;align-items:center;margin-top:38px;padding-top:22px;border-top:1px solid currentColor;font-size:14px}
|
||
.post-terms-label{font-size:11px;font-weight:700;letter-spacing:1.6px;text-transform:uppercase;margin-bottom:10px}
|
||
|
||
/* Blocks introduced with schema v6. No JavaScript: the accordion is details/summary. */
|
||
.block-icon{flex-shrink:0;display:block}
|
||
.block-iconcard{display:grid;gap:12px;padding:26px;border:1px solid currentColor;border-radius:12px}
|
||
.block-iconcard h3{margin:0;font-size:19px;line-height:1.3}
|
||
.block-iconcard h3 a{text-decoration:none}
|
||
.block-iconcard h3 a:hover{text-decoration:underline}
|
||
.block-iconcard p{margin:0;line-height:1.65}
|
||
.block-stat{display:grid;gap:4px;margin:0}
|
||
.block-stat strong{font-size:44px;font-weight:600;line-height:1.05;letter-spacing:-1.5px}
|
||
.block-stat span{font-size:14px;line-height:1.5}
|
||
.block-quote{margin:0;display:grid;gap:14px;padding:28px;border:1px solid currentColor;border-radius:12px}
|
||
.block-quote blockquote{margin:0}
|
||
.block-quote blockquote p{margin:0;font-size:18px;line-height:1.65}
|
||
.block-quote figcaption{font-size:14px;font-weight:600}
|
||
.block-quote figcaption span{font-weight:400;opacity:.75}
|
||
.quote-rating{margin:0;letter-spacing:3px}
|
||
.block-logos{list-style:none;margin:0;padding:0;display:grid;grid-template-columns:repeat(auto-fit,minmax(120px,1fr));align-items:center;gap:24px}
|
||
.block-logos img{max-height:52px;width:auto;margin-inline:auto;display:block}
|
||
.block-cta{display:flex;flex-wrap:wrap;gap:20px 34px;align-items:center;justify-content:space-between;padding:32px;border:1px solid currentColor;border-radius:14px}
|
||
.block-cta h2{margin:0 0 6px;font-size:26px;letter-spacing:-.6px}
|
||
.block-cta p{margin:0;line-height:1.6}
|
||
.block-contact{display:grid;gap:14px;font-style:normal}
|
||
.block-contact span{display:flex;align-items:center;gap:12px;line-height:1.5}
|
||
.block-divider{border:0;border-top:1px solid currentColor;opacity:.35;margin:0}
|
||
.block-spacer{display:block}
|
||
.spacer-small{height:18px}.spacer-medium{height:38px}.spacer-large{height:68px}.spacer-huge{height:110px}
|
||
.block-accordion{display:grid;gap:10px}
|
||
.block-accordionitem{border:1px solid currentColor;border-radius:10px;padding:4px 18px}
|
||
.block-accordionitem>summary{cursor:pointer;padding:15px 0;font-weight:600;list-style:none;display:flex;justify-content:space-between;gap:18px;align-items:center}
|
||
.block-accordionitem>summary::after{content:'+';font-size:22px;line-height:1}
|
||
.block-accordionitem[open]>summary::after{content:'–'}
|
||
.accordion-body{padding-bottom:16px}
|
||
|
||
/* Tabs. Rendered as an anchor list plus all panels; tabs.js upgrades them to a tab widget. */
|
||
.tabs-nav{list-style:none;display:flex;flex-wrap:wrap;gap:4px;margin:0 0 22px;padding:0;border-bottom:1px solid currentColor}
|
||
.tabs-nav a{display:inline-block;padding:12px 18px;text-decoration:none;border:1px solid transparent;border-bottom:0;border-radius:9px 9px 0 0;margin-bottom:-1px;font-weight:600}
|
||
.tabs-nav a:hover{background:#0000000a}
|
||
.tabs-nav [aria-selected=true]{border-color:currentColor;background:#fff}
|
||
.tabs-ready .tabs-nav a{cursor:pointer}
|
||
.block-tabitem{padding-block:4px}
|
||
.block-tabitem:focus-visible{outline:3px solid #b55b12;outline-offset:4px}
|
||
.tabs-heading{margin:0 0 12px;font-size:17px}
|
||
|
||
/* Forms. Server rendered, no JavaScript; every message sits at its field. */
|
||
.block-form{display:grid;gap:20px;max-width:640px}
|
||
.form-field{display:grid;gap:7px}
|
||
.form-field label{font-weight:600;font-size:14px}
|
||
.form-field input,.form-field textarea,.form-field select{width:100%;padding:12px 13px;border:1px solid currentColor;border-radius:8px;background:#fff;color:inherit;font:inherit;min-height:46px}
|
||
.form-field textarea{min-height:140px;line-height:1.6;resize:vertical}
|
||
.has-error input,.has-error textarea,.has-error select{border-width:2px;border-color:#8b2f22}
|
||
.field-error{margin:0;font-size:13px;font-weight:600;color:#8b2f22}
|
||
.field-note{margin:0;font-size:13px;opacity:.8}
|
||
.form-error-summary{margin:0;padding:14px 18px;border-radius:8px;background:#fff0eb;color:#8b2f22;border:1px solid #e2b2a7;font-weight:600}
|
||
.form-success{margin:0;padding:16px 20px;border-radius:8px;background:#edf3dc;color:#315b49;border:1px solid #c5d89a;font-weight:600}
|
||
.form-missing{margin:0;padding:14px 18px;border:1px dashed currentColor;border-radius:8px}
|
||
.check-row{display:flex;align-items:flex-start;gap:11px}
|
||
.check-row input{width:20px;min-height:20px;flex-shrink:0;margin-top:3px}
|
||
.check-row label{font-weight:400;line-height:1.55}
|
||
.required-mark{color:#8b2f22}
|
||
/* Off screen rather than display:none so a filled value still proves automation. */
|
||
.form-trap{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}
|
||
|
||
.block-breadcrumb ol{list-style:none;display:flex;flex-wrap:wrap;gap:6px 10px;margin:0;padding:0;font-size:13px}
|
||
.block-breadcrumb li+li::before{content:'›';margin-right:10px;opacity:.6}
|
||
.block-breadcrumb a{text-decoration:none}
|
||
.block-breadcrumb a:hover{text-decoration:underline}
|
||
|
||
/* The column rules above were written for a flat section > text structure and set a colour meant
|
||
for light backgrounds. Inside a dark tone they won on specificity, so text in a dark column was
|
||
unreadable - 1.87:1 in the seeded footer. These two selectors match that specificity and come
|
||
later, so the dark tone wins where it has to. */
|
||
.tone-ink .block-column .block-text,
|
||
.tone-ink .block-column>.block-text:first-child{color:#dae3da}
|
||
|
||
/* The consent question. Fixed to the bottom, but it never covers the whole page: the content
|
||
stays readable while the decision is open, because a wall would make "accept" the only way
|
||
forward. Accepting and refusing are the same button in the same row. */
|
||
.consent-bar{position:fixed;inset-inline:12px;bottom:12px;z-index:50;max-width:620px;margin-inline:auto;background:#fff;color:#203b34;border:1px solid #cfd8d1;border-radius:14px;padding:20px 22px;box-shadow:0 18px 50px #1b2a2426;font-size:14px;line-height:1.6}
|
||
.consent-bar h2{font-size:16px;margin:0 0 8px}
|
||
.consent-bar p{margin:0 0 14px}
|
||
.consent-actions{display:flex;flex-wrap:wrap;gap:10px}
|
||
.consent-button{display:inline-flex;align-items:center;justify-content:center;min-height:44px;padding:11px 18px;border-radius:8px;border:1px solid #2c6b4f;background:#2c6b4f;color:#fff;font:inherit;font-weight:600;cursor:pointer}
|
||
.consent-actions .consent-button:last-child{background:#fff;color:#1b2a24;border-color:#b3c0b7}
|
||
.consent-button:hover{filter:brightness(.94)}
|
||
.consent-button:focus-visible,.consent-bar a:focus-visible,.consent-bar summary:focus-visible,.consent-bar input:focus-visible{outline:3px solid #b55b12;outline-offset:2px}
|
||
.consent-details{margin-top:14px}
|
||
.consent-details>summary{cursor:pointer;font-size:13px;font-weight:600;padding:4px 0}
|
||
.consent-groups{list-style:none;margin:10px 0 0;padding:0;display:grid;gap:10px}
|
||
.consent-groups label{display:flex;gap:10px;align-items:flex-start;font-size:13px;line-height:1.5}
|
||
.consent-groups input{margin-top:3px;width:18px;height:18px;flex-shrink:0}
|
||
.consent-choice{margin-top:14px;background:#fff;color:#1b2a24;border-color:#b3c0b7}
|
||
.consent-links{margin:14px 0 0;font-size:12px}
|
||
.consent-links a{color:#a4510f}
|
||
/* While the question is open the bar is fixed at the bottom, so the page has to end above it -
|
||
otherwise it covers the last element, and an embed's own consent button sat right underneath.
|
||
Found by the browser test, which could not click it. */
|
||
body.consent-open{padding-bottom:260px}
|
||
@media (min-width:900px){body.consent-open{padding-bottom:200px}}
|
||
.consent-page main{max-width:680px;margin-inline:auto;padding:40px 20px}
|
||
.consent-page .consent-bar{position:static;inset:auto;box-shadow:none;max-width:none;margin-top:24px}
|
||
@media (max-width:620px){.consent-bar{inset-inline:0;bottom:0;border-radius:14px 14px 0 0;padding:16px}.consent-actions .consent-button{flex:1 1 140px}}
|
||
|
||
/* An embedded third party, and what stands in its place until it may load. */
|
||
.block-embed iframe{display:block;width:100%;border:0;border-radius:10px;background:#eef1ed}
|
||
.embed-blocked{border:1px dashed #b3c0b7;border-radius:10px;padding:24px;background:#f6f7f4;text-align:center}
|
||
.embed-title{margin:0 0 8px;font-weight:600}
|
||
.embed-hint{margin:0 0 10px;font-size:14px;line-height:1.6;color:#4a5751}
|
||
.embed-provider{margin:0 0 16px;font-size:13px;color:#4a5751}
|
||
|
||
/* Header and footer hold block documents of their own (ADR 026). They are therefore ordinary
|
||
block containers - as flex containers they shrank the section inside them until a single word
|
||
wrapped one letter per line. Their sections are chrome, not articles, so they sit tighter than
|
||
a section in the page body, and their rows keep their columns side by side. */
|
||
.site-header .block-section,
|
||
.site-footer .block-section{padding-block:20px}
|
||
.site-header .block-row,
|
||
.site-footer .block-row{margin-top:0;align-items:center}
|
||
.site-header .block-column,
|
||
.site-footer .block-column{border:0;border-radius:0;padding:0}
|
||
.site-header .block-text,
|
||
.site-footer .block-text{margin:0}
|
||
/* Size and weight only, never colour: the tone of the area decides that, and a colour here won
|
||
against .tone-ink and put 2.24:1 grey text on the dark footer. The browser check caught it. */
|
||
.site-header .block-column>.block-text:first-child{font-size:24px;font-weight:800;letter-spacing:-1px;text-transform:none;margin:0}
|
||
.site-footer .block-column>.block-text:first-child{font-size:14px;font-weight:400;letter-spacing:0;text-transform:none;margin:0}
|
||
/* One landmark for the three links that must be on every page. Loose text outside a landmark is
|
||
also what axe objects to, and rightly: a screen reader then has no way to skip to it. */
|
||
.legal-bar{padding:16px 20px;text-align:center;font-size:12px;color:var(--muted);background:#f1f3ef}
|
||
.legal-bar a{color:#a4510f}
|