/* ============================================================================
   ToffanoBlog — Design-System
   ----------------------------------------------------------------------------
   Aufbau dieser Datei:
     1. Tokens        — Farben/Schriften, hell und dunkel
     2. Bootstrap     — Komponenten, die nicht an den Tokens hängen
     3. Grundgerüst   — Seitenrahmen, Typografie, Kopf- und Fußzeile
     4. Bausteine     — Aufmacher, Beitragsliste, Archiv, Chips
     5. Inhalte       — Beitragstext, Kommentare, Lightbox, Editor

   Farbregel: Schwarz-Weiß trägt die Flächen, Orange markiert nur — Kanten,
   Links, Marker, Hover. Alles hängt an den Bootstrap-Variablen, damit die
   Standard-Klassen in den Views (btn, badge, card, text-muted) mitziehen und
   nirgends eine zweite Farbquelle entsteht.
   ========================================================================= */

/* == 1. Tokens ============================================================ */

:root {
    /* Eigene Tokens für alles, was Bootstrap nicht kennt */
    --tf-accent: #c2541a;          /* frisches Orange */
    --tf-accent-strong: #98400f;
    --tf-accent-wash: #fdf3ec;     /* Hinweisbox-Fläche */
    --tf-line-soft: #ededea;       /* Trennlinie innerhalb einer Liste */
    --tf-serif: Georgia, "Iowan Old Style", "Palatino Linotype", "Times New Roman", serif;
    --tf-mono: ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, monospace;

    /* Obergrenze für ein Bild im Beitragstext. Über die volle Spaltenbreite gezogen drängt ein
       Foto den Text an den Rand; mit diesen beiden Grenzen bleibt es eine Abbildung IM Text.
       Als Token, weil zwei Regeln sie brauchen — das einzelne Bild und das Aufmacherbild einer
       Galerie —, und weil genau das die Stellschraube ist, an der man drehen will.
       Zum Vergleich: das Titelbild eines Beitrags (.post-hero) ist 20rem hoch. */
    --tf-content-image-max-w: 34rem;
    --tf-content-image-max-h: 22rem;

    /* Syntax-Highlighting (highlight.js, siehe .blog-content .hljs-* unten): nur zwei neue
       Farben. Kommentar und Schlüsselwort brauchen keine eigene — Kommentare bekommen das
       vorhandene Sekundärgrau, Schlüsselwörter bleiben Tinte mit mehr Gewicht. Oliv und
       Terrakotta sind gedeckt genug, um Text nicht zu überstrahlen, und liegen in derselben
       warmen Familie wie der Akzent statt Bootstraps Blau/Grün/Lila zu importieren. */
    --tf-code-string: #5f6b35;   /* Strings, Zahlen, Regex, Entities */
    --tf-code-name: #9c5426;     /* Bezeichner, Typen, Tags, Selektoren, Attribute */

    /* Bootstrap-Tokens: neutrales Warmgrau statt Blaugrau */
    --bs-body-bg: #ffffff;
    --bs-body-bg-rgb: 255, 255, 255;
    --bs-body-color: #131312;
    --bs-body-color-rgb: 19, 19, 18;
    --bs-emphasis-color: #000000;
    --bs-secondary-color: #55544e;
    --bs-secondary-bg: #eeeeeb;
    --bs-tertiary-bg: #f6f6f4;
    --bs-border-color: #e2e2de;
    --bs-border-color-translucent: rgba(0, 0, 0, 0.12);
    /* Ohne diesen Wert färbt Bootstrap jedes <code> in seinem Pink (#d63384) — eine Farbe,
       die es im System nirgends gibt. Betraf alles außerhalb .blog-content, etwa die
       Hinweistexte im Editor. */
    --bs-code-color: var(--tf-accent);

    /* Akzent. primary ist bewusst das Orange — Flächen bleiben trotzdem
       schwarz-weiß, weil die Buttons unten eigene Werte bekommen. */
    --bs-primary: #c2541a;
    --bs-primary-rgb: 194, 84, 26;
    --bs-link-color: #c2541a;
    --bs-link-color-rgb: 194, 84, 26;
    --bs-link-hover-color: #98400f;
    --bs-link-hover-color-rgb: 152, 64, 15;
    --bs-focus-ring-color: rgba(194, 84, 26, 0.25);
    --bs-highlight-bg: #fdf3ec;
}

[data-bs-theme=dark] {
    --tf-accent: #ff9d5c;
    --tf-accent-strong: #ffb583;
    --tf-accent-wash: #261a10;
    --tf-line-soft: #202020;

    --tf-code-string: #b7c07e;
    --tf-code-name: #f0a978;

    --bs-body-bg: #0c0c0b;
    --bs-body-bg-rgb: 12, 12, 11;
    --bs-body-color: #eceae6;
    --bs-body-color-rgb: 236, 234, 230;
    --bs-emphasis-color: #ffffff;
    --bs-secondary-color: #b2b0aa;
    --bs-secondary-bg: #1e1e1c;
    --bs-tertiary-bg: #151514;
    --bs-border-color: #2b2b28;
    --bs-border-color-translucent: rgba(255, 255, 255, 0.12);
    --bs-code-color: var(--tf-accent);

    --bs-primary: #ff9d5c;
    --bs-primary-rgb: 255, 157, 92;
    --bs-link-color: #ff9d5c;
    --bs-link-color-rgb: 255, 157, 92;
    --bs-link-hover-color: #ffb583;
    --bs-link-hover-color-rgb: 255, 181, 131;
    --bs-focus-ring-color: rgba(255, 157, 92, 0.3);
    --bs-highlight-bg: #261a10;
}

/* == 2. Bootstrap-Komponenten mit fest verdrahteten Farben ================= */

/* Primärfläche ist Tinte, nicht Orange — Orange kommt erst beim Hover */
.btn-primary {
    --bs-btn-color: var(--bs-body-bg);
    --bs-btn-bg: var(--bs-body-color);
    --bs-btn-border-color: var(--bs-body-color);
    --bs-btn-hover-color: var(--bs-body-bg);
    --bs-btn-hover-bg: var(--tf-accent);
    --bs-btn-hover-border-color: var(--tf-accent);
    --bs-btn-active-color: var(--bs-body-bg);
    --bs-btn-active-bg: var(--tf-accent-strong);
    --bs-btn-active-border-color: var(--tf-accent-strong);
    --bs-btn-disabled-color: var(--bs-body-bg);
    --bs-btn-disabled-bg: var(--bs-secondary-color);
    --bs-btn-disabled-border-color: var(--bs-secondary-color);
}

.btn-outline-primary {
    --bs-btn-color: var(--tf-accent);
    --bs-btn-border-color: var(--bs-border-color);
    --bs-btn-hover-color: var(--bs-body-bg);
    --bs-btn-hover-bg: var(--tf-accent);
    --bs-btn-hover-border-color: var(--tf-accent);
    --bs-btn-active-color: var(--bs-body-bg);
    --bs-btn-active-bg: var(--tf-accent-strong);
    --bs-btn-active-border-color: var(--tf-accent-strong);
    --bs-btn-disabled-color: var(--bs-secondary-color);
    --bs-btn-disabled-border-color: var(--bs-border-color);
}

.btn-outline-secondary {
    --bs-btn-color: var(--bs-secondary-color);
    --bs-btn-border-color: var(--bs-border-color);
    --bs-btn-hover-color: var(--bs-body-color);
    --bs-btn-hover-bg: var(--bs-tertiary-bg);
    --bs-btn-hover-border-color: var(--bs-secondary-color);
    --bs-btn-active-color: var(--bs-body-color);
    --bs-btn-active-bg: var(--bs-secondary-bg);
    --bs-btn-active-border-color: var(--bs-secondary-color);
}

.btn-secondary {
    --bs-btn-color: var(--bs-body-color);
    --bs-btn-bg: var(--bs-secondary-bg);
    --bs-btn-border-color: var(--bs-border-color);
    --bs-btn-hover-color: var(--bs-body-color);
    --bs-btn-hover-bg: var(--bs-tertiary-bg);
    --bs-btn-hover-border-color: var(--bs-secondary-color);
}

/* Paginierung: aktive Seite als Tintenfläche, Rest als ruhige Kästchen */
.pagination {
    --bs-pagination-color: var(--bs-secondary-color);
    --bs-pagination-bg: transparent;
    --bs-pagination-border-color: var(--bs-border-color);
    --bs-pagination-hover-color: var(--tf-accent);
    --bs-pagination-hover-bg: var(--bs-tertiary-bg);
    --bs-pagination-hover-border-color: var(--tf-accent);
    --bs-pagination-focus-color: var(--tf-accent);
    --bs-pagination-focus-bg: var(--bs-tertiary-bg);
    --bs-pagination-active-color: var(--bs-body-bg);
    --bs-pagination-active-bg: var(--bs-body-color);
    --bs-pagination-active-border-color: var(--bs-body-color);
    --bs-pagination-disabled-color: var(--bs-secondary-color);
    --bs-pagination-disabled-bg: transparent;
    --bs-pagination-disabled-border-color: var(--bs-border-color);
    --bs-pagination-font-size: 0.875rem;
}

.page-link {
    font-family: var(--tf-mono);
}

/* Formularfelder: Bootstrap setzt hier ein hartes Blau */
.form-control:focus,
.form-select:focus,
.form-check-input:focus {
    border-color: var(--tf-accent);
    box-shadow: 0 0 0 0.2rem var(--bs-focus-ring-color);
}

.form-check-input:checked {
    background-color: var(--bs-body-color);
    border-color: var(--bs-body-color);
}

/* Bootstrap dimmt das Label einer disabled-Checkbox auf opacity .5 — im Consent-Modal trägt
   genau dieses Label die Erklärung, was essenzielle Cookies tun, und landet damit bei ~2,3:1.
   Das Kästchen selbst bleibt Bootstraps Disabled-Signal; nur der Text rückt auf lesbaren
   Kontrast. */
.form-check-input:disabled ~ .form-check-label {
    opacity: .75;
}

/* Im Dark Mode ist die angehakte Fläche oben die HELLE Tinte (--bs-body-color), Bootstraps
   Häkchen/Punkt bleibt aber fest weiß (stroke='%23fff' im data-URI) — weiß auf hell ist
   praktisch unsichtbar. Nur das SVG dunkel umfärben (Wert = --bs-body-bg dark, #0c0c0b;
   ein var() funktioniert innerhalb eines data-URI nicht, daher ausnahmsweise der Hex-Wert
   mit dieser Herkunftsangabe). Radio und indeterminate haben dasselbe Problem. */
[data-bs-theme=dark] .form-check-input:checked[type=checkbox] {
    --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%230c0c0b' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e");
}

[data-bs-theme=dark] .form-check-input:checked[type=radio] {
    --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='2' fill='%230c0c0b'/%3e%3c/svg%3e");
}

[data-bs-theme=dark] .form-check-input[type=checkbox]:indeterminate {
    --bs-form-check-bg-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%230c0c0b' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='M6 10h8'/%3e%3c/svg%3e");
}

.form-switch .form-check-input:checked {
    background-color: var(--tf-accent);
    border-color: var(--tf-accent);
}

/* Tabs (Editor: Bearbeiten/Vorschau) */
.nav-tabs {
    --bs-nav-tabs-link-active-color: var(--bs-emphasis-color);
    --bs-nav-link-color: var(--bs-secondary-color);
    --bs-nav-link-hover-color: var(--bs-body-color);
    --bs-nav-tabs-border-color: var(--bs-border-color);
}

.table {
    --bs-table-hover-bg: var(--bs-tertiary-bg);
    --bs-table-border-color: var(--bs-border-color);
}

.dropdown-menu {
    --bs-dropdown-border-color: var(--bs-border-color);
    --bs-dropdown-link-active-bg: var(--bs-tertiary-bg);
    --bs-dropdown-link-active-color: var(--tf-accent);
    --bs-dropdown-font-size: 0.9375rem;
}

.card {
    --bs-card-border-color: var(--bs-border-color);
}

.alert {
    --bs-alert-border-color: var(--bs-border-color);
}

/* Ein Fokusring für alles, in der Akzentfarbe statt im Bootstrap-Blau.
   .embed-consent-btn steht mit drin, weil er ein roher <button> ohne .btn-Klasse ist —
   ohne den Eintrag bekäme ausgerechnet der Embed-Platzhalter als einziges interaktives
   Element nur den nativen Browser-Outline. */
.btn:focus-visible,
.page-link:focus-visible,
.embed-consent-btn:focus-visible,
.settings-card-toggle:focus-visible,
a:focus-visible {
    outline: 2px solid var(--tf-accent);
    outline-offset: 2px;
    box-shadow: none;
}

/* Einklappbare Einstellungs-Karten (Views/Admin/Settings.cshtml, E-Mail-Texte und
   Werbeplatzhalter): der Kartenkopf ist selbst der Collapse-Button und füllt die ganze
   Kopfzeile — Optik wie ein normaler .card-header (dessen Padding übernimmt der Button, der
   Header trägt p-0), plus Chevron als Zustandsanzeige. Symbolzeichen, kein Emoji — nimmt
   currentColor an. Roher <button> ohne .btn-Klasse, deshalb oben im Fokusring aufgeführt. */
.settings-card-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    width: 100%;
    padding: 0.5rem 1rem;
    border: 0;
    background: none;
    color: inherit;
    font: inherit;
    text-align: left;
    cursor: pointer;
}

.settings-card-toggle::after {
    content: "▾";
    color: var(--bs-secondary-color);
}

.settings-card-toggle.collapsed::after {
    content: "▸";
}

/* == 3. Grundgerüst ======================================================= */

html {
    font-size: 15px;
    position: relative;
    min-height: 100%;
}

@media (min-width: 768px) {
    html {
        font-size: 16px;
    }
}

body {
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    -webkit-font-smoothing: antialiased;
    -moz-osx-font-smoothing: grayscale;
}

main.container {
    flex: 1;
}

/* Überschriften in Serif — der Gegenpol zu den Mono-Metadaten */
h1, h2, h3, h4,
.h1, .h2, .h3, .h4 {
    font-family: var(--tf-serif);
    letter-spacing: -0.01em;
}

h1, .h1 {
    letter-spacing: -0.02em;
    line-height: 1.2;
}

/* Kopfzeile: eine Linie statt Schatten, Wortmarke mit Mono-Zusatz */
.navbar {
    border-bottom: 1px solid var(--bs-border-color);
    background-color: var(--bs-body-bg) !important;
    padding-top: 0.65rem;
    padding-bottom: 0.65rem;
}

.navbar .navbar-brand {
    font-family: var(--tf-serif);
    font-weight: 700;
    letter-spacing: -0.015em;
    color: var(--bs-body-color);
}

.navbar .navbar-brand:hover {
    color: var(--bs-body-color);
}

.navbar .brand-mark {
    font-family: var(--tf-mono);
    font-weight: 500;
    font-size: 0.8em;
    color: var(--tf-accent);
}

.navbar .nav-link {
    color: var(--bs-secondary-color);
    border-bottom: 2px solid transparent;
    padding-bottom: 0.2rem;
}

.navbar .nav-link:hover,
.navbar .nav-link:focus {
    color: var(--bs-body-color);
    border-bottom-color: var(--bs-border-color);
}

.navbar .nav-link.active,
.navbar .nav-item.show > .nav-link {
    color: var(--bs-body-color);
    border-bottom-color: var(--tf-accent);
}

footer.border-top {
    border-color: var(--bs-border-color) !important;
    font-size: 0.875rem;
}

/* == 4. Bausteine ========================================================= */

/* Kleines Mono-Label über einem Abschnitt, mit auslaufender Linie */
.section-label {
    /* Explizit, weil die Rolle sowohl auf <div> als auch auf <h2> sitzt — Bootstrap gibt
       Überschriften sonst font-weight 500 und dieselbe Klasse sähe zweimal anders aus. */
    font-weight: 400;
    display: flex;
    align-items: center;
    gap: 0.625rem;
    margin-bottom: 1.125rem;
    font-family: var(--tf-mono);
    font-size: 0.6875rem;
    text-transform: uppercase;
    letter-spacing: 0.12em;
    color: var(--bs-secondary-color);
}

.section-label::after {
    content: "";
    flex: 1;
    height: 1px;
    background-color: var(--bs-border-color);
}

.section-label--center {
    justify-content: center;
    margin-bottom: 2.25rem;
}

.section-label--center::after {
    display: none;
}

/* Datum, Sprache, Tags: alles Mono, damit Metadaten nie mit Text konkurrieren */
.meta-date {
    font-family: var(--tf-mono);
    font-size: 0.78rem;
    color: var(--bs-secondary-color);
    white-space: nowrap;
}

/* Summenzahl über einer Verwaltungsliste (Dateien): dieselbe Mono-Rolle wie .meta-date, aber
   in Lesegröße. Eigene Klasse statt `.meta-date fs-5`, weil .meta-date für unauffällige
   Nebenangaben gedacht ist — eine Kopfzahl mit einer Größen-Utility darüberzubügeln macht aus
   der Rolle das Gegenteil ihrer selbst. --strong hebt einen Wert der Reihe über das Gewicht
   hervor, nicht über eine Farbe. */
.stat-figure {
    font-family: var(--tf-mono);
    font-size: 1.25rem;
    color: var(--bs-secondary-color);
    white-space: nowrap;
}

.stat-figure--strong {
    font-weight: 700;
}

/* Säulendiagramm "Aufrufe pro Tag" (Views/Admin/Stats.cshtml). Aufbau je Spalte: Zahl, Spur
   fester Höhe mit der Säule darin, Datum. Die feste Höhe liegt auf der SPUR und nicht auf dem
   ganzen Diagramm — vorher war das Diagramm 200px hoch und die Säule allein bis zu 180px, die
   Zahl darüber lief damit aus dem Kasten heraus und wurde abgeschnitten (overflow-x setzt
   overflow-y implizit mit auf auto). So bleibt oben und unten immer genau eine Zeile Platz,
   unabhängig davon, wie hoch die höchste Säule ausfällt.
   Die Beschriftungen tragen .meta-date (Mono, nowrap — ein Datum soll nicht umbrechen); hier
   steht nur die kleinere Schriftgröße, weil eine Achsenbeschriftung leiser sein darf als eine
   Nebenangabe im Fließtext. Der Akzent ist an dieser Stelle ausdrücklich erlaubt: die Säulen
   sind eine der wenigen Stellen, an denen Farbe eine Aussage trägt. */
.stat-chart {
    display: flex;
    align-items: flex-end;
    gap: 0.25rem;
    overflow-x: auto;
}

.stat-chart__col {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: 2rem;
}

.stat-chart__track {
    display: flex;
    align-items: flex-end;
    height: 11rem;
}

.stat-chart__bar {
    width: 1.25rem;
    background-color: var(--tf-accent);
    border-radius: 0.25rem 0.25rem 0 0;
}

.stat-chart__label {
    font-size: 0.65rem;
}

/* Dateiname in einer Tabellenzelle: Mono wie jede technische Angabe, aber im Gegensatz zu
   .meta-date umbrechend. Ein Dateiname ist beliebig lang, und ein nicht umbrechender Name
   zieht genau die Tabellenspalte breit, die auf dem Telefon ohnehin schon knapp ist. */
.meta-filename {
    font-family: var(--tf-mono);
    font-size: 0.78rem;
    color: var(--bs-secondary-color);
    overflow-wrap: anywhere;
}

/* Datei-Auswahl im "Neue Version hochladen"-Formular (Views/Admin/EditFile.cshtml) */
.file-replace-input {
    max-width: 24rem;
}

/* Sortierbarer Spaltenkopf (Views/Blog/Manage.cshtml): der ganze Kopf ist der Link, aber er
   sieht keinen Millimeter danach aus — eine unterstrichene Kopfzeile läse sich wie fünf Links
   nebeneinander. Welche Spalte sortiert, sagt der Pfeil in der Akzentfarbe; der Kopftext
   selbst wird nur dunkler. Orange markiert, es färbt nicht. */
.table-sort {
    color: inherit;
    text-decoration: none;
    white-space: nowrap;
}

.table-sort:hover,
.table-sort:focus-visible {
    color: var(--tf-accent);
}

.table-sort--active {
    color: var(--bs-emphasis-color);
}

.table-sort__arrow {
    margin-left: 0.15rem;
    font-size: 0.72em;
    color: var(--tf-accent);
}

.meta-chip {
    display: inline-block;
    padding: 0.1rem 0.45rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 3px;
    background-color: var(--bs-body-bg);
    font-family: var(--tf-mono);
    font-size: 0.7rem;
    line-height: 1.5;
    color: var(--bs-secondary-color);
    text-decoration: none;
}

a.meta-chip:hover,
a.meta-chip:focus {
    border-color: var(--tf-accent);
    color: var(--tf-accent);
    text-decoration: none;
}

/* Verwendungsnachweis für Bilder und Dateien (Views/Shared/_MediaUsageList.cshtml) — dieselbe
   Liste in Bilder- und Datei-Verwaltung (Betreibervorgabe: beide Medienarten funktionieren
   gleich). Eigene <li> je Fundort statt kommagetrennt in einem Absatz, damit Chip und
   Beitragstitel auf schmalen Bildschirmen sauber untereinander fallen statt die Zeile zu
   sprengen — genau der Fall, den die Datei-Tabelle (eigene Spalte hätte dort kippen können)
   vermeiden soll. */
.media-usage-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.media-usage-list li {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.4rem;
    font-size: 0.875rem;
}

/* Bilder-Verwaltung (Views/Admin/Images.cshtml): eigene Kachel statt Bootstraps .card mit
   Inline-Stilen für die feste Vorschauhöhe — Vorschaubild, Titel, Verwendungsnachweis und
   Metadaten stehen jetzt in einem Block, damit "wo wird das verwendet" ohne Extra-Klick zu
   sehen ist. */
.image-tile {
    display: flex;
    flex-direction: column;
    height: 100%;
    border: 1px solid var(--bs-border-color);
    border-radius: 5px;
    overflow: hidden;
    background-color: var(--bs-body-bg);
}

.image-tile__thumb {
    display: block;
    aspect-ratio: 4 / 3;
    overflow: hidden;
}

.image-tile__thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.image-tile__body {
    display: flex;
    flex: 1;
    flex-direction: column;
    gap: 0.4rem;
    padding: 0.65rem 0.75rem 0.75rem;
}

.image-tile__title {
    margin: 0;
    font-weight: 500;
    overflow-wrap: anywhere;
}

/* margin-top: auto schiebt die Knöpfe an den unteren Rand der Kachel, auch wenn eine Nachbar-
   kachel durch mehrere Verwendungszeilen höher wird (row-cols-Raster gleicht Reihenhöhen an). */
.image-tile__actions {
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-top: auto;
    padding-top: 0.4rem;
}

/* Aufmacher — der prominente Beitrag */
.feature-post .feature-shot {
    display: block;
    position: relative;
    overflow: hidden;
    width: 100%;
    aspect-ratio: 16 / 9;
    margin-bottom: 1.25rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 6px;
    background: linear-gradient(135deg, var(--bs-secondary-bg), var(--bs-tertiary-bg));
}

.feature-post .feature-shot img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Der orange Balken ist der einzige Farbakzent am Aufmacher */
.feature-post .feature-shot::after {
    content: "";
    position: absolute;
    left: 0;
    top: 0;
    bottom: 0;
    width: 4px;
    background-color: var(--tf-accent);
}

.feature-post .feature-monogram {
    position: absolute;
    inset: 0;
    display: grid;
    place-items: center;
    font-family: var(--tf-serif);
    font-weight: 700;
    font-size: 4rem;
    color: var(--bs-secondary-color);
    opacity: 0.35;
}

.feature-post h1 {
    font-size: clamp(1.6rem, 1.1rem + 1.8vw, 2.1rem);
    margin-bottom: 0.6rem;
}

.feature-post h1 a {
    color: var(--bs-body-color);
    text-decoration: none;
}

.feature-post h1 a:hover {
    color: var(--tf-accent);
}

.feature-post .feature-lead {
    color: var(--bs-secondary-color);
    font-size: 1.02rem;
}

/* Liste der letzten Beiträge — Hover schiebt die Zeile an einer Akzentkante an */
.recent-list {
    list-style: none;
    margin: 0;
    padding: 0;
}

.recent-list li + li {
    border-top: 1px solid var(--tf-line-soft);
}

.recent-item {
    display: flex;
    gap: 0.75rem;
    align-items: flex-start;
    padding: 0.8rem 0;
    border-left: 2px solid transparent;
    text-decoration: none;
    transition: padding-left 0.12s ease, border-left-color 0.12s ease;
}

.recent-thumb {
    flex: 0 0 3.5rem;
    width: 3.5rem;
    height: 3.5rem;
    overflow: hidden;
    border: 1px solid var(--bs-border-color);
    border-radius: 4px;
}

.recent-thumb img {
    display: block;
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.recent-thumb .img-placeholder {
    font-size: 1.4rem;
}

/* min-width: 0 lässt lange Titel umbrechen, statt das Flex-Element zu sprengen */
.recent-body {
    min-width: 0;
}

.recent-meta {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.35rem;
    margin-top: 0.3rem;
}

.recent-item:hover,
.recent-item:focus {
    border-left-color: var(--tf-accent);
    padding-left: 0.625rem;
    text-decoration: none;
}

.recent-item .recent-title {
    display: block;
    color: var(--bs-body-color);
    font-weight: 500;
    line-height: 1.4;
}

.recent-item:hover .recent-title,
.recent-item:focus .recent-title {
    color: var(--tf-accent);
}

/* Archiv — Changelog-Liste statt Kartenstapel */
.post-archive {
    max-width: 48rem;
    margin: 0 auto;
}

.archive-year {
    display: flex;
    justify-content: space-between;
    padding: 1.4rem 0 0.5rem;
    border-bottom: 1px solid var(--bs-border-color);
    font-family: var(--tf-mono);
    font-size: 0.75rem;
    letter-spacing: 0.1em;
    color: var(--bs-secondary-color);
}

.archive-year:first-child {
    padding-top: 0;
}

.archive-entry {
    display: grid;
    grid-template-columns: 4.75rem 1fr auto;
    gap: 1rem;
    align-items: baseline;
    padding: 0.85rem 0.35rem;
    border-bottom: 1px solid var(--tf-line-soft);
    text-decoration: none;
}

.archive-entry:hover,
.archive-entry:focus {
    background-color: var(--bs-tertiary-bg);
    text-decoration: none;
}

.archive-entry .archive-title {
    color: var(--bs-body-color);
    font-weight: 500;
}

.archive-entry:hover .archive-title,
.archive-entry:focus .archive-title {
    color: var(--tf-accent);
}

.archive-entry .archive-tags {
    display: flex;
    gap: 0.35rem;
}

@media (max-width: 575.98px) {
    .archive-entry {
        grid-template-columns: 4rem 1fr;
    }

    .archive-entry .archive-tags {
        display: none;
    }
}

/* Platzhalter, wenn ein Beitrag kein Bild hat — füllt den umgebenden Rahmen
   (.post-list-thumb gibt die Größe vor, nicht mehr die Klasse selbst) */
.img-placeholder {
    width: 100%;
    height: 100%;
    background-color: var(--bs-tertiary-bg);
    display: flex;
    align-items: center;
    justify-content: center;
    font-family: var(--tf-serif);
    font-weight: 700;
    font-size: 2rem;
    color: var(--bs-secondary-color);
    opacity: 0.6;
}

/* == 5. Inhalte =========================================================== */

/* Zeilenlänge begrenzt, nicht die Spalte: auf einem breiten Fenster wird die Beitragsspalte
   sonst so breit, dass das Auge am Zeilenende die nächste Zeile nicht mehr sicher findet.
   68ch liegt in der üblichen Spanne von 65–75 Zeichen und wächst automatisch mit der
   Schriftgröße mit, anders als eine feste Pixelbreite.

   Bewusst LINKSBÜNDIG (kein margin: auto): Titel, Aufmacherbild und Beitragsfuß stehen weiter
   über die volle Spaltenbreite, der Text beginnt an derselben linken Kante wie sie. Zentriert
   säße der Textblock als einziges Element in der Spaltenmitte.

   Gilt für den ganzen Inhalt, nicht nur für Absätze — auch Bilder, Tabellen und Codeblöcke
   halten damit dieselbe rechte Grenze ein. Die Bildgrenze oben (34rem) liegt darunter, Bilder
   stoßen also nicht daran an. */
.blog-content {
    line-height: 1.8;
    font-size: 1.05rem;
    max-width: 68ch;
}

.blog-content h2,
.blog-content h3,
.blog-content h4 {
    margin-top: 2rem;
    margin-bottom: 0.75rem;
}

.blog-content img {
    max-width: 100%;
    height: auto;
    border-radius: 0.375rem;
}

/* Ein Bild, das ALLEIN in seinem Absatz steht — das ist der Normalfall einer Abbildung im
   Beitrag. Es wird begrenzt, statt die ganze Spaltenbreite einzunehmen.
   Bewusst über :only-child und nicht über .blog-content img: ein kleines Bild mitten in einem
   Satz soll im Textfluss bleiben und nicht zu einem eigenen Block werden.
   min(100%, …) statt der reinen Obergrenze, damit auf dem Telefon die Bildschirmbreite gilt.

   LINKSBÜNDIG, nicht zentriert: im ganzen Blog ist alles linksbündig — Überschriften, Text,
   Metazeilen, Archiv. Sobald das Bild schmaler ist als die Spalte (auf einem breiten Fenster
   ist es das deutlich), steht ein zentriertes Bild auf keiner Linie und wird zur einzigen
   Ausnahme im Satzbild. Linksbündig teilt es die linke Kante mit dem Text. Zum Umstellen
   genügte hier und bei .image-gallery je ein margin-inline: auto. */
.blog-content p > img:only-child {
    display: block;
    width: auto;
    max-width: min(100%, var(--tf-content-image-max-w));
    max-height: var(--tf-content-image-max-h);
}

/* Bild MIT Unterschrift (MarkdownService: selbst geschrieben über "^^^ … ^^^ Text" oder
   automatisch aus der Mediotheks-Unterschrift gebaut) — dieselben Maße wie ein Einzelbild ohne
   Unterschrift oben, sonst stünde ein Bild nur deshalb in voller Spaltenbreite, weil es über
   figure statt über :only-child läuft. "figure img" statt "figure > img": eine selbst
   geschriebene Unterschrift lässt Markdig das Bild zusätzlich in einen <p> legen, unser eigens
   gebautes figure (Mediotheks-Unterschrift ohne eigene Klammer) nicht — beide sollen gleich
   groß werden. margin wie .image-gallery-block unten: eine Abbildung mit Unterschrift ist im
   Textfluss nichts Größeres als eine ohne. Der Browser-Standardrand (figure { margin: 1em 40px })
   wird deshalb zurückgesetzt.

   width/max-width sind NICHT redundant zu den Bildmaßen darunter: figure ist ein Block und wäre
   ohne sie so breit wie die Textspalte (.blog-content, max-width: 68ch) — das Bild bliebe bei
   34rem, die Unterschrift liefe aber über dessen rechte Kante hinaus. fit-content zieht die Box
   auf das tatsächliche Bild zusammen (ein kleines Bild bekommt so keine überbreite Unterschrift),
   max-width deckelt sie: fit-content allein genügt nicht, weil der Max-Content einer langen
   Unterschrift in die Shrink-to-fit-Rechnung eingeht und die Box wieder aufziehen würde.
   Dieselbe Klammer wie beim Bild, damit beide Obergrenzen an EINEM Token hängen. */
.blog-content figure {
    width: fit-content;
    max-width: min(100%, var(--tf-content-image-max-w));
    margin: 1.5rem 0;
}

.blog-content figure img {
    display: block;
    width: auto;
    max-width: min(100%, var(--tf-content-image-max-w));
    max-height: var(--tf-content-image-max-h);
}

/* Bildunterschrift — Metadatum, keine Beitragsprosa: Mono-Rolle wie .meta-date, aber wrappend
   wie .meta-filename statt nowrap, weil eine Unterschrift auch mal zwei Sätze sein darf (siehe
   dort). Gilt für ein figcaption (geschrieben oder automatisch erzeugt) GENAUSO wie für die
   sichtbare Unterschrift unter einer Galerie (.image-gallery-caption) — dieselbe Rolle, nur
   zwei verschiedene HTML-Bausteine, weil eine Galerie bewusst KEIN figure/figcaption bekommt
   (siehe MarkdownService: das würde ihr eigenes CSS und die Zähler-Logik zerlegen). */
/* Ein Link in der Unterschrift (z.B. die Quellenangabe eines Screenshots) bleibt ein
   gewöhnlicher Link in --tf-accent — die Metadatum-Farbe gilt für den Fließtext der
   Unterschrift, nicht für das, was sie als Verweis markiert. Ein Quellenlink, der aussieht wie
   der Rest des grauen Texts, wäre für seinen Zweck (§63 UrhG: erkennbare Urheberangabe) genau
   die falsche Zurückhaltung. */
.blog-content figcaption,
.blog-content .image-gallery-caption {
    font-family: var(--tf-mono);
    font-size: 0.78rem;
    color: var(--bs-secondary-color);
    overflow-wrap: anywhere;
    margin-top: 0.5rem;
    /* Nur für .image-gallery-caption nötig: die ist ein <p> und erbt sonst Bootstraps
       margin-bottom: 1rem, ein figcaption hat keinen Standardrand. Heute unsichtbar, weil
       beide als letztes Kind mit dem 1.5rem ihres Wrappers kollabieren — aber sobald jener
       Abstand unter 1rem fällt, liefen die zwei Wege optisch auseinander. */
    margin-bottom: 0;
}

.blog-content pre {
    background-color: var(--bs-tertiary-bg);
    border: 1px solid var(--bs-border-color);
    border-radius: 0.375rem;
    padding: 0.9rem 1rem;
    overflow-x: auto;
    font-size: 0.875rem;
}

/* Kopieren-Button in Codeblöcken (wwwroot/js/site.js: addCodeCopyButtons) — zur Laufzeit
   eingefügt, steht nie im gespeicherten BodyHtml. .code-block ist der Wrapper, den das Skript
   um jedes <pre> legt, damit der Button NEBEN dem Code sitzt statt darin (siehe dort: sonst
   stünde "Kopieren" in der Kopie und im Ausdruck). position: relative auf dem Wrapper gibt dem
   Button einen Fixpunkt oben rechts, der beim horizontalen Scrollen einer langen Zeile nicht
   mitwandert — nur .blog-content pre selbst scrollt (overflow-x: auto oben), der Wrapper nicht.
   Ein leiser Metadaten-Baustein wie .image-gallery__count: Mono, gedeckte Fläche in Ruhe,
   Akzent erst beim Hover — keine neue Farbe. */
.blog-content .code-block {
    position: relative;
}

/* Platz für den Button rechts oben reservieren — sonst läuft eine bis zum rechten Rand
   reichende Codezeile UNTER den deckend hinterlegten Button und wird dort unlesbar; bei einem
   einzeiligen Codeblock wäre das die einzige Zeile. Bewusst am Wrapper verankert statt an
   .blog-content pre selbst: den Platz zahlt nur ein pre, das tatsächlich einen Button trägt —
   ohne JavaScript gibt es weder Wrapper noch Button, und das pre behält seine volle Breite. */
.blog-content .code-block pre {
    padding-right: 4.5rem;
}

.blog-content .code-copy-btn {
    position: absolute;
    top: 0.5rem;
    right: 0.5rem;
    padding: 0.15rem 0.55rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 3px;
    background-color: var(--bs-body-bg);
    font-family: var(--tf-mono);
    font-size: 0.7rem;
    line-height: 1.5;
    color: var(--bs-secondary-color);
    opacity: 0.85;
    cursor: pointer;
    /* rechtsbündig verankert (right: 0.5rem) — bei einem längeren Rückmeldetext ("Kopieren
       nicht möglich") wächst der Button nach links statt umzubrechen und aus der Ecke zu fallen */
    white-space: nowrap;
}

.blog-content .code-copy-btn:hover,
.blog-content .code-copy-btn:focus-visible {
    border-color: var(--tf-accent);
    color: var(--tf-accent);
    opacity: 1;
}

/* Weder im Ausdruck noch als Bedienelement auf Papier sinnvoll */
@media print {
    .blog-content .code-copy-btn {
        display: none;
    }
}

/* Syntax-Highlighting (wwwroot/js/syntax-highlight.js, highlight.js 11 ohne mitgeliefertes
   Theme). Vier Rollen statt der ~15 hljs-Klassen, die csharp/sql/json/bash/xml/javascript/css
   zusammen mitbringen — alles andere (Satzzeichen, Parameterlisten, Template-Interpolation)
   bleibt normaler, unfarbiger Codetext, damit die Hervorhebung den Text nicht erschlägt.
   .blog-content trifft sowohl die Beitragsseite als auch die Editor-Vorschau (#markdownPreview
   trägt dieselbe Klasse, siehe Edit.cshtml). */
.blog-content .hljs-comment,
.blog-content .hljs-doctag {
    color: var(--bs-secondary-color);
    font-style: italic;
}

.blog-content .hljs-keyword,
.blog-content .hljs-meta,
.blog-content .hljs-literal {
    font-weight: 600;
}

.blog-content .hljs-string,
.blog-content .hljs-number,
.blog-content .hljs-regexp,
.blog-content .hljs-symbol {
    color: var(--tf-code-string);
}

.blog-content .hljs-title,
.blog-content .hljs-type,
.blog-content .hljs-built_in,
.blog-content .hljs-attr,
.blog-content .hljs-attribute,
.blog-content .hljs-tag,
.blog-content .hljs-name,
.blog-content .hljs-selector-tag,
.blog-content .hljs-selector-id,
.blog-content .hljs-selector-class,
.blog-content .hljs-selector-pseudo,
.blog-content .hljs-selector-attr,
.blog-content .hljs-property,
.blog-content .hljs-variable {
    color: var(--tf-code-name);
}

.blog-content :not(pre) > code {
    font-size: 0.875em;
    color: var(--tf-accent);
    background-color: var(--bs-tertiary-bg);
    padding: 0.1rem 0.3rem;
    border-radius: 3px;
}

.blog-content blockquote {
    border-left: 3px solid var(--tf-accent);
    padding-left: 1rem;
    margin-left: 0;
    color: var(--bs-secondary-color);
}

.blog-content table {
    width: 100%;
    margin-bottom: 1rem;
    border-collapse: collapse;
    font-size: 0.95rem;
}

.blog-content th,
.blog-content td {
    border: 1px solid var(--bs-border-color);
    padding: 0.45rem 0.7rem;
}

.blog-content th {
    background-color: var(--bs-tertiary-bg);
    text-align: left;
}

/* Hinweisboxen aus "> [!NOTE]" (Markdig-Alerts). Das mitgelieferte Icon-SVG
   entfernt der HtmlSanitizer (svg steht nicht in den erlaubten Tags), die
   Kennzeichnung kommt deshalb aus CSS. */
.blog-content .markdown-alert {
    background-color: var(--tf-accent-wash);
    border: 1px solid var(--bs-border-color);
    border-left: 3px solid var(--tf-accent);
    border-radius: 0 0.375rem 0.375rem 0;
    padding: 0.75rem 1rem;
    margin: 1.25rem 0;
}

.blog-content .markdown-alert > :last-child {
    margin-bottom: 0;
}

.blog-content .markdown-alert-title {
    font-weight: 600;
    margin-bottom: 0.35rem;
}

.blog-content .markdown-alert-tip { border-left-color: var(--bs-success); }
.blog-content .markdown-alert-warning { border-left-color: var(--bs-warning); }
.blog-content .markdown-alert-caution { border-left-color: var(--bs-danger); }

.blog-content .markdown-alert-note .markdown-alert-title::before { content: "ℹ️ "; }
.blog-content .markdown-alert-tip .markdown-alert-title::before { content: "💡 "; }
.blog-content .markdown-alert-important .markdown-alert-title::before { content: "❗ "; }
.blog-content .markdown-alert-warning .markdown-alert-title::before { content: "⚠️ "; }
.blog-content .markdown-alert-caution .markdown-alert-title::before { content: "⛔ "; }

/* Download-Links (/download/…, siehe MarkdownService.ToHtml/DownloadLinkRegex): eine
   gestrichelte statt der durchgezogenen Standard-Unterstreichung plus ein Pfeil-Symbol
   davor machen sichtbar, dass ein Klick eine Datei lädt statt zu einer Seite zu führen —
   ohne eine neue Farbe. Text und Symbol erben --bs-link-color (= --tf-accent, wie jeder
   andere Link im Beitrag auch), die Linie liegt in Ruhe auf --bs-border-color und wechselt
   beim Hover auf --tf-accent — dieselbe Geste wie bei .meta-chip. */
.blog-content .download-link {
    text-decoration: none;
    border-bottom: 1px dashed var(--bs-border-color);
    padding-bottom: 0.05rem;
}

.blog-content .download-link::before {
    content: "⬇ ";
}

.blog-content .download-link:hover,
.blog-content .download-link:focus {
    border-bottom-color: var(--tf-accent);
}

/* Zwei-Klick-Embed-Lösung für eingebettete Inhalte Dritter — YouTube, X, TikTok, Instagram,
   Facebook (MarkdownService: EmbedPlatforms baut nur noch diesen Platzhalter, site.js lädt den
   echten iframe erst nach einem Klick auf den Button) — ohne ihn würde die Seite beim bloßen
   Aufruf eine Verbindung zur jeweiligen Plattform aufbauen, bevor der Leser zugestimmt hat.
   YouTube behält zusätzlich .ratio (Bootstrap) für das feste 16:9-Format; Platzhalter UND (nach
   dem Klick) der echte iframe sind nacheinander das einzige Kind von .embed-consent und bekommen
   darüber automatisch position:absolute/inset:0 von Bootstraps ".ratio > *"-Regel, ohne dass
   diese Datei selbst etwas positionieren müsste. Die vier Social-Plattformen tragen stattdessen
   .embed-consent--post (kein .ratio, siehe unten) — sie haben keine feste Seitenrelation wie ein
   Video.
   Ruhige Fläche statt eines Vorschaubilds — ein Thumbnail von der Plattform selbst wäre bereits
   der Verbindungsaufbau, den die Lösung verhindern soll (siehe MarkdownService). Gestrichelter
   Rand wie bei .ad-placeholder ("vorläufig/reserviert", keine Fehlermeldung), keine neue Farbe. */
.embed-consent {
    background-color: var(--bs-tertiary-bg);
    border-radius: 0.375rem;
    overflow: hidden;
}

/* Social-Posts (X/TikTok/Instagram/Facebook): keine feste Seitenrelation, deshalb kein .ratio.
   min-height hält den noch nicht geladenen Platzhalter auf einer ruhigen, kompakten Höhe (~10rem,
   .embed-consent-btn zieht sie unten per "min-height: inherit" nach); die nach dem Klick
   eingesetzten iframes bekommen weiter unten ihre eigene, plattformtypische feste Höhe. Der
   dadurch entstehende Layout-Sprung beim Klick ist eine bewusste Nutzeraktion und wird in Kauf
   genommen — kein Resize-Skript existiert für eines dieser vier Portale unter script-src 'self'. */
.embed-consent--post {
    max-width: 540px;
    min-height: 10rem;
}

.embed-consent-btn {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 0.4rem;
    width: 100%;
    height: 100%;
    min-height: inherit;
    padding: 1rem;
    border: 1px dashed var(--bs-border-color);
    border-radius: 0.375rem;
    background: none;
    color: var(--bs-body-color);
    text-align: center;
    cursor: pointer;
}

/* Symbol als CSS-Inhalt statt Emoji (Design-System-Vorgabe) — dieselbe Technik wie der Pfeil vor
   einem Download-Link oben, nur größer, weil es hier die einzige grafische Markierung des
   Bausteins ist. Bewusst EIN Symbol für alle fünf Plattformen statt fünf verschiedener Icons:
   der Baustein sagt "hier wird nach einem Klick etwas nachgeladen", das WAS steht im Label
   darunter. */
.embed-consent-btn::before {
    content: "▶";
    font-size: 2.25rem;
    line-height: 1;
    color: var(--bs-secondary-color);
}

/* Ein Play-Dreieck verspricht ein Video — ein X-/Instagram-/Facebook-POST ist aber oft
   Text oder Bild. Die Post-Variante zeigt deshalb ein neutrales "öffnet etwas"-Zeichen;
   YouTube und TikTok (immer Video) behalten das ▶. */
.embed-consent--post .embed-consent-btn::before {
    content: "↗";
}

.embed-consent-btn__label {
    font-weight: 500;
}

/* Datenschutzhinweis in derselben Mono-Metadaten-Rolle wie ein figcaption (siehe oben) — dieser
   Text ist ein Nebenhinweis zum Baustein, keine Beitragsprosa. Wrappend statt nowrap (.meta-date
   wäre hier falsch), weil der Satz auf einem schmalen Fenster umbrechen muss. */
/* Mono/Muted/wrappend kommt aus .meta-filename (MarkdownService gibt die Klasse mit) —
   hier steht nur, was die Rolle nicht abdeckt. Eine Kopie der Rollenwerte wäre die zweite
   Quelle für dieselbe Rolle (siehe .ad-label-Lehre weiter oben). */
.embed-consent-btn__hint {
    max-width: 24rem;
}

/* Hover markiert nur, er füllt nicht (Farbregel): Rand und Symbol wechseln auf den Akzent, die
   Fläche bleibt unverändert. */
.embed-consent-btn:hover,
.embed-consent-btn:focus-visible {
    border-color: var(--tf-accent);
}

.embed-consent-btn:hover::before,
.embed-consent-btn:focus-visible::before {
    color: var(--tf-accent);
}

/* Nach dem Klick eingesetzter echter iframe der vier Social-Plattformen. Die Höhen hier sind
   nur die STARTWERTE: X- und Instagram-iframes melden ihre echte Höhe per postMessage, site.js
   setzt sie dann inline nach (Auto-Resize ohne Plattform-Skript). TikTok und Facebook senden
   keine verlässliche Nachricht — dort bleibt die feste Höhe, innen scrollbar wenn nötig.
   YouTube braucht nichts davon: sein iframe füllt weiterhin die .ratio-Box (s.o.). */
.embed-consent--post iframe {
    display: block;
    width: 100%;
    border: 0;
}

.embed-consent--x iframe {
    height: 350px;
}

/* Hochformat-Player (TikToks Format), deshalb deutlich höher als breit. */
.embed-consent--tiktok iframe {
    height: 740px;
}

.embed-consent--instagram iframe {
    height: 540px;
}

.embed-consent--facebook iframe {
    height: 560px;
    overflow: auto;
}

/* Kommentar-Antwort: Zitat des beantworteten Kommentars, dort wo die Einrückung endet */
.comment-quote {
    display: block;
    margin-bottom: 0.5rem;
    padding: 0.35rem 0.6rem;
    border-left: 3px solid var(--bs-border-color);
    border-radius: 0 0.375rem 0.375rem 0;
    background-color: var(--bs-tertiary-bg);
    color: var(--bs-secondary-color);
    font-size: 0.875rem;
    text-decoration: none;
    transition: border-left-color 0.15s ease, color 0.15s ease;
}

.comment-quote:hover,
.comment-quote:focus {
    border-left-color: var(--tf-accent);
    color: var(--bs-body-color);
}

/* Hebt den angesprungenen Kommentar hervor — aus einem Zitat-Link oder nach dem
   Abschicken. box-shadow statt padding, damit die Hervorhebung nichts verschiebt. */
.comment:target {
    background-color: var(--tf-accent-wash);
    box-shadow: 0 0 0 0.5rem var(--tf-accent-wash);
    border-radius: 0.375rem;
    transition: background-color 0.3s ease;
}

/* Bild-Lightbox (Markup: Views/Shared/_ImageLightbox.cshtml, Steuerung: site.js) */
.lightbox-img {
    max-width: 100%;
    max-height: 78vh;
    height: auto;
    border-radius: 0.375rem;
}

/* Klickbare Bilder: im Beitrag/in der Vorschau alle, sonst per .js-zoom markiert */
.blog-content img,
.js-zoom img {
    cursor: zoom-in;
}

/* Bild und Pfeile; die Unterschrift steht bewusst AUSSERHALB (siehe _ImageLightbox.cshtml),
   damit die auf 50% zentrierten Pfeile die Bildmitte treffen und nicht die Mitte aus Bild plus
   Unterschrift. Volle Breite wie zuvor der modal-body, damit die Pfeile dort bleiben, wo sie
   bisher saßen. */
.lightbox-stage {
    position: relative;
}

/* Bildunterschrift im Overlay — dieselbe Mono-Metadaten-Rolle wie das figcaption im Beitrag
   (site.css oben), nur mittig, weil das Bild im Overlay als einziges Element im Blog mittig
   steht. max-width als Lesebreite: im modal-xl liefe eine zweisätzige Unterschrift sonst über
   die ganze Fensterbreite, was sich schlechter liest als das Bild breit ist. */
.lightbox-caption {
    font-family: var(--tf-mono);
    font-size: 0.78rem;
    color: var(--bs-secondary-color);
    overflow-wrap: anywhere;
    max-width: 60ch;
    margin: 0.75rem auto 0.25rem;
}

/* Blättern im Overlay. Die Pfeile liegen über den Bildrändern statt in der Kopfzeile — dort
   erwartet sie die Hand, und die Kopfzeile trägt schon Zähler und zwei Knöpfe. Ruhig
   gehalten: Flächenfarbe des Blattes, dünner Rand, Akzent erst beim Überfahren. */
.lightbox-nav {
    position: absolute;
    top: 50%;
    transform: translateY(-50%);
    z-index: 2;
    width: 2.5rem;
    height: 2.5rem;
    padding: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    border: 1px solid var(--bs-border-color);
    border-radius: 50%;
    background-color: var(--bs-body-bg);
    color: var(--bs-body-color);
    font-size: 1.5rem;
    line-height: 1;
    opacity: 0.85;
}

.lightbox-nav:hover,
.lightbox-nav:focus-visible {
    border-color: var(--tf-accent);
    color: var(--tf-accent);
    opacity: 1;
}

.lightbox-nav--prev { left: 0.75rem; }
.lightbox-nav--next { right: 0.75rem; }

/* Bildergalerie im Beitrag (MarkdownService: mehrere Bilder in einem Absatz). Sichtbar ist nur
   das erste Bild — unbeschnitten und in voller Breite, also genau so, wie ein einzelnes Bild im
   Beitrag auch aussieht. Die übrigen stehen im HTML, sind aber ausgeblendet; dass es sie gibt,
   sagt der Zähler in der Ecke, und geblättert wird im Overlay.

   display:none und nicht etwa versteckt-aber-geladen: so holt der Browser die weiteren Bilder
   gar nicht erst: sie kommen erst, wenn das Overlay sie anfordert (site.js lädt beim Öffnen die
   Nachbarn vor). Der Preis ist, dass sie auch aus dem Accessibility-Baum fallen — ihre
   Alt-Texte gibt es erst im Overlay. */
/* fit-content statt voller Breite: der Zähler sitzt absolut in der Ecke DIESES Kastens, und
   der muss deshalb genau so breit sein wie das sichtbare Bild — sonst klebte er am rechten
   Spaltenrand statt am Bild. Linksbündig aus demselben Grund wie das einzelne Bild oben.
   Trägt selbst KEINEN Außenabstand mehr — der sitzt auf .image-gallery-block, ihrem äußeren
   Wrapper, damit eine Galerie MIT sichtbarer Unterschrift (MarkdownService: Mediotheks-Text des
   ersten Bildes) nicht zweimal Abstand bekommt (Galerie-Rand + Unterschrift-Rand) und eine
   Galerie OHNE Unterschrift optisch unverändert bleibt. */
.image-gallery {
    position: relative;
    display: block;
    width: fit-content;
}

/* Äußerer Wrapper aus MarkdownService — immer vorhanden, auch ohne sichtbare Unterschrift
   (ein Codepfad statt eines bedingten zweiten). Trägt den Abstand, den früher .image-gallery
   selbst trug. Breite aus demselben Grund wie bei .blog-content figure oben: .image-gallery ist
   zwar fit-content und damit bildbreit, .image-gallery-caption steht aber als GESCHWISTER
   daneben, nicht darin — ohne Begrenzung am gemeinsamen Wrapper liefe sie über die Bildkante
   hinaus. */
.image-gallery-block {
    width: fit-content;
    max-width: min(100%, var(--tf-content-image-max-w));
    margin: 1.5rem 0;
}

.image-gallery img {
    display: none;
}

/* Dieselben Grenzen wie beim einzelnen Bild im Absatz — eine Galerie ist im Beitrag nichts
   Größeres als eine Abbildung, nur eine mit mehr dahinter. */
.image-gallery img:first-child {
    display: block;
    width: auto;
    max-width: min(100%, var(--tf-content-image-max-w));
    max-height: var(--tf-content-image-max-h);
}

/* "1 / 3" über der unteren rechten Ecke des Aufmacherbildes. Mono wie jede andere Zahlangabe,
   auf der Blattfarbe statt auf einer Tönung, damit sie auf hellen wie dunklen Bildern lesbar
   bleibt. pointer-events:none ist Pflicht und kein Feinschliff: ohne das wäre ausgerechnet die
   Stelle, die das Weiterblättern ankündigt, die einzige, an der ein Klick nichts öffnet. */
.image-gallery__count {
    position: absolute;
    right: 0.5rem;
    bottom: 0.5rem;
    pointer-events: none;
    padding: 0.1rem 0.45rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 3px;
    background-color: var(--bs-body-bg);
    opacity: 0.9;
    font-family: var(--tf-mono);
    font-size: 0.7rem;
    line-height: 1.5;
    color: var(--bs-secondary-color);
}

.image-gallery:hover .image-gallery__count {
    border-color: var(--tf-accent);
    color: var(--tf-accent);
    opacity: 1;
}

/* Platzhalter für einen unbelegten Werbeplatz (Views/Blog/Detail.cshtml) — nur die Redaktion
   sieht ihn. Gestrichelter Rand statt durchgezogenem: dieselbe Aussage wie beim Download-Link,
   nämlich "vorläufig". Bewusst kein Orange und keine Signalfarbe — das hier ist kein Fehler
   und keine Warnung, sondern eine reservierte Fläche. */
.ad-placeholder {
    padding: 1.25rem;
    border: 1px dashed var(--bs-border-color);
    border-radius: 6px;
    background-color: var(--bs-tertiary-bg);
    color: var(--bs-secondary-color);
}

/* Das Abschnittslabel über dem Redaktions-Platzhalter UND das "Anzeige"-Label der
   Fallback-Anzeige (AdSlots.RenderFallbackHtml) — Letzteres ist kein Schmuck, sondern
   medienrechtliches Trennungsgebot: bezahlte Werbung muss als solche erkennbar sein. Beide
   nutzen die bestehende Mono-Metadaten-Rolle .meta-date und erweitern sie nur — eine eigene
   Klasse mit denselben Werten wäre die zweite Quelle für dieselbe Rolle. */
.ad-placeholder .meta-date,
.ad-fallback .meta-date {
    display: block;
    margin-bottom: 0.35rem;
    text-transform: uppercase;
    letter-spacing: 0.08em;
}

/* Consentfreie Fallback-Anzeige eines Werbeplatzes (AdSlots.RenderFallbackHtml) — lokales Bild
   und/oder Link, immer sichtbar sobald kein Tracking-Code rendert. In After-Header, Sidebar und
   Before-Footer bestimmt der Slot die Breite; nur der InContent-Slot sitzt IM Beitragstext
   (.blog-content, 68ch) und bekommt darum unten dieselbe Deckelung wie ein Beitragsbild — sonst
   wäre die Werbung breiter als jedes redaktionelle Bild im selben Beitrag. */
.ad-fallback img {
    max-width: 100%;
    height: auto;
    display: block;
}

.ad-incontent.ad-fallback img {
    max-width: min(100%, var(--tf-content-image-max-w));
    max-height: var(--tf-content-image-max-h);
}

/* Markdown-Editor: Formatierungsleiste über der Textarea */
.md-toolbar {
    background-color: var(--bs-tertiary-bg);
}

.md-toolbar .btn {
    --bs-btn-padding-x: 0.5rem;
    min-width: 2.15rem;
    line-height: 1.25;
}

#BodyMarkdown {
    font-family: var(--tf-mono);
    font-size: 0.9rem;
    line-height: 1.6;
}

/* Tag-Auswahl im Editor (wwwroot/js/tag-picker.js): entfernbare Chips + Suchfeld mit
   Vorschlagsliste statt des früheren Freitextfelds. Die Vorschlagsliste ist absichtlich
   Bootstraps .dropdown-menu ohne dessen JS-Plugin — die statische Absolut-Positionierung
   direkt unter dem Suchfeld reicht, .show blendet sie ein wie beim echten Dropdown. */
.tag-chip {
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
}

.tag-chip__remove {
    border: none;
    background: none;
    padding: 0;
    line-height: 1;
    font-size: 0.95rem;
    font-family: var(--tf-mono);
    color: inherit;
    cursor: pointer;
}

.tag-chip__remove:hover,
.tag-chip__remove:focus-visible {
    color: var(--tf-accent);
}

#tagSuggestions {
    max-height: 14rem;
    overflow-y: auto;
}

/* Bootstraps .dropdown-item bringt white-space: nowrap mit — bei einem langen Tag-Namen
   würde das den absolut positionierten, w-100-breiten Vorschlags-<ul> über seinen Container
   hinausschieben. Dieselbe Falle wie bei #tagUrlPreview unten, hier nur seltener auslösbar. */
#tagSuggestions .dropdown-item {
    cursor: pointer;
    white-space: normal;
    overflow-wrap: anywhere;
}

/* Der Hinweis "Kein passender Tag …" ist .dropdown-item-text, kein anklickbarer Eintrag, und
   erbt seine Farbe aus .text-muted — braucht deshalb keine eigene Regel. Die frühere
   .dropdown-item--create-Variante ist mit dem Anlegen aus dem Editor entfallen. */

/* Adressvorschau in der Tag- und der Datei-Verwaltung (CreateTag.cshtml/EditTag.cshtml/
   UploadFile.cshtml/EditFile.cshtml, wwwroot/js/site.js): .meta-date bringt white-space:
   nowrap mit (richtig für ein Datum, falsch hier) — Host + Pfad + URL-Name als ein
   unbrechbarer Block könnte den Seitenkörper auf schmalen Bildschirmen horizontal scrollen
   lassen, sobald der Name lang wird. */
#tagUrlPreview,
#fileUrlPreview {
    white-space: normal;
    overflow-wrap: anywhere;
}

/* Medien-Auswahl im Editor (Views/Blog/_MediaPicker.cshtml, wwwroot/js/media-picker.js): ein
   Bootstrap-Modal mit zwei Reitern, aus dem eine bereits hochgeladene Datei per Klick als
   Markdown-Link eingefügt wird. Die Kacheln/Zeilen sind <button>, keine <a> — ein Klick löst
   kein Navigieren aus, sondern das CustomEvent an editor.js. */
.media-picker-tile {
    display: flex;
    flex-direction: column;
    width: 100%;
    padding: 0 0 0.4rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 5px;
    background-color: var(--bs-body-bg);
    overflow: hidden;
    text-align: left;
    cursor: pointer;
    /* <button> statt <a> bringt Browser-Vorgaben für Schrift mit, die vom Rest der Seite
       abweichen — zurückgesetzt wie bei .tag-chip__remove weiter oben. */
    font: inherit;
}

.media-picker-tile:hover,
.media-picker-tile:focus-visible {
    border-color: var(--tf-accent);
}

/* Angehakt in der Mehrfachauswahl (Galerie). Der Rand ist schon die Hover-Markierung, also
   braucht der gewählte Zustand ein zweites Merkmal, das auch ohne Mauszeiger gilt: ein
   kräftigerer Rand plus getönte Fläche — dieselbe Akzentfarbe, keine neue. */
.media-picker-tile--selected {
    border-color: var(--tf-accent);
    border-width: 2px;
    background-color: var(--bs-tertiary-bg);
}

.media-picker-tile img {
    width: 100%;
    height: 6rem;
    object-fit: cover;
    margin-bottom: 0.35rem;
}

/* Titel als Wiedererkennungspunkt (statt des früheren GUID-Dateinamens) — fett wie ein
   .card-title, aber ohne dessen Bootstrap-Abstände, die hier schon die Kachel selbst setzt. */
.media-picker-tile__title {
    padding: 0 0.4rem;
    font-weight: 500;
    color: var(--bs-body-color);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Verwendung und Herkunft/Datum/Größe unter der Kachel — eine Zeile je Angabe, abgeschnitten
   statt umbrechend, damit alle Kacheln einer Reihe gleich hoch bleiben. */
.media-picker-tile .meta-date {
    padding: 0 0.4rem;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

/* Dateien stehen als Liste, nicht als Kacheln — Titel, Dateiname und Downloadzahl passen
   nebeneinander besser als in einem Bild-Raster. */
.media-picker-row {
    display: flex;
    flex-wrap: wrap;
    align-items: baseline;
    gap: 0.5rem;
    width: 100%;
    padding: 0.5rem 0.25rem;
    border: none;
    border-top: 1px solid var(--bs-border-color);
    background: none;
    text-align: left;
    cursor: pointer;
    font: inherit;
}

.media-picker-row:first-child {
    border-top: none;
}

.media-picker-row:hover,
.media-picker-row:focus-visible {
    background-color: var(--bs-tertiary-bg);
}

.media-picker-row__title {
    flex: 1 1 auto;
    min-width: 10rem;
    color: var(--bs-body-color);
}

/* Feste Höhe mit eigenem Scrollbalken statt das ganze Modal wachsen zu lassen, wenn die
   Bibliothek groß ist. */
#mediaImageResults,
#mediaFileResults {
    max-height: 22rem;
    overflow-y: auto;
}

/* Titelbild auf der Beitragsseite — flaches Format, damit der Titel oben bleibt */
.post-hero {
    display: block;
    overflow: hidden;
    margin-bottom: 1.25rem;
    border: 1px solid var(--bs-border-color);
    border-radius: 6px;
    aspect-ratio: 21 / 9;
    max-height: 20rem;
}

.post-hero img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

/* Beitragsfuß: Datum, Autor, Aufrufe und Tags stehen unter dem Text, nicht im Kopf.
   Die Linie trennt sie vom Beitrag — bewusst nur oben, denn nach unten grenzt schon
   die Linie über "Weiterlesen" ab. Reichlich Abstand über der Linie, damit sie als
   Ende des Textes gelesen wird und nicht als Zwischenüberschrift. */
.post-meta-footer {
    margin-top: 2.75rem;
    padding-top: 1.125rem;
    border-top: 1px solid var(--bs-border-color);
}

/* Erstellt / Aktualisiert / Aufrufe stehen hier je in einer eigenen Zeile — drei
   verschiedene Aussagen, nicht eine Aufzählung. Übernimmt Schrift, Größe und Farbe von
   .meta-date und hebt nur das dort nötige nowrap auf: als Blockzeile mit Namen darin
   muss der Text auf schmalen Schirmen umbrechen dürfen. */
.post-meta-footer .meta-date {
    display: block;
    white-space: normal;
}

.post-meta-footer .meta-date + .meta-date {
    margin-top: 0.3rem;
}

/* Werbekennzeichnung für Amazon-Affiliate-Links (MarkdownService.ToHtml, UWG-Pflicht) — beide
   Textstellen bekommen die Klasse ihrer Mono-Metadaten-Rolle direkt vom Markup mit
   (.meta-date/.meta-filename), genau wie .embed-consent-btn__hint oben. .affiliate-hint
   bekommt darum bewusst KEINE eigene Regel: .meta-date deckt bereits alles ab, was der
   Inline-Hinweis am Link braucht (nowrap, gedämpfte Farbe, läuft als <span> im Satz mit statt
   eine Zeile zu erzwingen); das führende "&nbsp;" im Markup selbst trennt ihn vom Linktext.
   .affiliate-disclosure erweitert die Rolle nur um den Abstand zum übrigen Beitragstext —
   angelehnt an .post-meta-footer, weil der Satz denselben Zweck hat (Ende einer Aussage
   markieren), nur dass er als letztes Element INNERHALB von .blog-content landet, nicht im
   separaten Beitragsfuß darunter. */
.affiliate-disclosure {
    margin-top: 1.75rem;
    padding-top: 1rem;
    border-top: 1px solid var(--bs-border-color);
}

/* Beitragsliste mit Vorschaubild (Tag-Seiten) — Linien statt Karten */
.post-list-item {
    display: flex;
    gap: 1rem;
    padding: 1.35rem 0;
    border-top: 1px solid var(--bs-border-color);
}

.post-list-item:last-child {
    border-bottom: 1px solid var(--bs-border-color);
}

.post-list-thumb {
    flex-shrink: 0;
    display: block;
    width: 6rem;
    height: 6rem;
    overflow: hidden;
    border: 1px solid var(--bs-border-color);
    border-radius: 5px;
}

.post-list-thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}

.post-list-item h2 {
    font-size: 1.15rem;
    margin-bottom: 0.35rem;
}

.post-list-item h2 a {
    color: var(--bs-body-color);
    text-decoration: none;
}

.post-list-item h2 a:hover {
    color: var(--tf-accent);
}

/* Seitenkopf: Serif-Titel mit Mono-Beisatz (Tag-Name, Trefferzahl) */
.page-head {
    padding-bottom: 1.25rem;
    margin-bottom: 0.5rem;
    border-bottom: 1px solid var(--bs-border-color);
}

.page-head h1 {
    margin-bottom: 0.35rem;
}

/* Chip-Varianten für Status und Rollen: Abstufung über Tinte/Akzent,
   nicht über eine Ampel aus rot/gelb/blau */
.meta-chip--strong {
    background-color: var(--bs-body-color);
    border-color: var(--bs-body-color);
    color: var(--bs-body-bg);
}

.meta-chip--accent {
    border-color: var(--tf-accent);
    color: var(--tf-accent);
}

/* Supporter-Badge (Kommentare, Benutzerverwaltung): wie --accent, nur mit der getönten
   Akzent-Waschfläche der Hinweisboxen (--tf-accent-wash) als Hintergrund — das hebt es vom
   umrandeten Mitglied-Chip daneben ab, ohne eine neue Farbe einzuführen. Eine VOLL gefüllte
   Orangefläche war der erste Wurf und ist verworfen: neben dem zurückhaltenden Mitglied-Chip
   wirkte sie wie ein Alarm-Knopf statt wie eine Auszeichnung — Orange markiert in diesem
   Farbsystem, es trägt keine Flächen. */
.meta-chip--supporter {
    background-color: var(--tf-accent-wash);
    border-color: var(--tf-accent);
    color: var(--tf-accent);
}

/* Zustand "vorhanden, aber nicht aktiv" für einen Chip — z.B. das Supporter-Badge in der
   Benutzerverwaltung, wenn der Nutzer die Anzeige selbst abgeschaltet hat: die neutrale
   Grau-Optik des Standard-Chips plus gestrichelter Rand, dasselbe Signal wie .ad-placeholder
   und .embed-consent (gestrichelt = Platzhalter). Bewusst als eigener Zustands-Modifier
   statt einer Supporter-Sondervariante, damit ein künftiges zweites Badge ihn mitnutzt. */
.meta-chip--hidden {
    border-style: dashed;
    color: var(--bs-secondary-color);
}

/* Beitragsbezug in der Kommentar-Moderation: derselbe Listeneintrag wie auf der
   Startseite, nur schmaler und ohne den oberen Innenabstand der Listenvariante */
.comment-post-ref {
    flex-shrink: 0;
    max-width: 20rem;
}

.comment-post-ref .recent-item {
    padding-top: 0;
}
