/* =========================================================================
   petit-souris — Design-System
   Der ganze Bildschirm ist eine Linux-Konsole: eine Spalte Textausgabe in
   Monospace, ASCII-Kästen statt Karten, Menü als ASCII-Leiste, und ganz unten
   eine feste Eingabezeile, die den Befehl zur Seite zeigt und selbst welche
   entgegennimmt.
   ========================================================================= */

/* ---------- Schrift (selbst gehostet, keine externen Requests) ---------- */
@font-face {
    font-family: "JetBrains Mono";
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url("../fonts/jetbrains-mono-latin-AcnlIJe.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA,
        U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191,
        U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
@font-face {
    font-family: "JetBrains Mono";
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url("../fonts/jetbrains-mono-latin-ext-c8h-btv.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF,
        U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020,
        U+20A0-20AB, U+20AD-20C0, U+2113, U+2C60-2C7F, U+A720-A7FF;
}

:root {
    --bg: #0d1016;
    --bg-soft: #131822;
    --surface: #151b26;
    --surface-2: #1b2230;

    --border: #29313f;
    /* Nachgemessen: #3c4759 kam auf dem Seitengrund nur auf 2,03:1. Eingabefelder
       haben denselben Hintergrund wie die Seite — der Rahmen ist also das Einzige,
       was sie begrenzt, und braucht darum die 3:1 aus WCAG 1.4.11. */
    --border-strong: #55647e;

    --text: #d3dae6;
    --text-muted: #869; /* Fallback, wird direkt darunter überschrieben */
    --text-muted: #838ea3;
    --ink: var(--text);

    --green: #52d97f;
    /* Nachgemessen: #2f7f4d kam als Text auf 3,9:1. Etwas heller, damit auch
       kleine Angaben (Adressvorsatz, `git log`, Zähler) lesbar bleiben. */
    --green-dim: #3f9a61;
    --cyan: #56c7d6;
    --yellow: #e5c07b;
    --orange: #e59560;
    --red: #e0655f;
    --magenta: #c98bdd;
    --blue: #61a8ef;

    --accent: var(--green);
    --accent-strong: #7fe89f;
    --accent-soft: rgba(82, 217, 127, .1);

    --success: var(--green);
    --success-soft: rgba(82, 217, 127, .1);
    --danger: var(--red);
    --danger-soft: rgba(224, 101, 95, .12);

    /* Eine Konsole hat keine runden Ecken. */
    --radius: 0;
    --radius-sm: 0;
    --bw: 1px;
    --glow: 0 0 0 1px var(--green);

    /* Zeilenbreite wie ein Terminal: rund 92 Spalten. */
    --maxw: 92ch;
    --font: "JetBrains Mono", ui-monospace, SFMono-Regular, "SF Mono", Menlo,
        Consolas, "DejaVu Sans Mono", monospace;

    /* Halbdurchsichtige Fläche über Bewegtbild (großer Abspielknopf). Muss dem
       Schema folgen — fest dunkel wäre im hellen Schema unlesbar. */
    --overlay: rgba(13, 16, 22, .78);

    /* Platzhaltertext zählt als Text und braucht damit 4,5:1. Das alte #5a6274
       kam auf #0d1016 nur auf 3,1:1. Nachgerechnet: #757f94 ergibt 4,7:1, und in
       den dunkleren Schemata (amber, cyan, mono) wird es dadurch nur besser. */
    --placeholder: #757f94;

    --prompt-h: 2.8rem;
    /* Höhe der Symfony-Debug-Leiste; nur im Dev-Modus ungleich null (siehe unten). */
    --debug-bar: 0px;
}

/* Im Dev-Modus klebt die Symfony-Debug-Leiste ganz unten und würde die
   Eingabezeile verdecken — dann rutscht diese darüber. In Produktion, wo es die
   Leiste nicht gibt, greift die Regel nie. Die genaue Höhe misst der
   prompt-Controller nach (die Leiste bricht auf schmalen Fenstern um). */
body:has(.sf-toolbar) { --debug-bar: 38px; }

/* =========================================================================
   Farbschemata — umschaltbar per `style <name>` in der Eingabezeile.
   Es genügt, die Leitfarben und die Flächen zu tauschen; alles andere leitet
   sich über var() daraus ab.
   ========================================================================= */
:root { color-scheme: dark; }

/* Bernstein-Röhre. */
:root[data-theme="amber"] {
    --bg: #120f09; --bg-soft: #1a140b; --surface: #1d160c; --surface-2: #261d10;
    --border: #3a2c14; --border-strong: #7d5d2b; /* 3,17:1 statt 1,99:1 */
    --text: #f0ddb6; --text-muted: #ac9063;
    /* --green-dim ist Text (Hinweiszeile, ls-Spalten): #8a5f10 kam auf 3,39:1. */
    --green: #ffb000; --green-dim: #aa7514;
    --cyan: #ffcf6b; --blue: #ffc247; --yellow: #ffd88a;
}

/* Kaltes Cyan. */
:root[data-theme="cyan"] {
    --bg: #08111a; --bg-soft: #0d1a25; --surface: #10202c; --surface-2: #163041;
    --border: #22404f; --border-strong: #3b6983; /* 3,18:1 statt 2,65:1 */
    --text: #cfe7f0; --text-muted: #7b9dad;
    --green: #35d6e8; --green-dim: #208b99; /* 4,73:1 statt 3,78:1 */
    --cyan: #8ce9f5; --blue: #6fb7ff;
}

/* Gold — die Belohnung für alle 26 Eier. Steht erst dann in `style`, und wer den
   Namen vorher errät, bekommt dieselbe Absage wie bei jedem Tippfehler: das Ziel
   soll man erreichen, nicht umgehen. Nachgemessen wie die übrigen Paletten. */
:root[data-theme="gold"] {
    --bg: #0e0c07; --bg-soft: #171308; --surface: #1b1609; --surface-2: #241d0d;
    --border: #3a3115; --border-strong: #786935; /* 3,60:1 */
    --text: #f2e3bd; --text-muted: #bfa870; /* 15,36:1 und 8,42:1 */
    /* --green-dim ist Text (Hinweiszeile, ls-Spalten): 6,06:1. */
    --green: #ffd24a; --green-dim: #b08a2a;
    --cyan: #ffe07a; --blue: #ffcf6b; --yellow: #ffe9a8;
}

/* Monochrom — reines Schwarzweiß-Terminal. */
:root[data-theme="mono"] {
    --bg: #08090b; --bg-soft: #0e1013; --surface: #121418; --surface-2: #191c21;
    --border: #24282f; --border-strong: #58616f; /* 3,20:1 statt 1,94:1 */
    --text: #d9dee5; --text-muted: #8c939d;
    --green: #e8edf4; --green-dim: #747d87; /* 4,75:1 statt 4,27:1 */
    --cyan: #cdd5df; --blue: #b8c1cd; --yellow: #cdd5df;
}

/* Heller Tag — Papier statt Röhre. */
:root[data-theme="light"] {
    color-scheme: light;
    --bg: #f6f3e9; --bg-soft: #efe9da; --surface: #fbf8f1; --surface-2: #e8e1cf;
    --border: #d5cdb8; --border-strong: #958763; /* 3,20:1 statt 2,13:1 */
    --overlay: rgba(246, 243, 233, .88);
    --text: #2b2924; --text-muted: #6a6355;
    /* Nachgemessen: #1c7a3e kam auf hellem Grund nur auf 4,44:1, #7a8f7f auf 2,9:1.
       Beides jetzt dunkler — der Farbeindruck bleibt, die Lesbarkeit stimmt. */
    --green: #14652f; --green-dim: #4c6752;
    /* --yellow ist auf Papier Inline-Code, also Text: #96690a kam auf 4,38:1. */
    --cyan: #16626e; --blue: #2b5fa8; --yellow: #8f640a; --red: #b3271f;
}
:root[data-theme="light"] h1, :root[data-theme="light"] h2,
:root[data-theme="light"] h3, :root[data-theme="light"] h4 { color: #1a1815; }
/* Auf Papier war #9b9382 mit 2,8:1 noch weiter daneben als im Dunkeln; #736b55
   kommt auf 4,8:1 und bleibt trotzdem deutlich leiser als der Fließtext. */
:root[data-theme="light"] { --placeholder: #736b55; }
/* Auf hellem Grund müssen die Scanlines dunkeln statt aufzuhellen. */
:root[data-theme="light"] body::before {
    background: repeating-linear-gradient(to bottom, rgba(0, 0, 0, .022) 0 1px, transparent 1px 3px);
}

* { box-sizing: border-box; }

/* Die Kopfzeile klebt oben — ohne Polsterung landet ein angesprungenes Ziel
   (etwa „#about" von der Startseite aus) genau darunter und ist verdeckt. Gilt
   für Sprungmarken **und** für das Scrollen zum Fokus, deshalb am Wurzelelement
   statt als scroll-margin an jedem einzelnen Ziel. */
html { scroll-behavior: smooth; scroll-padding-top: 6rem; }

body {
    margin: 0;
    padding-bottom: calc(var(--prompt-h) + var(--debug-bar));
    font-family: var(--font);
    font-size: 15px;
    color: var(--text);
    background: var(--bg);
    line-height: 1.65;
    -webkit-font-smoothing: antialiased;
}

/* Ganz feine Scanlines — CRT-Andeutung, bewusst kaum sichtbar. */
body::before {
    content: "";
    position: fixed; inset: 0; z-index: 1; pointer-events: none;
    background: repeating-linear-gradient(to bottom, rgba(255, 255, 255, .016) 0 1px, transparent 1px 3px);
}

a { color: var(--cyan); text-decoration: none; }
a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* `height: auto` gehört zwingend dazu. Die Attribute `width`/`height` am <img>
   stehen für den Browser wie eine Regel im Stylesheet — nur mit der geringsten
   Kraft. Setzt hier eine Regel bloß die Breite, bleibt die **Höhe aus dem
   Attribut** stehen: das Profilbild kam so 260 Pixel breit und 1420 Pixel hoch
   heraus, und `aspect-ratio` half nicht, weil das nur bei automatischer Höhe
   greift. */
img { max-width: 100%; height: auto; display: block; }

h1, h2, h3, h4 { line-height: 1.3; margin: 1.4em 0 .5em; font-weight: 700; color: #eaeff8; overflow-wrap: anywhere; }
h1 { font-size: 1.5rem; margin-top: 0; }
h2 { font-size: 1.15rem; }
h3 { font-size: 1rem; }
/* Überschriften wie in einer Markdown-Datei, die man im Terminal liest.
   Die Rauten sind Schmuck fürs Auge — ein Screenreader soll sie nicht vorlesen
   („Nummernzeichen Nummernzeichen Über mich"). Deshalb überall die Schreibweise
   `content: "…" / ""`: der Teil hinter dem Schrägstrich ist der Alternativtext,
   und leer heißt „nichts ansagen". Die schmucklose Zeile davor bleibt als
   Rückfallebene stehen — ein Browser, der die Schreibweise nicht kennt, verwirft
   nur die zweite Zeile und zeigt weiterhin das Zeichen. Dasselbe Muster gilt
   unten für Knöpfe, Marken, Aufzählungen und Dateipfade. */
main h1::before { content: "# "; content: "# " / ""; color: var(--green); }
main h2::before { content: "## "; content: "## " / ""; color: var(--green); }
main h3::before { content: "### "; content: "### " / ""; color: var(--green-dim); }
main h4::before { content: "#### "; content: "#### " / ""; color: var(--green-dim); }

:focus-visible { outline: 2px solid var(--green); outline-offset: 1px; }
::selection { background: var(--green); color: var(--bg); }

.container { width: 100%; max-width: var(--maxw); margin-inline: auto; padding-inline: clamp(.9rem, 3.5vw, 1.6rem); }
.icf-svg { vertical-align: -0.18em; }
.muted { color: var(--text-muted); }
.stack > * + * { margin-top: .9rem; }

@keyframes blink { 0%, 49% { opacity: 1; } 50%, 100% { opacity: 0; } }
.cursor {
    display: inline-block; width: .55em; height: 1.05em; margin-left: .1em;
    background: var(--green); vertical-align: -.16em; animation: blink 1.1s steps(1) infinite;
}

/* Skip-Link: erst sichtbar, wenn er den Fokus bekommt. */
.skip-link {
    position: absolute; left: .5rem; top: -4rem; z-index: 400;
    padding: .5rem .8rem; background: var(--green); color: var(--bg);
    font-weight: 700; font-size: .85rem; transition: top .12s ease;
}
.skip-link:focus { top: .5rem; text-decoration: none; }
main:focus { outline: none; }

/* ---------- Kopfzeile: Terminaltitel + ASCII-Menü ---------- */
.site-header { position: sticky; top: 0; z-index: 50; background: var(--bg); border-bottom: var(--bw) solid var(--border); }
.site-header__inner { display: flex; align-items: center; gap: .7rem; flex-wrap: wrap; padding-block: .5rem; }
.brand { display: inline-flex; align-items: center; gap: .5rem; font-weight: 700; font-size: .88rem; color: var(--text); flex: none; }
.brand:hover { text-decoration: none; color: var(--green); }
.brand .icf-svg { color: var(--green); }
.term-dots { display: inline-flex; gap: .3rem; }
.term-dots span { width: 9px; height: 9px; border-radius: 50%; display: block; }
.term-dots span:nth-child(1) { background: #e0655f; }
.term-dots span:nth-child(2) { background: #e5c07b; }
.term-dots span:nth-child(3) { background: #52d97f; }
/* Die Fensterpunkte der Kopfzeile schalten das Farbschema. Jeder Punkt trägt die
   Akzentfarbe seines Schemas, fest — nicht `var(--green)`, sonst wären alle gleich.
   Der aktive bekommt einen Ring in der Textfarbe. */
.theme-dots { display: inline-flex; align-items: center; gap: .3rem; flex: none; margin-right: -.2rem; }
.theme-dot {
    width: 11px; height: 11px; padding: 0; border-radius: 50%; cursor: pointer;
    border: 1px solid transparent; background-clip: padding-box;
    transition: transform .12s ease;
}
.theme-dot:hover, .theme-dot:focus-visible { transform: scale(1.3); }
.theme-dot[aria-pressed="true"] { box-shadow: 0 0 0 2px var(--bg), 0 0 0 3px var(--text); }
.theme-dot--green { background: #52d97f; }
.theme-dot--amber { background: #ffb000; }
.theme-dot--cyan { background: #35d6e8; }
.theme-dot--mono { background: #e8edf4; }
.theme-dot--light { background: #f6f3e9; border-color: #8a8270; }
/* Auf Papiergrund verschwände der fast weiße Mono-Punkt ohne Rand. */
:root[data-theme="light"] .theme-dot--mono { border-color: #8a8270; }
/* Mit dem Finger sind 11 Pixel zu wenig: auf Touch-Geräten größer und weiter auseinander. */
@media (pointer: coarse) {
    .theme-dots { gap: .55rem; }
    .theme-dot { width: 16px; height: 16px; }
}
.term-title { font-size: .78rem; flex: 1; }
/* Rechtliches oben: eine dezente Zeile zwischen Marke und Uhr. */
.site-legal { margin: 0; flex: 1; display: flex; flex-wrap: wrap; gap: 0 .8rem; font-size: .75rem; min-width: 0; }
.site-legal a { color: var(--text-muted); }
.site-legal a:hover, .site-legal a:focus-visible { color: var(--green); }
.nav__legal { display: none; }
.term-clock { font-size: .78rem; flex: none; font-variant-numeric: tabular-nums; }

/* Das Menü als ASCII-Leiste: durchgezogene Kästen, ein Rahmen um alles. */
.nav {
    display: flex; align-items: stretch; flex-wrap: wrap;
    width: 100%; border: var(--bw) solid var(--border-strong);
}
.nav a, .nav__caret {
    display: inline-flex; align-items: center; gap: .4rem;
    padding: .3rem .7rem; font: inherit; font-size: .82rem;
    color: var(--text); background: transparent;
    border: 0; border-right: var(--bw) solid var(--border-strong); cursor: pointer;
}
.nav > a:last-child, .nav > .lang-switch:last-child { border-right: 0; }
.nav a:hover, .nav a:focus-visible, .nav__caret:hover, .nav__caret:focus-visible {
    text-decoration: none; background: var(--green); color: var(--bg);
}
.nav a:hover .icf-svg, .nav__caret:hover .icf-svg { color: var(--bg); }
.nav .icf-svg { color: var(--green-dim); }

/* ---------- Untermenü (Projekte) ---------- */
.nav__group { position: relative; display: inline-flex; align-items: stretch; }
.nav__group::after { content: ""; position: absolute; left: 0; right: 0; top: 100%; height: .4rem; }
.nav__group-link { border-right: var(--bw) solid var(--border-strong); }
.nav__caret { padding-inline: .45rem; }
.nav__submenu {
    position: absolute; top: calc(100% + .4rem); left: 0; z-index: 60;
    min-width: 250px; max-width: min(86vw, 340px); margin: 0; padding: 0; list-style: none;
    background: var(--bg); border: var(--bw) solid var(--border-strong);
    opacity: 0; visibility: hidden; transition: opacity .1s ease, visibility .1s;
}
.nav__group--open > .nav__submenu,
.nav__group:focus-within > .nav__submenu { opacity: 1; visibility: visible; }
@media (hover: hover) { .nav__group:hover > .nav__submenu { opacity: 1; visibility: visible; } }
/* Ausdrücklich zugeklappt (Pfeil, Esc): schlägt Fokus und hängendes :hover. */
.nav__group.nav__group--shut > .nav__submenu { opacity: 0; visibility: hidden; }
.nav__submenu li + li { border-top: var(--bw) solid var(--border); }
.nav__submenu a {
    display: flex; align-items: center; gap: .5rem; width: 100%;
    padding: .35rem .6rem; border: 0; font-size: .8rem;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.nav__submenu-all { border-top: var(--bw) solid var(--border-strong) !important; }

/* Sprachumschalter — sitzt als letzte Zelle im Menükasten. */
.lang-switch { display: inline-flex; margin-left: auto; border-left: var(--bw) solid var(--border-strong); }
.lang-switch a { border-right: var(--bw) solid var(--border-strong); font-size: .78rem; text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted); }
.lang-switch a:last-child { border-right: 0; }
.lang-switch a[aria-current="true"] { background: var(--surface-2); color: var(--green); }

/* ---------- Startvorgang ---------- */
.boot { display: none; }
:root[data-boot] .boot {
    display: block;
    position: fixed; inset: 0; z-index: 200;
    background: var(--bg);
    padding-top: 14vh;
    overflow: hidden;
}
/* Solange gebootet wird, bleibt die Seite darunter ruhig. */
:root[data-boot] body { overflow: hidden; }
.boot__line { font-size: .86rem; line-height: 1.7; color: var(--text-muted); white-space: pre-wrap; overflow-wrap: anywhere; }
.boot__ok { color: var(--green); }

/* ---------- Echo: der Befehl, der zu dieser Seite gehört ---------- */
.echo { margin: 1.4rem 0 1.6rem; font-size: .86rem; overflow-wrap: anywhere; }
.echo__user { color: var(--green); font-weight: 700; }
.echo__path { color: var(--blue); }
.echo__sign { color: var(--text-muted); }
.echo__cmd { color: var(--text); }

/* ---------- Vollbildsitzung (`ssh`) ---------- */
.session {
    position: fixed; inset: 0 0 var(--prompt-h) 0; z-index: 90;
    background: var(--bg); overflow: hidden;
}
:root.is-session body { overflow: hidden; }
/* Kopfzeile und Seiteninhalt treten für die Dauer der Sitzung ab. */
:root.is-session .site-header,
:root.is-session main,
:root.is-session .site-footer { display: none; }
.session__scroll { height: 100%; overflow-y: auto; padding-block: 1.2rem; scrollbar-width: thin; }
.session__block { margin-bottom: .6rem; }
.session__echo { margin: .9rem 0 .3rem; font-size: .86rem; overflow-wrap: anywhere; }

.video-stub {
    display: block; width: 100%; text-align: left; font: inherit; font-size: .85rem;
    padding: .5rem .7rem; margin: .4rem 0; cursor: pointer;
    background: var(--surface); border: var(--bw) solid var(--border-strong); color: var(--cyan);
}
.video-stub:hover { border-color: var(--green); color: var(--green); }

/* ---------- Hinweis beim Ziehen eines Bildes ---------- */
/* Nur sichtbar, solange ein Bild über der Seite hängt (body.dropping setzt der
   Prompt-Controller). Reine Anzeige — die Ereignisse fängt das Fenster ab. */
.drop-hint {
    position: fixed; inset: 0; z-index: 250;
    display: grid; place-items: center;
    background: rgba(0, 0, 0, .72);
    color: var(--green); font-size: 1rem; letter-spacing: .06em;
    pointer-events: none; opacity: 0; visibility: hidden;
    transition: opacity .12s linear;
}
.drop-hint::before {
    content: ''; position: absolute; inset: 1.2rem;
    border: var(--bw) dashed var(--green);
}
body.dropping .drop-hint { opacity: 1; visibility: visible; }

/* ---------- Videoplayer ---------- */
/* Ein <dialog> im Modalbetrieb liegt in der obersten Ebene — der z-index von
   früher wird nicht mehr gebraucht, auch nicht gegen die Symfony-Debug-Leiste.
   Der Dialog selbst füllt den Schirm und ist nur Rahmen; sichtbar ist das
   Fenster darin, dunkel dahinter der ::backdrop. Wichtig: `display` nur im
   geöffneten Zustand setzen, sonst schlägt es die Vorgabe des Browsers
   (`dialog:not([open]) { display: none }`) und der Player stünde immer offen. */
.player {
    max-width: 100%; max-height: 100%; width: 100%; height: 100%;
    margin: 0; padding: 1.2rem; border: 0; background: transparent;
    overflow: hidden;
}
.player[open] { display: grid; place-items: center; }
.player::backdrop { background: rgba(0, 0, 0, .78); }
.player__window { width: min(100%, 60rem); border: var(--bw) solid var(--border-strong); background: var(--bg); }
.player__bar { display: flex; align-items: center; gap: .6rem; padding: .45rem .7rem; background: var(--bg-soft); border-bottom: var(--bw) solid var(--border); font-size: .78rem; }
.player__stage { position: relative; aspect-ratio: 16/9; background: #000; }
.player__stage iframe, .player__stage video { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }

/* ---------- Eingabezeile (fest unten) ---------- */
.prompt-bar {
    /* Der hohe z-index gilt der Symfony-Debug-Leiste (99999): die Eingabezeile darf
       nie hinter ihr verschwinden. Für alles andere auf der Seite ist er ohne Belang. */
    position: fixed; left: 0; right: 0; bottom: var(--debug-bar); z-index: 100000;
    height: var(--prompt-h); display: flex; align-items: center;
    background: var(--bg-soft); border-top: var(--bw) solid var(--border);
    /* Die ganze Zeile ist Eingabefläche, nicht nur der Text darin. */
    cursor: text;
}
.prompt-bar:hover { border-top-color: var(--green-dim); }
.prompt-bar:focus-within { border-top-color: var(--green); }
.prompt-bar__inner { display: flex; align-items: center; gap: .45rem; font-size: .84rem; white-space: nowrap; }

/* Vorschläge über der Eingabezeile — nur für Finger.
   `pointer: coarse` fragt genau das, worum es geht: ein Zeigegerät ohne Tastatur.
   Über die Bildschirmbreite zu raten wäre falsch — ein schmales Fenster am
   Schreibtisch braucht die Reihe nicht, ein großes Tablet sehr wohl. Am
   Schreibtisch wäre sie Möblierung: dort funktioniert „einfach lostippen". */
.prompt-chips { display: none; }
@media (pointer: coarse) {
    /* Die Leiste ist sonst eine Zeile fester Höhe — mit der Vorschlagsreihe werden
       es zwei. `--prompt-h` mitzuwachsen zu lassen genügt: daran hängen auch der
       Abstand am Seitenende, die Höhe des Ausgabekastens und die Meldungen. */
    :root { --prompt-h: 5.2rem; }
    .prompt-bar { flex-direction: column; align-items: stretch; justify-content: center; }

    .prompt-chips {
        display: flex; gap: .4rem; padding-bottom: .45rem;
        /* Passt die Reihe nicht, wird sie geschoben statt umgebrochen: die
           Eingabezeile darf nicht wandern, während jemand tippt. */
        overflow-x: auto; scrollbar-width: none;
    }
    .prompt-chips::-webkit-scrollbar { display: none; }
    .prompt-chips__item {
        flex: none; display: inline-flex; align-items: center; gap: .3rem;
        padding: .3rem .6rem; font: inherit; font-size: .8rem;
        color: var(--text); background: var(--surface); cursor: pointer;
        border: var(--bw) solid var(--border-strong);
    }
    .prompt-chips__item:active { color: var(--bg); background: var(--green); }
}
.prompt-bar__prefix { display: inline-flex; flex: none; cursor: text; }
.prompt-bar__user { color: var(--green); font-weight: 700; }
.prompt-bar__path { color: var(--blue); }
.prompt-bar__sign { color: var(--text-muted); }
/* Das Eingabefeld soll wie Fließtext im Terminal wirken, nicht wie ein Formular.
   Die doppelte Klasse schlägt bewusst die generische input[type=text]-Regel. */
.prompt-bar input.prompt-bar__input {
    flex: 1 1 auto; min-width: 0; width: auto; font: inherit; font-size: inherit;
    color: var(--text); background: transparent; border: 0; padding: 0; caret-color: var(--green);
    white-space: pre; overflow: hidden; text-overflow: ellipsis; /* lange Befehle: von links lesbar, Ende mit … */
}
.prompt-bar input.prompt-bar__input:focus { outline: none; box-shadow: none; border: 0; }
.prompt-bar .cursor { flex: none; }
.prompt-bar:focus-within .cursor { display: none; }
/* Vorher --border-strong: eine Rahmenfarbe als Text ergab 1,95:1. Rahmenfarben
   sind für Linien gedacht, nicht zum Lesen. */
.prompt-bar__hint { flex: none; color: var(--text-muted); font-size: .74rem; }
.prompt-bar:focus-within .prompt-bar__hint { color: var(--green-dim); }

/* Ausgabe von `help` & Co. — klappt über der Zeile auf. */
.console-output {
    position: absolute; left: 0; right: 0; bottom: 100%;
    display: flex; flex-direction: column;
    background: var(--bg-soft); border-top: var(--bw) solid var(--border);
}
.console-output__body { max-height: min(58vh, 26rem); overflow-y: auto; padding: .7rem 0; scrollbar-width: thin; }
.console-output__body:focus { outline: none; }
/* Spielfeld: Wischen darf die Seite nicht scrollen — nur hier. */
.console-output__body--game { touch-action: none; overscroll-behavior: contain; }
/* Steuerkreuz nur bei Touch-Geräten; der Beenden-Knopf bleibt immer. */
.console-output__pad {
    display: grid; grid-template-columns: repeat(3, 3.25rem); grid-template-areas: ". up quit" "left down right";
    gap: .4rem; justify-content: center; align-items: center; padding: .5rem 0 .7rem;
}
.console-output__pad-key {
    min-width: 44px; min-height: 44px; font: inherit; font-size: 1.1rem;
    color: var(--green); background: var(--bg); border: var(--bw) solid var(--border); border-radius: 6px;
    touch-action: manipulation;
}
.console-output__pad-key--up { grid-area: up; }
.console-output__pad-key--left { grid-area: left; }
.console-output__pad-key--down { grid-area: down; }
.console-output__pad-key--right { grid-area: right; }
.console-output__pad-quit {
    grid-area: quit; min-width: 44px; min-height: 44px; font: inherit; font-size: .8rem;
    color: var(--red); background: var(--bg); border: var(--bw) solid var(--border); border-radius: 6px;
    touch-action: manipulation;
}
@media (pointer: fine) { .console-output__pad-key { display: none; } .console-output__pad { grid-template-columns: auto; grid-template-areas: "quit"; } }
/* Statuszeile des Pagers — sitzt außerhalb des Scrollbereichs, bleibt also stehen. */
.console-output__status {
    display: flex; gap: 1rem; justify-content: space-between;
    max-width: var(--maxw); width: 100%; margin-inline: auto;
    padding: .3rem clamp(.9rem, 3.5vw, 1.6rem);
    border-top: var(--bw) dashed var(--border);
    font-size: .76rem; color: var(--green-dim);
}
.console-output__line {
    max-width: var(--maxw); margin-inline: auto; padding-inline: clamp(.9rem, 3.5vw, 1.6rem);
    font-size: .8rem; line-height: 1.7; color: var(--text-muted);
    white-space: pre-wrap; overflow-wrap: anywhere;
    /* Eine leere Zeile ist eine Zeile. Ohne Mindesthöhe hat sie keine: ein leeres
       Element bekommt keine Zeilenbox und damit null Höhe — die Absätze in den
       Handbuchseiten klebten aneinander, und auch `help` und `neofetch` verloren
       ihre Gliederung. Erst der Rückfall, dann die genaue Angabe für Browser,
       die `lh` kennen. */
    min-height: 1.7em;
    min-height: 1lh;
}
/* Überschrift bzw. Fehlermeldung setzt der Controller ausdrücklich — nicht jede
   erste Zeile ist eine (siehe `history`, `ls`). */
.console-output__line--head { color: var(--green); }
.console-output__line--error { color: var(--red); }
/* Blockschrift (`banner`) braucht Zeilen ohne Luft, sonst zerfallen die Buchstaben. */
/* Bildschirmkunst: kein Umbruch, dafür schrumpfende Schrift. --art-cols setzt der
   Controller auf die Breite der breitesten Zeile; 0.62em ist die Zeichenbreite der
   Schreibmaschinenschrift. So passt die Lok auch aufs Telefon. */
.console-output__line--tight {
    line-height: 1;
    color: var(--green);
    white-space: pre;
    overflow-wrap: normal;
    font-size: min(.8rem, calc((100vw - 3.2rem) / (var(--art-cols, 64) * 0.62)));
}

/* ---------- Buttons ---------- */
.btn {
    display: inline-flex; align-items: center; justify-content: center; gap: .45rem;
    font: inherit; font-size: .84rem; cursor: pointer; text-decoration: none;
    padding: .35rem .7rem; border: var(--bw) solid var(--border-strong);
    background: transparent; color: var(--text);
    transition: background .1s ease, color .1s ease, border-color .1s ease;
}
.btn::before { content: "["; content: "[" / ""; color: var(--text-muted); }
.btn::after { content: "]"; content: "]" / ""; color: var(--text-muted); }
.btn:hover { text-decoration: none; background: var(--green); color: var(--bg); border-color: var(--green); }
.btn:hover::before, .btn:hover::after, .btn:hover .icf-svg { color: var(--bg); }
.btn--primary { border-color: var(--green); color: var(--green); }
.btn--primary::before, .btn--primary::after { color: var(--green-dim); }
.btn--block { width: 100%; }
/* Die leise Stufe: für Nebenwege („Übersicht", „Abbrechen", „Abmelden"), die
   neben einem [ Speichern ] nicht gleich laut auftreten sollen. Ohne diese Regel
   sah `btn--ghost` aus wie ein gewöhnlicher Knopf — die Abstufung, die die
   Vorlagen meinen, kam schlicht nicht an. */
.btn--ghost { border-color: var(--border); color: var(--text-muted); }
.btn--ghost::before, .btn--ghost::after { color: var(--border-strong); }
.btn--ghost:hover { background: var(--surface-2); color: var(--text); border-color: var(--border-strong); }
.btn--ghost:hover::before, .btn--ghost:hover::after { color: var(--text-muted); }
.btn--ghost:hover .icf-svg { color: var(--text); }
.btn--danger { border-color: var(--red); color: var(--red); }
.btn--danger:hover { background: var(--red); border-color: var(--red); }
.btn .icf-svg { color: inherit; }

/* ---------- ASCII-Kasten: ersetzt „Karten" ---------- */
.card, .ascii-box {
    position: relative; margin-top: 1.6rem;
    border: var(--bw) solid var(--border-strong); padding: 1.1rem;
}
/* Der Titel sitzt im oberen Rahmen — wie eine legend im ncurses-Dialog. */
.ascii-box__title {
    position: absolute; top: 0; left: .9rem; transform: translateY(-50%);
    padding-inline: .4rem; background: var(--bg);
    color: var(--green); font-size: .8rem; font-weight: 700;
}

/* ---------- Fließtext ---------- */
.prose { color: var(--text); }
.prose p { margin: 0 0 1rem; }
.prose ul, .prose ol { padding-left: 1.4rem; }
.prose li { margin-bottom: .25rem; }
.prose ul li::marker { content: "* "; content: "* " / ""; color: var(--green); }
.prose ol li::marker { color: var(--green); }
.prose a { color: var(--cyan); text-decoration: underline; text-underline-offset: 3px; }
.prose blockquote { margin: 1.1rem 0; padding-left: 1rem; border-left: 2px solid var(--green-dim); color: var(--text-muted); }
.prose code { background: var(--surface-2); border: var(--bw) solid var(--border); padding: .05em .35em; color: var(--yellow); font-size: .94em; }
.prose pre { background: var(--bg-soft); border: var(--bw) solid var(--border); padding: .9rem; overflow-x: auto; }
.prose pre code { background: none; border: 0; color: var(--text); }
.prose hr { border: 0; border-top: var(--bw) dashed var(--border-strong); margin: 1.8rem 0; }

/* ---------- Seitenbereiche ---------- */
main { padding-bottom: 1rem; }
.section { padding: 0 0 1.4rem; }
.section__kicker { margin: 0 0 .5rem; color: var(--text-muted); font-size: .8rem; overflow-wrap: anywhere; }
.section__kicker::before { content: "$ "; content: "$ " / ""; color: var(--green); font-weight: 700; }

/* Bilder bekommen einen Rahmen und darunter ihren „Pfad". */
.figure-shell { position: relative; margin: 1.2rem 0; }
/* Der Deckel, auf dem `decrypt image` entschlüsselt (profile_image_controller).
   Ort und Maße setzt der Controller — er kennt die Stelle des Bildes im Kasten;
   im Stylesheet stünde davon nur eine Zahl, die bei jedem Zuschnitt falsch wird. */
.decode-cover { position: absolute; z-index: 1; pointer-events: none; }
.figure-shell img { border: var(--bw) solid var(--border-strong); }
/* Das Schloss oben links am Profilbild — `decrypt image` ohne Konsole. Liegt
   über dem Deckel, damit es auch mitten in der Entschlüsselung klickbar bleibt. */
.figure-lock {
    position: absolute; top: .4rem; left: .4rem; z-index: 2;
    display: inline-flex; align-items: center; justify-content: center;
    width: 1.9rem; height: 1.9rem; padding: 0; cursor: pointer;
    font: inherit; font-size: .95rem; color: var(--green);
    background: var(--bg); border: var(--bw) solid var(--border-strong);
}
.figure-lock:hover { background: var(--green); color: var(--bg); border-color: var(--green); }
.figure-lock[aria-pressed="true"] .figure-lock__closed,
.figure-lock[aria-pressed="false"] .figure-lock__open { display: none; }
.figure-lock > span { display: inline-flex; }
.figure-shell figcaption, .file-label { margin-top: .35rem; color: var(--text-muted); font-size: .78rem; }
.file-label::before { content: "» "; content: "» " / ""; color: var(--green-dim); }

.hero { padding: 0; }
.hero__tagline { color: var(--text-muted); margin-top: -.2rem; }
/* Neuheiten-Kasten. Mobil: unter dem Bild, volle Breite, blätterbar (JS stapelt die
   Einträge in eine Rasterzelle, damit die Höhe des höchsten gilt und nichts springt).
   Desktop (ab 640px): rechts neben dem Bild, genauso hoch wie das Bild — der Rollbereich
   liegt absolut im Kasten und trägt nichts zur eigenen Höhe bei, die Zeile (align-items:
   stretch) gibt sie vor. Alle Einträge stehen untereinander, bei Bedarf scrollbar. */
.news-box {
    position: relative; margin-top: 1.2rem;
    background: var(--bg-soft); border: var(--bw) solid var(--border-strong);
    border-left: .25rem solid var(--green);
}
.news-box__scroll { padding: .8rem 1rem; }
.news-box__scroll:focus-visible { outline: 2px solid var(--green); outline-offset: -4px; }
.news-box__item + .news-box__item { margin-top: 1rem; padding-top: 1rem; border-top: var(--bw) dashed var(--border); }
.news-box__title { margin: 0 0 .3rem; color: var(--green); font-size: .82rem; text-transform: uppercase; letter-spacing: .04em; }
.news-box__title::before { content: "> "; content: "> " / ""; }
.news-box__title time { margin-left: .6rem; color: var(--text-muted); }
.news-box__text { margin: 0; overflow-wrap: anywhere; }
.news-box__link { display: inline-flex; align-items: center; gap: .4rem; min-height: 44px; margin-top: .2rem; color: var(--green); }
.news-box__nav { display: flex; align-items: center; justify-content: space-between; gap: .5rem; padding: 0 .4rem .4rem; }
.news-box__nav[hidden] { display: none; }
.news-box__btn {
    display: inline-flex; align-items: center; justify-content: center; width: 44px; height: 44px; padding: 0;
    font: inherit; color: var(--green); background: var(--bg); border: var(--bw) solid var(--border-strong); cursor: pointer;
}
.news-box__btn:hover { background: var(--green); color: var(--bg); border-color: var(--green); }
.news-box__count { color: var(--text-muted); font-size: .84rem; }
.news-box__dots { display: none; gap: .35rem; }
.news-box__dot { width: .5rem; height: .5rem; border: var(--bw) solid var(--border-strong); }
.news-box__dot.is-active { background: var(--green); border-color: var(--green); }
@media (max-width: 639.98px) {
    .news-box.is-carousel .news-box__slides { display: grid; touch-action: pan-y; }
    .news-box.is-carousel .news-box__item { grid-area: 1 / 1; margin: 0; padding: 0; border: 0; }
    .news-box.is-carousel .news-box__item[hidden] { display: block; }
    .news-box.is-carousel .news-box__item:not(.is-active) { visibility: hidden; }
    .news-box__dots { display: inline-flex; }
    .news-box__nav { justify-content: space-between; }
}
@media (min-width: 640px) {
    /* Die Zeile ist genau so hoch wie das Bild: die Bildunterschrift hängt absolut unter
       der Figur (Platz dafür als Rand der Zeile), die Figur hat keinen eigenen Rand. */
    .hero__row { display: flex; align-items: stretch; gap: 2rem; margin: 1.2rem 0 1.8rem; }
    .hero__row .figure-shell { margin: 0; flex: none; }
    .hero__row .figure-shell figcaption { position: absolute; top: 100%; left: 0; white-space: nowrap; }
    .news-box { flex: 1 1 0; min-width: 0; max-width: 32rem; margin-top: 0; }
    .news-box__scroll {
        position: absolute; inset: 0; overflow-y: auto; overscroll-behavior: contain;
        scrollbar-width: thin; scrollbar-color: var(--border-strong) transparent;
    }
    .news-box__scroll::-webkit-scrollbar { width: 8px; }
    .news-box__scroll::-webkit-scrollbar-thumb { background: var(--border-strong); }
    .news-box__item[hidden] { display: block; }
    .news-box__nav { display: none; }
}
/* Ohne festes Seitenverhältnis: die Porträts sind hochkant (578x800) und werden
   aus dem Klarbild erzeugt (`assets/images/build.php`) — quadratisch beschnitten
   fielen Schultern und Scheitel weg. Wer es doch quadratisch will, ergänzt
   `aspect-ratio: 1; object-fit: cover;`. */
.hero__photo { width: min(260px, 60vw); border: var(--bw) solid var(--border-strong); }

.chip { display: inline-flex; align-items: center; gap: .5rem; color: var(--text-muted); font-size: .84rem; }
.chip .icf-svg { color: var(--green); }
.contact__chips { display: flex; flex-direction: column; align-items: flex-start; gap: .3rem; margin: 1rem 0 0; }

/* ---------- Formulare im ASCII-Stil ---------- */
.field { margin-bottom: .9rem; }
.field label { display: block; font-size: .82rem; margin-bottom: .25rem; color: var(--text-muted); }
.field label::before { content: "› "; content: "› " / ""; color: var(--green); }
/* Die gemeinsame Überschrift über einem zweisprachigen Feldpaar. Sieht aus wie
   eine Feldbeschriftung, ist aber keine — siehe die Begründung in _macros. */
.field__label { display: block; font-size: .82rem; margin-bottom: .25rem; color: var(--text-muted); }
.field__label::before { content: "› "; content: "› " / ""; color: var(--green); }
.field .help, .help { color: var(--text-muted); font-size: .78rem; margin-top: .25rem; }
.input, .textarea, select,
input[type=text], input[type=email], input[type=password], input[type=url], textarea {
    width: 100%; font: inherit; font-size: .88rem; color: var(--text);
    background: var(--bg); border: var(--bw) solid var(--border-strong); padding: .4rem .55rem;
    transition: border-color .1s ease, box-shadow .1s ease;
}
select { padding-right: 2rem; }
::placeholder { color: var(--placeholder); }
.input:focus, select:focus, textarea:focus, input:focus { outline: none; border-color: var(--green); box-shadow: var(--glow); }
textarea { resize: vertical; }
.form-errors, .field .error { color: var(--red); font-size: .8rem; margin-top: .3rem; }
.form-errors ul { margin: 0; padding-left: 1.1rem; }
/* Das Formthema gibt die Fehler selbst als <ul class="form-errors"> aus — vorher
   war es eine Liste ganz ohne Klasse, Feldfehler standen dadurch in gewöhnlicher
   Textfarbe statt in Rot. Kein Aufzählungspunkt, sondern ein „!" im Terminalstil. */
ul.form-errors { list-style: none; margin: .3rem 0 0; padding: 0; }
ul.form-errors li::before { content: "! "; content: "! " / ""; font-weight: 700; }
.hp-field { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* ---------- Dropzone (Bild-Upload) ---------- */
.dropzone { border: var(--bw) dashed var(--border-strong); background: var(--bg); padding: 1.1rem; text-align: center; cursor: pointer; transition: border-color .1s ease, background .1s ease; }
.dropzone:hover { border-color: var(--green); background: var(--surface-2); }
.dropzone--over { border-color: var(--green); border-style: solid; background: var(--accent-soft); }
.dropzone__icon { color: var(--green); }
.dropzone__hint { color: var(--text-muted); font-size: .76rem; margin-top: .3rem; }
.dropzone--compact { padding: .7rem; }
.dropzone--compact strong { font-size: .84rem; }
.upload-previews { display: flex; flex-wrap: wrap; gap: .6rem; margin-top: .8rem; }
.upload-card { position: relative; width: 88px; height: 88px; overflow: hidden; border: var(--bw) solid var(--border-strong); }
.upload-card__preview { width: 100%; height: 100%; object-fit: cover; }
/* 24px ist das Mindestmaß aus WCAG 2.2 (2.5.8) — vorher waren es 22. */
.upload-card__remove { position: absolute; top: 2px; right: 2px; width: 24px; height: 24px; display: grid; place-items: center; cursor: pointer; font: inherit; border: var(--bw) solid var(--border-strong); background: var(--bg); color: var(--text); }
.upload-card__remove:hover { background: var(--red); color: var(--bg); }
.current-image { width: 88px; height: 88px; object-fit: cover; border: var(--bw) solid var(--border-strong); }

/* ---------- Meldungen ---------- */
.flashes { position: fixed; top: auto; bottom: calc(var(--prompt-h) + var(--debug-bar) + .5rem); right: 1rem; z-index: 100; display: flex; flex-direction: column; gap: .4rem; max-width: min(92vw, 420px); }
.flash { display: flex; align-items: center; gap: .5rem; padding: .5rem .8rem; font-size: .82rem; background: var(--bg-soft); border: var(--bw) solid var(--border-strong); }
.flash > span { flex: 1; }
/* Wegklicken: 24px Kantenlänge, damit der Knopf auch mit dem Daumen zu treffen ist. */
.flash__close {
    flex: none; display: grid; place-items: center; width: 24px; height: 24px;
    padding: 0; font: inherit; cursor: pointer;
    background: transparent; border: var(--bw) solid transparent; color: inherit;
}
.flash__close:hover { border-color: currentColor; }
.flash--success { border-color: var(--green); color: var(--green); }
.flash--error { border-color: var(--red); color: var(--red); }
.error-line { color: var(--red); font-size: .9rem; overflow-wrap: anywhere; }
.notice { display: flex; align-items: center; gap: .5rem; margin-bottom: 1.2rem; padding: .5rem .8rem; font-size: .82rem; background: var(--bg-soft); border: var(--bw) solid var(--yellow); color: var(--yellow); }

/* ---------- Auth ---------- */
.auth { min-height: 60vh; display: grid; place-items: center; padding: 2rem 0; }
.auth__card { width: 100%; max-width: 420px; }
.auth__divider { display: flex; align-items: center; gap: .7rem; color: var(--text-muted); font-size: .76rem; margin: 1.1rem 0; }
.auth__divider::before, .auth__divider::after { content: ""; height: 1px; flex: 1; background: var(--border); }
.passkey-status { color: var(--text-muted); font-size: .8rem; min-height: 1.2em; margin-top: .5rem; text-align: center; }

/* ---------- Badges ---------- */
.badge { display: inline-flex; align-items: center; gap: .25rem; font-size: .74rem; white-space: nowrap; color: var(--text-muted); }
.badge::before { content: "["; content: "[" / ""; }
.badge::after { content: "]"; content: "]" / ""; }
.badge--running { color: var(--green); }
.badge--planned { color: var(--blue); }
.badge--done { color: var(--text-muted); }
.badge--draft { color: var(--yellow); }
/* Bezugsquelle eines Projekts (Play Store, App Store, Quelltext …). Nur das
   Symbol, der Name steht für Screenreader daneben — in einer Zeile, die schon
   Rechte, Datum und Status trägt, ist für ein weiteres Wort kein Platz. */
.badge--store { color: var(--green-dim); }

/* ---------- Projektliste: sieht aus wie `ls -l` ---------- */
.ls { margin-top: 1rem; }
.ls__total { color: var(--text-muted); font-size: .82rem; margin-bottom: .4rem; }
.ls__row {
    display: grid; grid-template-columns: 10ch 8ch 12ch 1fr auto;
    gap: .8rem; align-items: baseline;
    padding: .25rem 0; color: var(--text); font-size: .85rem;
}
.ls__row:hover { background: var(--surface-2); text-decoration: none; }
.ls__mode { color: var(--green-dim); }
.ls__owner, .ls__date { color: var(--text-muted); }
.ls__name { color: var(--cyan); font-weight: 700; overflow-wrap: anywhere; }
.ls__row:hover .ls__name { text-decoration: underline; }
.ls__desc { margin: 0 0 .7rem; padding-left: calc(10ch + 8ch + 12ch + 2.4rem); color: var(--text-muted); font-size: .8rem; }
.empty-state { color: var(--text-muted); }
.empty-state__icon { color: var(--green-dim); margin-bottom: .3rem; }
.empty-state p { margin: .15rem 0; }

/* ---------- Download-Kasten ---------- */
.download-box { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: .8rem; }
.download-box__meta { display: flex; flex-wrap: wrap; align-items: baseline; gap: .6rem; min-width: 0; font-size: .86rem; }
.download-box__name { color: var(--cyan); font-weight: 700; overflow-wrap: anywhere; }

/* ---------- Projektseite ---------- */
.project-page { max-width: var(--maxw); }
.project-header__meta { display: flex; flex-wrap: wrap; align-items: center; gap: .6rem; margin: 0 0 .7rem; font-size: .8rem; color: var(--text-muted); }
.project-header__summary { color: var(--text-muted); }
.project-hero { width: 100%; border: var(--bw) solid var(--border-strong); margin: 1.3rem 0; }
.project-blocks { display: flex; flex-direction: column; gap: 1.6rem; margin-top: 1.6rem; }
.project-block { margin: 0; }
.project-block--image img { width: 100%; border: var(--bw) solid var(--border-strong); }
.project-block--image figcaption, .project-block__caption { margin-top: .4rem; color: var(--text-muted); font-size: .78rem; }
.project-block--image figcaption::before, .project-block__caption::before { content: "» "; content: "» " / ""; color: var(--green-dim); }
.video-frame { position: relative; aspect-ratio: 16/9; border: var(--bw) solid var(--border-strong); background: #000; }
.video-frame iframe, .video-frame video { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }
.video-fallback { display: flex; align-items: center; gap: .45rem; }
.link-card { display: flex; align-items: baseline; gap: .6rem; color: var(--text); font-size: .86rem; }
.link-card:hover { text-decoration: none; }
.link-card:hover strong { text-decoration: underline; }
.link-card__icon { color: var(--green); flex: none; }
.link-card__text { display: flex; flex-direction: column; min-width: 0; }
.link-card__text strong { color: var(--cyan); overflow-wrap: anywhere; }
.link-card__desc, .link-card__host { color: var(--text-muted); font-size: .8rem; }
.link-card__go { color: var(--text-muted); flex: none; }
.project-footer { display: flex; flex-wrap: wrap; align-items: center; gap: .8rem; margin-top: 2rem; padding-top: 1.1rem; border-top: var(--bw) dashed var(--border); }
.project-footer__others { color: var(--text-muted); font-size: .82rem; }

/* ---------- Admin ---------- */
.admin-grid { display: grid; grid-template-columns: 1.5fr 1fr; gap: 1.2rem; align-items: start; }
.admin-head { display: flex; align-items: center; justify-content: space-between; gap: .8rem; flex-wrap: wrap; margin-bottom: 1.1rem; }
/* Der Adminbereich stieg bisher mit <h2> ein, ganz ohne <h1> — die Gliederung
   begann also auf der zweiten Ebene. Die Überschrift ist jetzt eine erste, die
   Größe bleibt aber die alte: es ändert sich die Struktur, nicht das Bild. */
.admin-head h1, .auth__card h1 { font-size: 1.15rem; }
.admin-head__actions { display: flex; align-items: center; gap: .45rem; flex-wrap: wrap; }
.admin-head__actions form { margin: 0; }
.admin-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.admin-list__item { display: flex; align-items: center; gap: .8rem; padding: .5rem 0; border-bottom: var(--bw) dashed var(--border); }
.admin-list__thumb { display: grid; place-items: center; flex: 0 0 38px; width: 38px; height: 38px; overflow: hidden; border: var(--bw) solid var(--border); background: var(--bg); color: var(--green-dim); }
.admin-list__thumb img { width: 100%; height: 100%; object-fit: cover; }
.admin-list__main { display: flex; flex-direction: column; gap: .15rem; flex: 1; min-width: 0; }
.admin-list__title { color: var(--cyan); font-weight: 700; font-size: .9rem; overflow-wrap: anywhere; }
.admin-list__meta { display: flex; flex-wrap: wrap; align-items: center; gap: .45rem; font-size: .76rem; color: var(--text-muted); }
.admin-list__meta .muted { overflow-wrap: anywhere; }
.admin-list__tools { display: flex; align-items: center; gap: .25rem; }
.admin-list__tools form { margin: 0; }
.icon-btn { display: inline-grid; place-items: center; width: 30px; height: 30px; font: inherit; border: var(--bw) solid var(--border-strong); background: var(--bg); color: var(--text-muted); cursor: pointer; transition: border-color .1s ease, color .1s ease; }
.icon-btn:hover:not(:disabled) { color: var(--green); border-color: var(--green); text-decoration: none; }
.icon-btn:disabled { opacity: .3; cursor: default; }
.icon-btn--danger:hover:not(:disabled) { color: var(--red); border-color: var(--red); }
/* ---------- Tabelle (bisher nur der Papierkorb) ---------- */
/* Die Klasse stand in der Vorlage, ohne dass es je eine Regel dazu gab: die
   Tabelle kam nackt heraus und schob auf dem Telefon die ganze Seite waagerecht
   weg. Gerollt wird jetzt die Tabelle in ihrer Hülle, nicht das Dokument. */
.table-scroll { overflow-x: auto; margin-top: 1rem; scrollbar-width: thin; }
.table { width: 100%; border-collapse: collapse; font-size: .85rem; }
.table th, .table td { padding: .5rem .6rem; text-align: left; vertical-align: top; }
.table th {
    font-size: .76rem; letter-spacing: .05em; white-space: nowrap;
    color: var(--green-dim); border-bottom: var(--bw) solid var(--border-strong);
}
.table td { border-bottom: var(--bw) dashed var(--border); }
.table tbody tr:hover { background: var(--surface-2); }
/* Bündig mit dem übrigen Text der Seite beginnen und enden. */
.table th:first-child, .table td:first-child { padding-left: 0; }
.table th:last-child, .table td:last-child { padding-right: 0; }

.passkey-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; }
.passkey-item { display: flex; align-items: center; justify-content: space-between; gap: .7rem; padding: .45rem 0; border-bottom: var(--bw) dashed var(--border); font-size: .84rem; }
.passkey-item__meta { display: flex; align-items: center; gap: .5rem; }
.passkey-item__meta .icf-svg { color: var(--green); }

/* Admin: Baustein-Editor */
.block-list { display: flex; flex-direction: column; gap: .8rem; margin: .8rem 0; }
.block-list__empty { color: var(--text-muted); font-size: .82rem; margin: .4rem 0 .8rem; }
.block-row { border: var(--bw) solid var(--border-strong); background: var(--bg-soft); }
.block-row__head { display: flex; align-items: center; gap: .45rem; padding: .4rem .6rem; background: var(--surface); border-bottom: var(--bw) solid var(--border-strong); flex-wrap: wrap; }
.block-row__grip { color: var(--text-muted); cursor: grab; }
.block-row__head select { width: auto; min-width: 130px; }
.block-row__tools { margin-left: auto; display: flex; gap: .25rem; }
.block-row__fields { padding: .8rem; }
.block-row__fields .field:last-child { margin-bottom: 0; }
.block-add { display: flex; flex-wrap: wrap; gap: .45rem; }
.block-image { position: relative; }
.slug-field { display: flex; align-items: center; gap: .3rem; }
.slug-field__prefix { color: var(--green-dim); font-size: .78rem; white-space: nowrap; }
.field--check { display: flex; align-items: center; gap: .45rem; }
.field--check label { margin: 0; }
.field--check label::before { content: none; }
.field--check input[type=checkbox] { width: 1rem; height: 1rem; accent-color: var(--green); }

/* Formatierleiste über den HTML-Textfeldern */
.format-bar { display: flex; flex-wrap: wrap; gap: .2rem; margin-bottom: .3rem; }
.format-bar__btn {
    font: inherit; font-size: .72rem; line-height: 1;
    padding: .3rem .45rem; cursor: pointer;
    background: var(--bg); border: var(--bw) solid var(--border); color: var(--text-muted);
}
.format-bar__btn:hover { color: var(--green); border-color: var(--green); }

/* Zweisprachige Feldpaare (DE/EN nebeneinander) */
.i18n-pair { display: grid; grid-template-columns: 1fr 1fr; gap: .5rem; }
.i18n-pair__item { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
.i18n-pair__lang { align-self: flex-start; font-size: .68rem; letter-spacing: .06em; color: var(--text-muted); }
.i18n-pair__lang::before { content: "["; }
.i18n-pair__lang::after { content: "]"; }
.i18n-pair__item--en .i18n-pair__lang { color: var(--blue); }

[hidden] { display: none !important; }

/* ---------- Fußzeile ---------- */
.site-footer { border-top: var(--bw) dashed var(--border); margin-top: 2rem; padding: 1rem 0; color: var(--text-muted); font-size: .8rem; }
.site-footer__inner { display: flex; flex-wrap: wrap; gap: .7rem; align-items: center; justify-content: space-between; }
.site-footer a { color: var(--text-muted); }
.site-footer a:hover { color: var(--green); }

/* ---------- Responsive ---------- */
@media (max-width: 900px) {
    .admin-grid { grid-template-columns: 1fr; }
}
@media (max-width: 760px) {
    .i18n-pair { grid-template-columns: 1fr; }
    .admin-list__item { flex-wrap: wrap; }
    .admin-list__tools { width: 100%; justify-content: flex-end; }
    .project-footer { flex-direction: column; align-items: flex-start; }
    /* `ls -l` verliert die Spalten, die auf einem schmalen Terminal nur stören. */
    .ls__row { grid-template-columns: 1fr auto; gap: .5rem; }
    .ls__mode, .ls__owner, .ls__date { display: none; }
    .ls__desc { padding-left: 0; }
}
@media (max-width: 640px) {
    body { font-size: 14px; }
    .term-title { display: none; }
    /* Schmal: die Zeile zieht ins Burgermenü, die Kopfzeile bleibt ruhig. */
    .site-legal { display: none; }
    .nav__legal { display: flex; flex-wrap: wrap; gap: 0 .8rem; width: 100%; margin: 0; padding: .4rem .7rem; font-size: .75rem; border-top: var(--bw) solid var(--border-strong); }
    /* Der Hilfetext bleibt als Beschriftung in der Zeile stehen, kürzt sich aber, statt die Eingabe zu verdrängen. */
    .prompt-bar__hint { flex: 0 1 auto; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: .68rem; }
    .prompt-bar__input { min-width: 5ch; }
    .flashes { left: .6rem; right: .6rem; max-width: none; }
    .nav { flex-wrap: wrap; position: relative; }
    .nav a, .nav__caret { flex: 1 1 auto; justify-content: center; }
    /* Das Untermenü hing an seiner Menüzelle und stand mit min-width: 250px rechts
       aus dem Fenster heraus — die Seite ließ sich dadurch waagerecht schieben.
       Auf schmalen Schirmen hängt es deshalb am ganzen Menükasten und wird so breit
       wie dieser. */
    .nav__group { position: static; }
    .nav__submenu { left: 0; right: 0; min-width: 0; max-width: none; }
    .lang-switch { margin-left: 0; flex: 1 1 100%; border-left: 0; border-top: var(--bw) solid var(--border-strong); }
    .lang-switch a { flex: 1; justify-content: center; }
}
/* ---------- Burgermenü (schmale Schirme, nur mit JavaScript) ---------- */
.burger, .burger-backdrop { display: none; }
@media (max-width: 640px) {
    .has-burger { flex-wrap: nowrap; }
    .has-burger .term-clock { display: none; }
    .has-burger .burger {
        display: inline-flex; align-items: center; justify-content: center;
        position: relative; z-index: 3; margin-left: auto;
        min-width: 44px; min-height: 44px; padding: 0;
        font: inherit; color: var(--text); background: transparent;
        border: var(--bw) solid var(--border-strong); cursor: pointer;
    }
    .has-burger .burger:hover, .has-burger .burger:focus-visible { background: var(--green); color: var(--bg); }
    .has-burger .burger .icf-svg { color: currentColor; }
    .burger__close, .is-burger-open .burger__open { display: none; }
    .is-burger-open .burger__close { display: inline-flex; }
    .burger__open { display: inline-flex; }

    .has-burger .burger-backdrop {
        display: block; position: fixed; inset: 0; z-index: 1;
        background: color-mix(in srgb, var(--bg) 70%, transparent);
        opacity: 0; visibility: hidden; transition: opacity .15s ease, visibility .15s;
    }
    .has-burger.is-burger-open .burger-backdrop { opacity: 1; visibility: visible; }

    .has-burger .nav {
        position: fixed; top: 0; right: 0; bottom: 0; z-index: 2;
        width: min(86vw, 340px); padding: 4rem 0 1.5rem; overflow-y: auto;
        flex-direction: column; flex-wrap: nowrap; align-items: stretch;
        background: var(--bg); border: 0; border-left: var(--bw) solid var(--border-strong);
        transform: translateX(100%); visibility: hidden;
        transition: transform .18s ease, visibility .18s;
        overscroll-behavior: contain;
    }
    .has-burger.is-burger-open .nav { transform: none; visibility: visible; }
    .has-burger .nav a {
        flex: none; justify-content: flex-start; min-height: 48px; padding: .6rem 1rem;
        font-size: .95rem; border-right: 0; border-bottom: var(--bw) solid var(--border);
    }
    /* Der Pfeil entfällt: die Untermenüs stehen im Feld gleich aufgeklappt darunter. */
    .has-burger .nav__group { display: block; }
    .has-burger .nav__group::after { display: none; }
    .has-burger .nav__caret { display: none; }
    .has-burger .nav__group-link { border-right: 0; }
    .has-burger .nav__submenu,
    .has-burger .nav__group--shut > .nav__submenu {
        position: static; opacity: 1; visibility: visible; transition: none;
        min-width: 0; max-width: none; border: 0; background: transparent;
    }
    .has-burger .nav__submenu li + li { border-top: 0; }
    .has-burger .nav__submenu a {
        padding-left: 2.2rem; font-size: .88rem; white-space: normal; color: var(--text-muted);
    }
    .has-burger .nav__submenu-all { border-top: 0 !important; }
    .has-burger .lang-switch { flex: none; margin: 0; border-top: 0; display: flex; }
    .has-burger .lang-switch a { flex: 1; justify-content: center; border-right: var(--bw) solid var(--border); }
    .has-burger .lang-switch a:last-child { border-right: 0; }
}
/* Die Rechtszeile im Menü: klein und ohne Menüzellen-Optik. */
@media (max-width: 640px) {
    .nav .nav__legal a { flex: none; min-height: 0; padding: .35rem 0; border: 0; font-size: inherit; display: inline; color: var(--text-muted); }
    .has-burger .nav .nav__legal { margin-top: auto; }
}
html.burger-lock, html.burger-lock body { overflow: hidden; }
@media (max-width: 480px) {
    .prompt-bar__user, .prompt-bar__path { display: none; }
    .echo__user, .echo__path { display: none; }
}

/* Safari auf dem iPhone zoomt die ganze Seite heran, sobald ein angetipptes Feld
   kleiner als 16px gesetzt ist — bei einer festen Eingabezeile am unteren Rand
   ist das besonders lästig, weil die Zeile mit aus dem Bild wandert. Auf
   Tastgeräten laufen Felder deshalb auf glatten 16px, unabhängig von der
   Fensterbreite (das iPad zoomt genauso). An der Maus bleibt alles wie gehabt. */
@media (pointer: coarse) {
    .input, .textarea, select,
    input[type=text], input[type=email], input[type=password], input[type=url], textarea {
        font-size: 1rem;
    }
    /* Vorsatz und Eingabe müssen zusammen wachsen — sonst stehen im Prompt zwei
       verschieden große Schriften nebeneinander. */
    .prompt-bar__inner { font-size: 1rem; }
}

@media (prefers-reduced-motion: reduce) {
    html { scroll-behavior: auto; }
    * { transition: none !important; }
    .cursor { animation: none; }
}

/* Entfernen-Haken unter einer hinterlegten Datei — bewusst unauffällig, aber
   erreichbar. Erscheint nur, wenn überhaupt etwas zu entfernen ist. */
.field--remove {
    display: flex; align-items: center; gap: .45rem;
    margin: .5rem 0 0; font-size: .82rem; color: var(--text-muted);
}
.field--remove label { margin: 0; font-weight: normal; cursor: pointer; }
.field--remove input { margin: 0; accent-color: var(--red); }
.field--remove:hover label { color: var(--red); }

/* =========================================================================
   Fremde Bausteine im Terminalstil
   PhotoSwipe 5 (MIT) für die Galerie, Plyr 3 (MIT) für Videos — beide lokal
   ausgeliefert. Die Pakete bringen ihr eigenes, rundes und buntes Aussehen mit;
   hier wird es auf die Konsole zurückgestutzt: eckig, monospace, grün.
   ========================================================================= */

/* ---------- Galerie: Vorschau auf der Seite ---------- */
/* Der Link um das Bild ist die Rückfallebene ohne JavaScript — er soll aussehen
   wie ein anklickbares Bild, nicht wie ein Link. */
.project-block--image a[data-gallery-item],
.project-hero-figure a[data-gallery-item] {
    position: relative; display: block; text-decoration: none;
    border: var(--bw) solid var(--border);
}
.project-block--image a[data-gallery-item]:hover,
.project-hero-figure a[data-gallery-item]:hover,
.project-block--image a[data-gallery-item]:focus-visible,
.project-hero-figure a[data-gallery-item]:focus-visible {
    border-color: var(--green); text-decoration: none;
}
.project-hero-figure { margin: 0 0 1.2rem; }

/* „[ ansehen ]" wie eine Terminalschaltfläche, erst beim Überfahren. */
.zoom-hint {
    position: absolute; right: .5rem; bottom: .5rem;
    padding: .1rem .45rem; font-size: .74rem; letter-spacing: .04em;
    color: var(--green); background: var(--bg);
    border: var(--bw) solid var(--green);
    opacity: 0; transition: opacity .12s linear;
}
a[data-gallery-item]:hover .zoom-hint,
a[data-gallery-item]:focus-visible .zoom-hint { opacity: 1; }

/* ---------- Galerie: die geöffnete Ansicht ---------- */
.pswp { --pswp-bg: var(--bg); --pswp-icon-color: var(--green); --pswp-icon-color-secondary: var(--bg); }
.pswp__bg { background: var(--bg); }
/* Keine runden Ecken, kein Schlagschatten — ein Bild im Rahmen. */
.pswp__img { border: var(--bw) solid var(--border-strong); }
.pswp__button { border-radius: 0; }
.pswp__button:hover .pswp__icn, .pswp__button:focus-visible .pswp__icn { color: var(--accent-strong); }
.pswp__counter {
    font-family: var(--font); font-size: .8rem; color: var(--green-dim);
    opacity: 1; letter-spacing: .05em;
}
.pswp__counter::before { content: "["; content: "[" / ""; margin-right: .3rem; }
.pswp__counter::after { content: "]"; content: "]" / ""; margin-left: .3rem; }
/* Eigene Beschriftungsleiste (siehe gallery_controller). */
.pswp__caption {
    position: absolute; left: 0; right: 0; bottom: 0; z-index: 10;
    padding: .5rem clamp(.9rem, 3.5vw, 1.6rem);
    font-family: var(--font); font-size: .82rem; color: var(--text-muted);
    text-align: center; background: var(--bg-soft);
    border-top: var(--bw) solid var(--border);
    pointer-events: none;
}
.pswp__caption:empty { display: none; }

/* ---------- Videoplayer ---------- */
.plyr {
    --plyr-color-main: var(--green);
    --plyr-video-background: var(--bg);
    --plyr-video-control-color: var(--text);
    --plyr-video-control-color-hover: var(--bg);
    --plyr-video-control-background-hover: var(--green);
    --plyr-audio-control-color: var(--text);
    --plyr-menu-background: var(--surface);
    --plyr-menu-color: var(--text);
    --plyr-menu-border-color: var(--border-strong);
    --plyr-menu-arrow-color: var(--green-dim);
    --plyr-tooltip-background: var(--surface);
    --plyr-tooltip-color: var(--green);
    --plyr-font-family: var(--font);
    --plyr-font-size-small: .74rem;
    --plyr-font-size-base: .8rem;
    --plyr-font-size-time: .78rem;
    --plyr-range-track-height: 3px;
    --plyr-range-thumb-height: 11px;
    --plyr-range-thumb-shadow: none;
    --plyr-control-radius: 0;
    --plyr-badge-background: var(--green-dim);
    --plyr-captions-background: var(--bg-soft);
}
/* Eckig bleibt eckig — Plyr rundet an mehreren Stellen selbst.
   Achtung: die Hersteller-Pseudoelemente müssen **jedes für sich** stehen. Steht
   ein Selektor in der Liste, den der Browser nicht kennt (Chrome kennt kein
   ::-moz-range-thumb), verwirft er die komplette Regel — genau daran ist der
   große Abspielknopf zuerst rund geblieben. */
.plyr, .plyr__control, .plyr__menu__container, .plyr__tooltip { border-radius: 0 !important; }
.plyr__progress input[type="range"]::-webkit-slider-thumb { border-radius: 0 !important; }
.plyr__progress input[type="range"]::-moz-range-thumb { border-radius: 0 !important; }
.plyr__volume input[type="range"]::-webkit-slider-thumb { border-radius: 0 !important; }
.plyr__volume input[type="range"]::-moz-range-thumb { border-radius: 0 !important; }

/* Der große Abspielknopf: ein Kasten mit grünem Rahmen, kein Kreis. */
.plyr .plyr__control--overlaid {
    border: var(--bw) solid var(--green); border-radius: 0 !important;
    background: var(--overlay); color: var(--green);
    padding: .8rem 1rem;
}
.plyr .plyr__control--overlaid:hover,
.plyr .plyr__control--overlaid:focus-visible { background: var(--green); color: var(--bg); }
.plyr__time { font-variant-numeric: tabular-nums; }

/* Die Steuerleiste sitzt in einem Kasten wie alles andere hier — deckend, nicht
   als Verlauf über dem Bild (Plyrs Vorgabe ist spezifischer, daher .plyr davor). */
.video-frame .plyr { border: var(--bw) solid var(--border-strong); }
.plyr--video .plyr__controls, .plyr .plyr__controls {
    background: var(--bg-soft); border-top: var(--bw) solid var(--border);
    padding: .35rem .5rem;
}

/* Der 16:9-Rahmen oben positioniert `video` absolut — das gilt für den nackten
   Player ohne JavaScript. Sobald Plyr übernommen hat, schiebt es zwei eigene
   Hüllen dazwischen; die Regel griffe dann gegen Plyrs Hülle statt gegen den
   Rahmen, und der Player klappte auf null Höhe zusammen. Also: hat Plyr
   übernommen, füllt *es* den Rahmen, und das <video> darin wird wieder normal. */
.video-frame .plyr { position: absolute; inset: 0; width: 100%; height: 100%; }
.video-frame .plyr__video-wrapper { height: 100%; }
.video-frame .plyr video {
    position: static; width: 100%; height: 100%; object-fit: contain;
}

/* ---------- Verweis auf einen eigenen Download ---------- */
/* Sieht aus wie ein Eintrag in einer Dateiliste: Symbol, Name, Größe. */
.dl-card {
    display: flex; align-items: center; gap: .8rem;
    padding: .7rem .9rem; margin: .9rem 0;
    background: var(--surface); border: var(--bw) solid var(--border-strong);
    color: var(--text); text-decoration: none;
}
.dl-card:hover, .dl-card:focus-visible {
    border-color: var(--green); text-decoration: none; background: var(--surface-2);
}
.dl-card__icon { color: var(--green); flex: 0 0 auto; }
.dl-card__text { display: flex; flex-direction: column; gap: .1rem; min-width: 0; flex: 1 1 auto; }
.dl-card__text strong { overflow-wrap: anywhere; }
.dl-card__desc { font-size: .82rem; color: var(--text-muted); overflow-wrap: anywhere; }
.dl-card__meta { font-size: .76rem; color: var(--green-dim); overflow-wrap: anywhere; }
.dl-card__go { color: var(--green-dim); flex: 0 0 auto; }
.dl-card:hover .dl-card__go { color: var(--green); }

/* Bekannte Ziele (Play Store, App Store, F-Droid …) heben sich leicht ab. */
.link-card--store .link-card__icon { color: var(--cyan); }
.link-card--store:hover { border-color: var(--cyan); }

/* Nur für Screenreader: sichtbar für Hilfsmittel, nicht für das Auge.
   Wird für die Ansage benutzt, wenn ein Baustein die Position wechselt. */
.visually-hidden {
    position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0;
    overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0;
}

/* Sortieren per Ziehen: der Griff zeigt, dass er einer ist. */
.block-row__grip { cursor: grab; }
.block-row__grip:active { cursor: grabbing; }
.block-row--dragging { opacity: .45; outline: var(--bw) dashed var(--green); }

/* Die Ablagefläche ist ein <label> um ein optisch verborgenes Dateifeld. Bekommt
   das Feld den Fokus, muss man das der Fläche ansehen — sonst tappt man beim
   Tabben im Dunkeln. */
.dropzone:has(input:focus-visible) {
    outline: 2px solid var(--green);
    outline-offset: 2px;
    border-color: var(--green);
}
.dropzone { cursor: pointer; }

/* ---------- Medienübersicht ---------- */
.media-filter { display: flex; gap: .3rem; margin: 1rem 0; flex-wrap: wrap; }
.media-filter__tab {
    padding: .25rem .8rem; font-size: .82rem; color: var(--text-muted);
    border: var(--bw) solid var(--border);
}
.media-filter__tab:hover { text-decoration: none; border-color: var(--green); color: var(--green); }
.media-filter__tab.is-active { background: var(--surface-2); color: var(--green); border-color: var(--green); }

.media-grid { list-style: none; margin: .8rem 0 1.8rem; padding: 0; display: grid; gap: .6rem; }
.media-card {
    display: grid; grid-template-columns: 76px 1fr auto; gap: .8rem; align-items: start;
    padding: .6rem; background: var(--surface); border: var(--bw) solid var(--border);
}
/* Verwaistes fällt auf — aber gestrichelt statt schreiend rot. */
.media-card--orphan { border-style: dashed; border-color: var(--border-strong); }
.media-card__preview {
    width: 76px; height: 60px; display: grid; place-items: center; overflow: hidden;
    background: var(--bg); border: var(--bw) solid var(--border);
}
.media-card__preview img { width: 100%; height: 100%; object-fit: cover; }
.media-card__icon { color: var(--green-dim); }
.media-card__body { min-width: 0; }
.media-card__name { margin: 0; font-size: .84rem; overflow-wrap: anywhere; }
.media-card__meta { margin: .15rem 0 .35rem; font-size: .76rem; color: var(--text-muted); }
.media-card__usage { margin: 0; padding-left: 1.1rem; font-size: .78rem; color: var(--text-muted); }
.media-card__usage--none { padding-left: 0; list-style: none; color: var(--orange); }
.media-card__locked { color: var(--text-muted); padding: .35rem; display: inline-flex; }
/* Dritte Spalte der Karte: die Werkzeuge standen ohne eigene Regel als Blöcke
   untereinander. Nebeneinander, oben bündig mit dem Namen. */
.media-card__tools { display: flex; align-items: flex-start; gap: .25rem; }
.media-card__tools form { margin: 0; }

@media (max-width: 560px) {
    .media-card { grid-template-columns: 56px 1fr auto; }
    .media-card__preview { width: 56px; height: 46px; }
}
/* Der Hinweis über verwaiste Dateien trägt einen Knopf — Text links, Knopf rechts. */
.notice--warn { justify-content: space-between; flex-wrap: wrap; gap: .8rem; }

/* ---------- Bild aus dem Archiv wählen ---------- */
.image-slot { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; margin-top: .6rem; }
.image-slot__chosen {
    display: flex; align-items: center; gap: .45rem;
    padding: .25rem .45rem; font-size: .76rem; color: var(--text-muted);
    background: var(--surface-2); border: var(--bw) solid var(--green);
}
.image-slot__chosen img { width: 32px; height: 24px; object-fit: cover; }
.btn--small { padding: .25rem .6rem; font-size: .78rem; }

/* <dialog> bringt Fokusfang, Escape und Hintergrund selbst mit. */
.picker {
    width: min(56rem, 92vw); max-height: 82vh; padding: 1rem;
    background: var(--surface); color: var(--text);
    border: var(--bw) solid var(--border-strong); border-radius: 0;
}
.picker::backdrop { background: rgba(0, 0, 0, .7); }
.picker__head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: .8rem; }
.picker__grid {
    list-style: none; margin: 0; padding: 0; overflow-y: auto; max-height: 60vh;
    display: grid; grid-template-columns: repeat(auto-fill, minmax(120px, 1fr)); gap: .5rem;
}
.picker__item {
    display: block; width: 100%; padding: .3rem; cursor: pointer; font: inherit; text-align: left;
    background: var(--bg); border: var(--bw) solid var(--border); color: var(--text-muted);
}
.picker__item:hover, .picker__item:focus-visible { border-color: var(--green); color: var(--green); }
.picker__item img { width: 100%; height: 82px; object-fit: cover; margin-bottom: .3rem; }
.picker__caption { display: block; font-size: .72rem; overflow-wrap: anywhere; }
.picker__caption em { display: block; font-style: normal; color: var(--green-dim); }

/* ---------- Vorschau: so sieht der geteilte Link aus ---------- */
.share-card {
    display: flex; gap: .7rem; align-items: stretch; max-width: 32rem;
    margin: .2rem 0 .8rem; padding: .5rem;
    background: var(--bg-soft); border: var(--bw) solid var(--border);
}
.share-card__image {
    flex: 0 0 96px; height: 68px; display: grid; place-items: center; overflow: hidden;
    background: var(--bg); border: var(--bw) solid var(--border);
}
.share-card__image img { width: 100%; height: 100%; object-fit: cover; }
.share-card__body { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.share-card__host { font-size: .7rem; color: var(--text-muted); text-transform: uppercase; letter-spacing: .06em; }
.share-card__text { font-size: .78rem; color: var(--text-muted); overflow-wrap: anywhere; }
/* Kommt der Text aus der Kurzbeschreibung statt aus dem eigenen Feld: kursiv. */
.share-card__text.is-fallback { font-style: italic; }
.share-card__count { font-size: .72rem; color: var(--green-dim); }
.share-card__count.is-warn { color: var(--orange); }

/* ---------- Hochladen und Zuschneiden im Archiv ---------- */
.media-upload {
    display: flex; align-items: center; gap: .6rem; flex-wrap: wrap;
    margin: 1rem 0; padding: .6rem .8rem;
    background: var(--surface); border: var(--bw) dashed var(--border-strong);
}
.media-upload label { font-size: .82rem; color: var(--text-muted); }

.cropper {
    width: min(60rem, 94vw); padding: 1rem;
    background: var(--surface); color: var(--text);
    border: var(--bw) solid var(--border-strong); border-radius: 0;
}
.cropper::backdrop { background: rgba(0, 0, 0, .78); }
.cropper__head { display: flex; align-items: center; justify-content: space-between; gap: 1rem; margin-bottom: .7rem; }
/* Die Bühne: das Bild mittig, darüber das Auswahlrechteck. */
.cropper__stage {
    position: relative; display: grid; place-items: center;
    max-height: 56vh; overflow: hidden; user-select: none;
    background: var(--bg); border: var(--bw) solid var(--border); cursor: crosshair;
}
.cropper__stage img { max-width: 100%; max-height: 56vh; object-fit: contain; }
.cropper__box {
    position: absolute; pointer-events: none;
    border: 1px dashed var(--green);
    box-shadow: 0 0 0 9999px rgba(0, 0, 0, .5);
}
.cropper__controls { display: flex; justify-content: space-between; gap: 1rem; flex-wrap: wrap; margin: .7rem 0 .5rem; }
.cropper__numbers { display: flex; gap: .5rem; flex-wrap: wrap; }
.cropper__numbers label { display: flex; align-items: center; gap: .3rem; font-size: .76rem; color: var(--text-muted); }
.cropper__numbers input { width: 5.5rem; }
.cropper__actions { display: flex; align-items: center; gap: .6rem; flex-wrap: wrap; }
/* Betriebswarnung — dringender als der gelbe Hinweis, deshalb rot. */
.notice--danger { border-color: var(--red); color: var(--red); }

/* ---------- Einscrollen wie Terminalausgabe ----------
   Die Klasse `scroll-in` setzt ein kleines Skript im <head> — nur beim echten
   Aufruf (nicht beim Zurückblättern, nicht bei reduzierter Bewegung, nicht
   während des Startvorgangs). Die Blöcke der Seite steigen nacheinander um ein
   paar Pixel von unten in ihre Zeile: nur transform und opacity, also kein
   Layoutsprung. Gesamtdauer: höchstens 12 × 30 ms Staffelung + 220 ms = 580 ms.
   `backwards` statt `both`: danach gilt wieder der ganz normale Stil, und
   nichts bleibt hängen, falls die Animation je nicht läuft. Der Inhalt bleibt
   die ganze Zeit im Zugänglichkeitsbaum — es ändert sich nur das Aussehen. */
@keyframes term-in {
    from { opacity: 0; transform: translateY(14px); }
    to { opacity: 1; transform: none; }
}
@media (prefers-reduced-motion: no-preference) {
    .scroll-in main .container > :not(section),
    .scroll-in main section > * {
        animation: term-in 220ms ease-out backwards;
        animation-delay: 360ms;
    }
    .scroll-in main .container > :nth-child(1), .scroll-in main section > :nth-child(1) { animation-delay: 0ms; }
    .scroll-in main .container > :nth-child(2), .scroll-in main section > :nth-child(2) { animation-delay: 30ms; }
    .scroll-in main .container > :nth-child(3), .scroll-in main section > :nth-child(3) { animation-delay: 60ms; }
    .scroll-in main .container > :nth-child(4), .scroll-in main section > :nth-child(4) { animation-delay: 90ms; }
    .scroll-in main .container > :nth-child(5), .scroll-in main section > :nth-child(5) { animation-delay: 120ms; }
    .scroll-in main .container > :nth-child(6), .scroll-in main section > :nth-child(6) { animation-delay: 150ms; }
    .scroll-in main .container > :nth-child(7), .scroll-in main section > :nth-child(7) { animation-delay: 180ms; }
    .scroll-in main .container > :nth-child(8), .scroll-in main section > :nth-child(8) { animation-delay: 210ms; }
    .scroll-in main .container > :nth-child(9), .scroll-in main section > :nth-child(9) { animation-delay: 240ms; }
    .scroll-in main .container > :nth-child(10), .scroll-in main section > :nth-child(10) { animation-delay: 270ms; }
    .scroll-in main .container > :nth-child(11), .scroll-in main section > :nth-child(11) { animation-delay: 300ms; }
    .scroll-in main .container > :nth-child(12), .scroll-in main section > :nth-child(12) { animation-delay: 330ms; }
}
