/* =====================================================================
   Apewars – handgeschriebenes Stylesheet (kein Framework, kein Build).
   Modernes CSS: Custom Properties, Flexbox, Grid, clamp().
   ===================================================================== */

:root {
    /* SCHRIFTSKALA. Fünf Stufen, aus denen gewählt wird – statt für jede Stelle eine
       neue Zahl zu erfinden. Am 18.08.2026 standen 38 verschiedene rem-Werte in dieser
       Datei, allein sechzehn davon zwischen 0,70 und 1,00. Solche Unterschiede sieht
       man einzeln nicht, in Summe wirken sie unruhig.

       Die Stufen sind bewusst weit auseinander (rund 15 % Abstand): Nur so ist ein
       Unterschied auch als Absicht erkennbar. Wer eine neue Größe braucht, nimmt die
       nächstgelegene Stufe – und wenn wirklich keine passt, ändert er die Skala hier,
       nicht die einzelne Regel.

       Neue Regeln nutzen var(--fs-*); die Altbestände werden nach und nach umgezogen. */
    --fs-2xs: .625rem;  /* 10px – Etiketten in Großbuchstaben mit weitem Zeichenabstand */
    --fs-xs:  .6875rem; /* 11px – Mikroschrift: Prozente unter Loot-Rahmen, Zähler */
    --fs-s:   .75rem;   /* 12px – Kleintext: Kacheln, Hinweise, Fußzeilen */
    --fs-m:   .8125rem; /* 13px – dichter Text: Tabellen, Plaketten */
    --fs-l:   .875rem;  /* 14px – STANDARD. Alles, was normaler Fließtext ist. */
    --fs-xl:  1rem;     /* 16px – hervorgehobene Zahlen, kleine Überschriften */
    --fs-2xl: 1.25rem;  /* 20px – große Zahlen (Bilanzen, Belohnungen) */
    --fs-3xl: 1.75rem;  /* 28px – Schaustücke: Titel der Startseite, Kampfergebnis */

    /* ABSTAND ZWISCHEN FENSTERN – eine Zahl für die ganze Seite.
       --luecke ist der SICHTBARE Spalt. Gemessen wird aber von Rahmen zu Rahmen,
       und ein Titel- oder Reiter-Badge ragt über den oberen Kartenrand hinaus.
       Ohne Ausgleich blieben von 16px nur 5px übrig – deshalb --badge-ueber.
       Die 11px sind gemessen, nicht die -13px aus .card-title geschätzt: Der
       sichtbare Überstand fiel an 15 Karten auf 6 Seiten übereinstimmend auf 11px. */
    --luecke: 1rem;

    /* GEOMETRIE DER PUPPE – eine Quelle für die Puppe UND die Kopfzeile darüber.
       Standen vorher nur im Media-Block bei .paperdoll; die Kopfzeile braucht
       dieselben Zahlen, um exakt so breit zu sein wie Charakterbild + Slots.

       --puppe-breite = Bild + 4 Slot-Spalten + die Lücken dazwischen:
         .eq-left/.eq-right haben je .3rem zwischen ihren beiden Spalten,
         .paperdoll .5rem zwischen den Blöcken, .char-center .15rem Rand.
       Die Fläschchen zählen NICHT mit – sie flankieren die Puppe außen. */
    --char-h: 224px;    /* gemessene Höhe UND Breite des Charakterbildes */
    --eq-gap: .5rem;
    --puppe-breite: calc(var(--char-h) + 4 * (var(--char-h) - 2 * var(--eq-gap)) / 3
                         + 2 * (.3rem + .5rem + .15rem));

    /* EIN Wert für den Abstand über UND unter der Kopfzeile. */
    --kopf-luft: 10px;
    --badge-ueber: 11px;

    /* WIEDERKEHRENDE FARBEN. Kein neuer Ton, nur ein Name: Diese sieben standen
       zusammen 87-mal wörtlich im Blatt. Wer das Gold ändern will, ändert es sonst
       an 29 Stellen – und übersieht drei.

       Die Paare sind KEINE Dubletten, sondern zwei Untergründe: --gut gilt auf
       dunklem Grund, --gut-plakette auf der braunen Plakette (dort gemessene 8,69:1;
       der andere Ton käme auf 1,06:1 und wäre unsichtbar). Wer sie zusammenlegt,
       macht Text unlesbar – nicht bloß anders. */
    --gold: #e0a020;              /* Akzent: Rahmen, Hervorhebungen */
    /* HELLES Gold auf dunklem Grund – Sterne, Zahlen, aktive Ränder, Hervorhebungen.
       Acht fast gleiche Töne standen dafür einzeln im Blatt (#ffd97a, #ffd23a,
       #ffc94a, #ffd24a, #ffd98a, #f0c060, #f4d9a0, #e8b84a). Alle lagen zwischen
       10,0:1 und 13,6:1 auf dunkel, waren also nie aus Kontrastgründen verschieden –
       sie sind einfach nacheinander entstanden. */
    --gold-hell: #ffd97a;
    --dunkel: #2b2620;            /* Innenkante dunkler Kästen */
    --tief: #17110a;              /* tiefe Kante, Schattenlinie */
    --gut: #7bd06a;               /* positiv auf dunklem Grund */
    --gut-plakette: #a6e86b;      /* positiv auf brauner Plakette */
    /* Positiv auf HELLEM Grund. Zwei Töne meinten das bisher: #1f5910 (Arena, Beute,
       Set-Liste) und #1f5214 (Statusstreifen). Dieser hier ist der bessere von beiden –
       gemessen 4,98:1 auf Pergament und 5,97:1 auf dem Statusstreifen gegen 4,54 und
       5,44 des anderen. Unterscheiden konnte man sie ohnehin nicht. */
    --gut-pergament: #1f5214;
    /* Gold für HELLEN Grund. --gold-hell (#ffd97a) ist fürs dunkle Braun gemacht
       und liegt auf Pergament bei 1,2:1. Gemessen: 5,4:1 auf dem Kartengrund,
       4,6:1 auf der grün getönten Belohnungskachel. Gegenstück zu --gut-pergament. */
    --gold-pergament: #6f4e0a;
    /* NEGATIV, Schrift auf DUNKLEM Grund. Der Wert kommt von #ff8a80, das an vier
       Stellen einzeln stand – es ist der bessere: 8,07:1 auf dunkel gegen 5,98:1 des
       früheren #ff5a4d. Zwei weitere Töne meinten dasselbe (#ff9a8f, #f08a7a) und
       sind hier aufgegangen; unterscheiden konnte man sie nicht. */
    --schlecht: #ff8a80;
    /* NEGATIV auf HELLEM Grund (Pergament). Drei Werte meinten das: #8a2f27 (fünfmal,
       4,50:1 – genau auf der Kante), #b8341f (dreimal, 3,19:1 – DURCHGEFALLEN, das war
       der „verloren"-Text im Kampfergebnis) und #7c2a1a (5,14:1). Der beste gewinnt. */
    --schlecht-hell: #7c2a1a;
    /* NEGATIV auf der BRAUNEN PLAKETTE – Gegenstück zu --gut-plakette, gleiche Logik
       wie dort. Am echten Kostenknopf der Schamanenhütte gemessen (Grund rgb(90,63,34)):
       --schlecht (#ff8a80) kommt dort nur auf 4,24:1 und liegt damit unter der
       Schwelle, --schlecht-flaeche auf 1,82:1. Dieser Ton erreicht 4,98:1. */
    --schlecht-plakette: #ffa199;
    --schlecht-flaeche: #b8443f;  /* negativ, Fläche */

    --sky: #141414;   /* aus dem unteren Rand von img/bg.jpg gemessen (2026-08-12, neues Bild) */
    --parch: #e8d5b7;
    --ink: #241a0e;
    --ink-soft: #6b5236;
    --wood: #6b4f2a;
    --holz-dunkel: #5a3f22;       /* Holzrahmen und .btn.ghost – zweimal derselbe Wert */
    /* Dunkle Plakette: gibt Schrift einen eigenen, gleichmäßigen Grund, wenn sie sonst
       auf Textur, Vignette oder Rahmenlaub läge. Kampfergebnis und Level-up-Feier. */
    --plakette: linear-gradient(160deg, #322619, #1c140c);

    /* CHANCEN-SKALA der Dungeons (Siegaussicht). Eigene Rolle, kein Zufall: Jede Stufe
       steht zweimal im Blatt – einmal als Schrift, einmal als Rand der Plakette. Als
       Token ändert man sie an EINER Stelle, statt beide Vorkommen zu suchen.
       „leicht" fehlt hier bewusst: dort gilt das normale --gut. */
    --chance-machbar: #c8d94a;
    --chance-riskant: #f0a03c;
    --chance-aus: #ff6b5c;
    /* Aktionsknopf. Der Ton ist 9 % dunkler als das ursprüngliche #4d9139: Weiß darauf
       kam auf 3,87:1 und lag damit unter dem Schwellwert 4,5:1 – die Knopfschrift ist
       13px fett, die Ausnahme für Großschrift (ab 18,66px fett) greift nicht. Jetzt
       sind es 4,55:1, und der Unterschied ist mit bloßem Auge nicht zu sehen. */
    --green: #468434;
    --green-d: #346927;

    /* Braune Box (Original: img/bg_braune_box.jpg). --box-bg = Mittelwert der
       Textur als Unterlage. Text darauf muss hell sein – .card definiert
       --ink/--ink-soft deshalb auf diese Werte um. */
    --box-bg: #402f21;
    --box-ink: #eaddc4;
    --box-ink-soft: #bdab8e;   /* 5.6:1 auf --box-bg; #a8957a lag mit 4.4 unter WCAG-AA */

    /* Seltenheits-Palette – EINE Quelle für Slots, Tooltip und Schrift.
       *-glow = heller Verlauf (auf dunklem Slot/Tooltip),
       *-ink  = dunklere, auf Pergament lesbare Schriftfarbe (gleicher Farbton). */
    --rar1-glow: #7a6a4a; --r1: #4a3c22;
    --rar2-glow: #3767f9; --r2: #1f6f9c;
    --rar3-glow: #e6c200; --r3: #b07a00;
    --rar4-glow: #ca0797; --r4: #8a2fa0;
    --rar5-glow: #fa0605; --r5: #b8341f;
    /* Set-Ecke */
    --set-green: #29da20;
    --set-orange: #e09120;

    --slot: 70px;   /* native Item-/Slot-Größe */

    /* Größe der 620px-Rahmenleinwand auf dem Bildschirm – die einzige
       Stellschraube je Rahmen. Größer = dickerer Rahmen, alles andere
       (Kanten, Füllung, Innenabstände) rechnet sich mit. */
    --fc-main: 323px;   /* Hauptfenster (.panel.pad-lg), Balken ~25px */
    --fc-side: 103px;   /* Seitenleisten-Kästen (.panel.pad), Balken ~9px */
}

* { box-sizing: border-box; }

html { -webkit-tap-highlight-color: transparent; }

body {
    margin: 0;
    min-height: 100vh;
    color: var(--ink);
    /* STANDARDSCHRIFT. Alles ohne eigene Angabe erbt sie – normaler Fließtext braucht
       deshalb KEINE font-size-Regel. Wer für gewöhnlichen Text eine eigene Größe setzt,
       baut eine Sonderregel, die später niemand mehr begründen kann.
       Die rem-Werte bleiben an der 16px-Wurzel: So verändert eine Änderung hier nur
       die Schrift, nicht Abstände, Kastenbreiten und Rundungen. */
    font-size: var(--fs-l);
    /* Bewusst NUR System-Schriften: nichts nachzuladen, kein externer Server, damit
       auch kein DSGVO-Thema. Die Liste deckt Windows, macOS/iOS, Android und Linux ab. */
    font-family: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    background-color: var(--sky);
    background-image: url('../img/bg.jpg');
    background-repeat: no-repeat;
    background-position: top center;
    background-size: auto;            /* volle Naturgröße, nichts verkleinern */
    position: relative;
    overflow-x: hidden;              /* kein horizontales Scrollen */
}

img { max-width: 100%; }
a { color: inherit; text-decoration: none; }

/* --- Grund-Layout ---------------------------------------------------- */
.wrap {
    width: 100%;
    max-width: 1420px;
    margin: 0 auto;
    /* Gilt nur unter 1280px und ausgeloggt (eingeloggt setzt .in-game .wrap auf 0,
       am Desktop überschreibt die Regel weiter unten). Handy: volle Breite,
       kein Seitenrand. */
    padding: clamp(60px, 11vw, 150px) 0 96px;
}

.cols { display: block; }
.center-win { margin-inline: auto; }

/* --- Fenster: Dschungel-Rahmen als Overlay ----------------------------
   4 Ecken + 4 Kanten als absolut positionierte Deko-Kinder ÜBER dem Panel,
   NICHT als border-image: border-image säße im Boxmodell und würde der Box
   Inhaltsfläche wegfressen. So behält .panel seine volle deklarierte Größe.

   Die acht Bilder sind ein echter 9-Slice (Betreiber-Export 2026-08-13,
   web/img/frame_main bzw. frame_side): alle liegen auf derselben quadratischen
   620px-Leinwand, in der der Steinbalken überall an derselben Stelle sitzt.
   Ecke und Kante treffen sich dadurch von selbst – nachgemessen liegen die
   Nähte bei 0-1px, und zwar ohne dass irgendein Teil eigene Maße bekommt.
   Vorher trug jede Kante ihren eigenen Versatz und ihre eigene Dicke; das ist
   komplett entfallen.

   EINE Stellschraube pro Variante: --fc (Kantenlänge der Leinwand auf dem
   Bildschirm). Dazu je vier gemessene Anteile für Aussen- und Innenkante des
   Balkens (--o-* / --i-*, siehe .panel). Diese acht Zahlen beschreiben die
   GRAFIK, nicht den Geschmack – sie werden nur angefasst, wenn neue Bilder
   kommen. */
.panel {
    position: relative;
    z-index: 1;
    box-sizing: border-box;
    color: var(--ink);
    isolation: isolate;   /* eigener Stacking-Context, damit z-index der Rahmenteile nicht mit der Seite kollidiert */

    /* ABSTAND INHALT -> RAHMEN. Das sind die vier Zahlen zum Drehen; gesetzt
       werden sie je Variante unten bei .panel.pad-lg / .panel.pad.
       Untergrenze ist die Balkendicke, sonst liegt Text auf dem Stein:
       Hauptfenster 25px, Seitenleiste 9px. Nach oben ist alles erlaubt – je
       größer, desto weniger ragen die Ecken-Ornamente in den Inhalt. */
    padding: var(--pad-oben) var(--pad-rechts) var(--pad-unten) var(--pad-links);
}
.panel > .content { position: relative; z-index: 1; }

/* Helle Füllung: eigene Ebene, exakt auf den Balken-Versatz eingerückt (siehe
   .pad-lg/.pad unten) – sie endet damit an der Balken-Innenkante statt den
   ganzen Kasten zu füllen und neben dem Ecken-Ornament herauszublitzen.
   Bild füllt diese Ebene randlos; die Farbe dahinter deckt den Rest, wenn das
   Bild bei hohen Boxen unten nicht mehr hinreicht. Bild NICHT zusätzlich
   einrücken – dann klafft je nach --fc eine Lücke zwischen Balken und Pergament.

   #dcc39a ist die UNTERKANTE von pergament.jpg, aus dem Bild gemessen (nicht der
   Mittelwert!). Nur so ist der Übergang unsichtbar: Genau dort läuft das Bild aus
   und die Farbe übernimmt. Beim Bildtausch muss dieser Wert MIT nachgemessen
   werden, sonst zieht sich eine helle Kante quer durch hohe Boxen.
   NICHT var(--parch) nehmen – das Token hängt an Tooltips und Dialogen (Zeilen
   240/266/281/397) und würde die mitverfärben. */
.frame-fill {
    position: absolute; z-index: 1;
    background: #dcc39a url('../img/pergament.jpg') no-repeat 0 0 / 100% auto;
    /* Endet an der Balken-INNENkante. Abstand zur Boxkante = Balkendicke, also
       (--i-* minus --o-*). 2px Überlappung unter den Stein, damit bei krummen
       Breiten kein Haarspalt aufgeht – die liegen auf deckendem Stein. */
    top:    calc(var(--fc) * (var(--i-t) - var(--o-t)) - 2px);
    right:  calc(var(--fc) * (var(--i-r) - var(--o-r)) - 2px);
    bottom: calc(var(--fc) * (var(--i-b) - var(--o-b)) - 2px);
    left:   calc(var(--fc) * (var(--i-l) - var(--o-l)) - 2px);
}

/* Ecken/Kanten: rein dekorativ, fangen keine Klicks, liegen ÜBER Füllung UND
   Inhalt (Ecken zusätzlich über den Kanten).
   Jedes Teil ist --fc gross und um --o-* nach AUSSEN versetzt – dadurch liegt
   die Balken-Aussenkante genau auf der Boxkante und die Ornamente ragen nach
   draussen, wie bei einem aufgelegten Rahmen. Die Kanten spannen exakt von
   Ecke zu Ecke: eine Ecke endet bei (1 - --o-*) x --fc. */
.frame-corner, .frame-edge { position: absolute; pointer-events: none; }
.frame-corner { z-index: 3; width: var(--fc); height: var(--fc); background-size: 100% 100%; }
.frame-edge { z-index: 2; }
.frame-corner.tl { top: calc(var(--fc) * var(--o-t) * -1); left:  calc(var(--fc) * var(--o-l) * -1); }
.frame-corner.tr { top: calc(var(--fc) * var(--o-t) * -1); right: calc(var(--fc) * var(--o-r) * -1); }
.frame-corner.bl { bottom: calc(var(--fc) * var(--o-b) * -1); left:  calc(var(--fc) * var(--o-l) * -1); }
.frame-corner.br { bottom: calc(var(--fc) * var(--o-b) * -1); right: calc(var(--fc) * var(--o-r) * -1); }

.frame-edge.top, .frame-edge.bottom {
    height: var(--fc); background-repeat: repeat-x; background-size: auto 100%;
    left:  calc(var(--fc) * (1 - var(--o-l)));
    right: calc(var(--fc) * (1 - var(--o-r)));
}
.frame-edge.top    { top:    calc(var(--fc) * var(--o-t) * -1); }
.frame-edge.bottom { bottom: calc(var(--fc) * var(--o-b) * -1); }

.frame-edge.left, .frame-edge.right {
    width: var(--fc); background-repeat: repeat-y; background-size: 100% auto;
    top:    calc(var(--fc) * (1 - var(--o-t)));
    bottom: calc(var(--fc) * (1 - var(--o-b)));
}
.frame-edge.left  { left:  calc(var(--fc) * var(--o-l) * -1); }
.frame-edge.right { right: calc(var(--fc) * var(--o-r) * -1); }

/* Haupt-Fenster (Mitte): der dickere der beiden Rahmen. */
.panel.pad-lg {
    --fc: var(--fc-main);
    /* Grafik-Messwerte, NICHT zum Drehen – nur anfassen, wenn neue Bilder kommen. */
    --o-t: .4323; --o-r: .3161; --o-b: .3806; --o-l: .3161;
    --i-t: .5097; --i-r: .4016; --i-b: .4613; --i-l: .4016;

    /* >>> ABSTAND INHALT -> RAHMEN (Hauptfenster). Hier drehen. <<<
       Balken ist 25px dick, weniger ist also nicht sinnvoll.
       Die Ecken-Ornamente ragen bei kleinen Werten in die Ecken der
       Inhaltsfläche: ab 120px gar nicht mehr, bei 40px rund 30px weit. Das ist
       Absicht und stört nur, wenn dort Text steht (Karten halten selbst 16px
       Abstand, so viel Überlappung schluckt der Kartenrand). */
    --pad-oben:   60px;
    --pad-rechts: 60px;
    --pad-unten:  60px;
    --pad-links:  60px;
}
.panel.pad-lg > .frame-corner.tl { background-image: url('../img/frame_main/ecke_ol.png'); }
.panel.pad-lg > .frame-corner.tr { background-image: url('../img/frame_main/ecke_or.png'); }
.panel.pad-lg > .frame-corner.bl { background-image: url('../img/frame_main/ecke_ul.png'); }
.panel.pad-lg > .frame-corner.br { background-image: url('../img/frame_main/ecke_ur.png'); }
.panel.pad-lg > .frame-edge.top    { background-image: url('../img/frame_main/oben.png'); }
.panel.pad-lg > .frame-edge.bottom { background-image: url('../img/frame_main/unten.png'); }
.panel.pad-lg > .frame-edge.left   { background-image: url('../img/frame_main/links.png'); }
.panel.pad-lg > .frame-edge.right  { background-image: url('../img/frame_main/rechts.png'); }

/* Seitenleisten-Kästen (Menü + Charakter): schlankere Variante, gleiche Technik,
   eigener Bildersatz und eigene gemessene Kanten-Anteile. */
.panel.pad {
    --fc: var(--fc-side);
    /* Seitlicher Einzug für ALLES, was in diesen Kästen untereinander steht –
       Menü-Buttons, Erfahrungsbalken, Ressourcen, Statuszeilen. Eine Variable,
       damit die Kanten nicht auseinanderlaufen, wenn einer davon angepasst wird. */
    --einzug: 15px;

    /* Grafik-Messwerte, NICHT zum Drehen – nur anfassen, wenn neue Bilder kommen. */
    --o-t: .3597; --o-r: .3129; --o-b: .3726; --o-l: .3129;
    --i-t: .4500; --i-r: .4048; --i-b: .4629; --i-l: .4065;

    /* >>> ABSTAND INHALT -> RAHMEN (Seitenleiste + Siegfenster). Hier drehen. <<<
       Balken ist 9px dick; die Ecken ragen bis 12px herein. */
    --pad-oben:   18px;
    --pad-rechts: 20px;
    --pad-unten:  18px;
    --pad-links:  20px;
}
.panel.pad > .frame-corner.tl { background-image: url('../img/frame_side/ecke_ol.png'); }
.panel.pad > .frame-corner.tr { background-image: url('../img/frame_side/ecke_or.png'); }
.panel.pad > .frame-corner.bl { background-image: url('../img/frame_side/ecke_ul.png'); }
.panel.pad > .frame-corner.br { background-image: url('../img/frame_side/ecke_ur.png'); }
.panel.pad > .frame-edge.top    { background-image: url('../img/frame_side/oben.png'); }
.panel.pad > .frame-edge.bottom { background-image: url('../img/frame_side/unten.png'); }
.panel.pad > .frame-edge.left   { background-image: url('../img/frame_side/links.png'); }
.panel.pad > .frame-edge.right  { background-image: url('../img/frame_side/rechts.png'); }

/* Der Titel/Reiter der OBERSTEN Karte musste früher vor dem Ecken-Ornament
   ausweichen und saß dadurch weiter rechts als alle Reiter darunter. Seit die
   Innenabstände auf 60px stehen, ist das erledigt: der Reiter beginnt bei 72px,
   das Ornament endet auf seiner Höhe bei 73px (2026-08-13 nachgemessen, inkl.
   halbtransparenter Blattränder). Alle Reiter stehen damit auf einer Flucht. */

/* Zwei gestapelte Rahmen in der Seitenleiste brauchen etwas Luft dazwischen. */
/* Abstand zwischen den Boxen der Seitenleiste. `> * + *` statt `.panel + .panel`,
   weil die Symbol-Leiste in einem <nav> steckt – der Nachbar-Selektor auf .panel
   griffe über den Wrapper hinweg nicht. */
.sidebar > * + * { margin-top: 1.2rem; }

/* --- Kopfleiste (nur Handy) ----------------------------------------- */
.topbar {
    position: sticky; top: 0; z-index: 40;   /* klebt ganz oben, bleibt beim Scrollen */
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    max-width: 854px;               /* mit dem Inhaltsfenster bündig (Tablet) */
    margin: 0 auto .75rem;
    padding: calc(.5rem + env(safe-area-inset-top)) 1rem .5rem;   /* Notch-sicher */
    border: 1px solid var(--wood);
    border-radius: 0 0 8px 8px;
    background: var(--parch);
    font-weight: 600;
}
/* im Spielzustand sitzt die Leiste ganz oben – keine Affen-Reserve nötig */
.in-game .wrap { padding-top: 0; }
.topbar .res { display: flex; align-items: center; gap: 1rem; }
.badge {
    background: var(--green);
    color: #fff;
    border-radius: 999px;
    padding: .1rem .5rem;
    font-size: var(--fs-s);
}
/* Für Werte, die KEINE gute Nachricht sind (z. B. Unehre-Rangliste) – gleicher
   Rotton wie die "ehrlos"-Warnung in der Arena (.arena-opp-warn). */
.badge.is-bad { background: var(--schlecht-flaeche); }

/* --- Icon-Menü (Diamanten, Rangliste, Post, Einstellungen, Logout) ---
   Handy/Tablet: Button oben rechts klappt eine Liste (Icon + Text) auf.
   Desktop: die runden Icons stehen als Reihe oben rechts (siehe unten). */
.iconmenu-toggle {
    position: fixed; top: calc(4px + env(safe-area-inset-top)); right: 8px; z-index: 61;
    cursor: pointer; padding: 0;
    display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 4px;
    width: 42px; height: 38px;
    border-radius: 9px; border: 1px solid var(--wood);
    background: var(--parch); box-shadow: 0 2px 6px rgba(0, 0, 0, .25);
}
.iconmenu-toggle span {
    display: block; width: 20px; height: 2px; border-radius: 2px;
    background: var(--ink); transition: transform .2s ease, opacity .2s ease;
}
.iconmenu-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(6px) rotate(45deg); }
.iconmenu-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.iconmenu-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-6px) rotate(-45deg); }

.iconmenu {
    position: fixed; top: calc(50px + env(safe-area-inset-top)); right: 8px; z-index: 60;
    display: none; flex-direction: column; gap: .15rem;
    min-width: 210px; padding: .35rem;
    border-radius: 12px; border: 1px solid var(--wood);
    background: var(--parch); box-shadow: 0 10px 28px rgba(0, 0, 0, .35);
}
.iconmenu.open { display: flex; }
/* AUF SCHMALEN SCHIRMEN OHNE RAHMEN. Die Symbole stecken seit dem 20.08.2026 in einer
   framed()-Box, damit sie in der Seitenleiste wie die übrigen Boxen aussehen. Im
   Aufklappmenü wäre das ein Kasten im Kasten – .iconmenu bringt dort seinen eigenen
   mit –, und die Rahmenvariante schleppt 80px Innenabstand für ihr unteres Ornament
   mit, was unter dem letzten Knopf eine große leere Fläche ergab.

   `display: contents` löst den Panel auf: Seine Einträge rücken direkt in die Liste,
   Innenabstände und Mindestmaße der Rahmenvariante fallen ersatzlos weg. Die
   Rahmenteile selbst würden sonst als eigene Listeneinträge auftauchen. */
@media (max-width: 1279px) {
    .iconmenu > .panel { display: contents; }
    .iconmenu > .panel > .frame-corner,
    .iconmenu > .panel > .frame-edge,
    .iconmenu > .panel > .frame-fill { display: none; }
    .iconmenu > .panel > .content { display: flex; flex-direction: column; gap: .3rem; }
}
.iconmenu-item {
    display: flex; align-items: center; gap: .6rem;
    padding: .35rem .5rem; border-radius: 8px;
    font-weight: 600; color: var(--ink); cursor: pointer; white-space: nowrap;
}
.iconmenu-item .ico {
    flex: 0 0 auto; width: 34px; height: 34px;
    background-size: contain; background-repeat: no-repeat; background-position: center;
}
.iconmenu-item.disabled { opacity: .5; cursor: not-allowed; }
.iconmenu-item:not(.disabled):hover { background: rgba(90, 60, 25, .12); }

/* IM AUFKLAPPMENÜ DES HANDYS OHNE SYMBOLE (Betreiber-Wunsch 25.08.2026). Am Desktop
   IST das Symbol der Menüpunkt – dort steht die Beschriftung gar nicht –, im
   Aufklappmenü steht der Name daneben und das Bild ist Beiwerk. Ohne die Bilder
   passen bei gleicher Höhe deutlich mehr Punkte in die Liste, und die neuen
   Nebenseiten (die keine Bilder haben) fallen nicht mehr aus der Reihe. */
@media (max-width: 1279px) {
    .iconmenu-item .ico { display: none; }
    /* GRUPPENABSTAND vor Einstellungen: Oben die Orte, wegen derer man das Menü
       öffnet, unten die Werkzeuge – Einstellungen, Adminbereich, Abmelden. Weil die
       Liste in der Mitte mit jeder neuen Seite wächst, sind das die einzigen beiden
       Punkte, die immer an derselben Stelle stehen: ganz unten. Der Abstand macht
       das sichtbar, damit man sie nicht sucht.
       Nur hier, nicht am Desktop – dort steht die Reihe waagerecht. */
    .iconmenu-item.iconmenu-gruppe { margin-top: .55rem; }
}
/* Nebenseiten stehen am Desktop als eigene Box in der Seitenleiste – hier wären sie
   derselbe Punkt ein zweites Mal, und die Symbolreihe ist bei fünf Plätzen ohnehin
   voll (siehe die 44px-Begründung weiter unten). */
@media (min-width: 1280px) {
    .iconmenu-extra { display: none; }
}

/* NEUER PFAD /img/buttons/ statt /img/main/.

   Alle 128×128, heruntergerechnet aus den 512er-Vorlagen: Angezeigt werden 34px,
   in der Icon-Reihe ab 1280px 54px. 128 deckt das auf einem 2x-Bildschirm (108px)
   mit Reserve ab und kostet 37 statt 530 KB je Symbol – zusammen 225 KB statt 2,6 MB. */
.ico-premium  .ico { background-image: url('../img/buttons/premium.png'); }
.ico-rank     .ico { background-image: url('../img/buttons/rangliste.png'); }
.ico-settings .ico { background-image: url('../img/buttons/einstellungen.png'); }
.ico-logout   .ico { background-image: url('../img/buttons/logout.png'); }
/* Der Umschalter trägt zwei Zustände: im Spiel führt er in die Verwaltung, dort
   zurück ins Spiel. Die Klasse am Element entscheidet, welches Bild gilt. */
.ico-admin    .ico { background-image: url('../img/buttons/admin.png'); }
.ico-spiel    .ico { background-image: url('../img/buttons/spiel.png'); }

/* --- Seitenleisten --------------------------------------------------- */
/* `contents` statt `none`: Die Seitenleiste selbst verschwindet aus dem Layout, ihre
   Kinder rücken durch. Nötig, weil die Symbol-Leiste seit dem 20.08.2026 IN der
   Seitenleiste steht – auf schmalen Schirmen ist sie aber das Aufklappmenü hinter dem
   Hamburger-Symbol und muss dort weiter erscheinen. Mit `display: none` am Elternteil
   wäre sie mit ausgeblendet, und kein Kind kann sich dagegen wehren.
   Ausgeblendet werden deshalb gezielt die ÜBRIGEN Boxen. */
.sidebar { display: contents; }
.sidebar > .panel { display: none; }
/* Ressourcen in der Seitenleiste – gut lesbar statt winzig */

/* Gold/Diamanten/Ehre als EINE Plakette mit drei Zeilen: Symbol und Wort links,
   Zahl rechtsbündig. tabular-nums lässt die Stellen untereinander stehen, sonst
   tanzen die Zahlen bei 0 gegen 34 Millionen. */
.res-list {
    /* Gleiche Kante wie die Menü-Buttons im Kasten darüber, siehe --einzug. */
    margin: .6rem var(--einzug, 15px) 0; text-align: left;
    display: grid; gap: 1px;
    border-radius: 8px; overflow: hidden;
    box-shadow: inset 0 0 0 2px var(--gold), inset 0 0 0 3px var(--tief), 0 2px 0 var(--tief);
}
.res-list > div {
    display: flex; align-items: center; gap: .45rem;
    padding: .3rem .55rem;
    background: linear-gradient(#4a3a26, #2e2317);
}
.res-ico { font-size: var(--fs-xl); line-height: 1; filter: drop-shadow(0 1px 1px #000); }
.res-lbl { font-size: var(--fs-xs); color: #bda67c; }
.res-val {
    margin-left: auto; font-weight: 800; font-size: var(--fs-l); color: var(--gold-hell);
    font-variant-numeric: tabular-nums; text-shadow: 0 1px 2px #000;
}

/* --- Navigations-Buttons: dunkle Plakette mit Goldrand ---------------
   Reines CSS, kein Bild. Das frühere glänzende Hellblau stammte vom alten,
   hellen Hintergrund; auf der dunklen Dschungelmalerei stach es als
   Fremdkörper heraus. Die Ranken-Ecken des kleinen Rahmens kämen hier NICHT
   in Frage – auf Buttonhöhe zusammengestaucht bleibt von 460px Ornament nur
   ein diffuser Punktrand übrig (2026-08-12 durchprobiert). */
.nav { display: grid; gap: .5rem; }
.nav-btn {
    display: block;
    text-align: center;
    /* Die Buttons haben KEINE eigene Breite – als Block füllen sie die Spalte.
       Der Einzug zieht sie schmaler, ohne dass irgendwo eine feste Breite
       entsteht, die beim nächsten Layout-Umbau nachgezogen werden muss.
       --einzug kommt von .panel.pad und gilt dort für alle gestapelten Blöcke. */
    margin-inline: var(--einzug, 15px);
    padding: .34rem .5rem;
    margin-block: 0;
}
/* KNOPF-OPTIK an EINER Stelle: dunkle Plakette, Goldrand, Innenkante. Zwei Nutzer –
   die Menüknöpfe der Seitenleiste und die Einträge des Aufklappmenüs auf dem Handy.
   Dort waren es bisher schlichte Textzeilen; in der kleinen alten Box fiel das kaum
   auf, in der gerahmten sah es nach Liste statt nach Menü aus. */
.nav-btn,
.iconmenu-item {
    border-radius: 6px;
    font-weight: 700;
    font-size: var(--fs-l);
    color: #f2e4c6;
    border: 2px solid var(--gold);
    background: linear-gradient(#4a3a26, #2e2317);
    box-shadow: inset 0 0 0 1px var(--tief), 0 2px 0 var(--tief);
    text-shadow: 0 1px 2px #000;
}
.nav-btn:hover,
.iconmenu-item:hover { background: linear-gradient(#5d4a30, #3a2c1c); border-color: var(--gold-hell); }

/* Live-Countdown auf Nav-Buttons (nav-timers.js) – Optik wie im Original:
   schräg gedreht, schwarze fette Schrift, hellblauer Glow rundum, KEIN Kasten. */
.has-timer { position: relative; }
.nav-timer {
    position: absolute; top: 50%; right: 10px;
    transform: translateY(-50%) rotate(-18deg);
    color: #000; font-weight: 800; font-size: var(--fs-xl);
    font-variant-numeric: tabular-nums; white-space: nowrap;
    text-shadow:
        1px 1px 1px #98eaff, 1px -1px 1px #98eaff,
        -1px 1px 1px #98eaff, -1px -1px 1px #98eaff,
        0 0 5px #98eaff;
    pointer-events: none; z-index: 4;
}
.bottomnav > a.has-timer .nav-timer { top: 1px; right: 3px; transform: rotate(-18deg); font-size: var(--fs-s); }
/* Auf den dunklen Plaketten dreht sich die Kontrastlage um: heller Text mit
   dunkler Kontur. Die Bottomnav liegt auf hellem Pergament und behält deshalb
   die schwarze Schrift mit hellem Glow von oben. */
.nav-btn .nav-timer {
    color: var(--gold-hell);
    text-shadow:
        1px 1px 1px #000, 1px -1px 1px #000,
        -1px 1px 1px #000, -1px -1px 1px #000,
        0 0 5px #000;
}
/* .iconmenu-item.active mit dabei: Die Nebenseiten stehen im Aufklappmenü des
   Handys und sollen dort dieselbe Markierung tragen wie die Hauptpunkte. Am
   Desktop greift es nie – dort ist das Symbol der Punkt und die Reihe hat keinen
   aktiven Zustand. */
.nav-btn.active,
.iconmenu-item.active { background: linear-gradient(#6d5531, #3e2f1c); border-color: var(--gold-hell); }
.nav-btn.disabled { opacity: .5; cursor: not-allowed; }

/* --- Untere Navigation (Handy) -------------------------------------- */
.bottomnav {
    position: fixed;
    inset: auto 0 0 0;
    z-index: 30;
    margin: 0 auto;
    width: 100%;
    max-width: 480px;
    display: flex;
    border-top: 1px solid var(--ink);
    background: var(--parch);
    padding-bottom: env(safe-area-inset-bottom);
}
/* NUR die direkten Kinder sind Menü-Einträge (`> a` bzw. `> span`). Ohne das `>`
   traf die Regel auch `<span class="ic">` mit dem Icon und `<span class="nav-angriff">`
   mit dem Zähler: Beide bekamen flex:1 und ein eigenes Padding, und die opacity-Regel
   darunter setzte JEDES Icon auf 50 % Deckkraft. Bei Emoji fiel das kaum auf, bei
   gezeichneten Motiven sofort (Betreiber-Meldung 17.08.: „ausgegraut oder durchsichtig").
   Die Regel unten für .nav-angriff musste das bisher mühsam zurückdrehen. */
.bottomnav > a, .bottomnav > span {
    flex: 1;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .1rem;
    padding: .4rem 0;
    font-size: var(--fs-xs);
    font-weight: 600;
    color: var(--ink-soft);
}
/* --gut, nicht --green: Das ist hier SCHRIFT auf dunklem Grund, keine Fläche.
   --green ist für Knopfflächen gemacht und wurde am 19.08.2026 abgedunkelt, damit
   weiße Schrift DARAUF genug Kontrast hat – als Schriftfarbe auf Dunkel wirkt genau
   das andersherum und drückte diesen Punkt von 4,76:1 auf 4,04:1. --gut ist die
   Rolle „positiv auf dunklem Grund" und kommt auf 9,71:1. */
.bottomnav > a.active { color: var(--gut); }
/* Blass NUR der noch nicht verfügbare Eintrag – der ist als <span> ausgezeichnet. */
.bottomnav > span { opacity: .5; }
/* Eigene Regel statt Erben vom Selektor oben, damit das Icon nicht davon abhängt,
   wie die Eintrags-Regel gerade aussieht. */
.bottomnav .ic {
    position: relative;            /* Anker für den Angriffs-Zähler (.nav-angriff) */
    display: flex; align-items: center; justify-content: center;
    font-size: var(--fs-2xl); line-height: 1;
}
/* --- Gezeichnete Icons im Handy-Menü (/img/mobileicons) --------------------
   Die Motive sind freigestellt und dunkel (gemessen: 54–69 % Deckung, mittlere
   Helligkeit 46–69), stehen also mit starkem Kontrast auf dem hellen Pergament des
   Balkens – der bleibt deshalb, wie er ist.

   Größe mitwachsend: Bei fünf Einträgen und höchstens 480px Balkenbreite bleiben
   96px je Eintrag; 28–36px Icon lassen daneben Platz für die Beschriftung. */
.bottomnav .nav-bild {
    width: clamp(28px, 8vw, 36px);
    height: auto; display: block;
}
/* Aktiver Eintrag: warmer Schein um das Motiv. Bewusst KEIN Abdunkeln der übrigen –
   fünf blasse Icons und ein normales lesen sich wie ein Fehler statt wie eine
   Auswahl. Die Beschriftung wird zusätzlich grün (Regel darüber). */
.bottomnav > a.active .nav-bild {
    filter: drop-shadow(0 0 3px rgba(224, 160, 32, .9)) drop-shadow(0 0 6px rgba(224, 160, 32, .5));
}

/* --- Inhalts-Karten (Frosted-Glas auf Pergament) -------------------- */
.content { color: var(--ink); }
/* Braune Box wie im Original (css/small.css: #rankingbg / .menubutton):
   Textur + schwarzer Rand + ZWEI innere Ringe – die geben die Tiefe.
   #402f21 = Mittelwert der Textur, damit große Karten nicht ausfransen
   (das Original hatte feste Größen und kam mit no-repeat aus). */
.card {
    position: relative;
    background: var(--box-bg) url('../img/bg_braune_box.jpg') no-repeat;
    border: 2px solid #000;
    border-radius: 10px;
    box-shadow: inset 0 0 0 2px #655e4d, inset 0 0 0 4px var(--dunkel);
    padding: 1.5rem 1rem 1rem;   /* oben Platz für das Badge */

    /* Auf Braun muss der Text hell sein. Die Token hier umzudefinieren zieht
       alles mit, was drinnen var(--ink)/var(--ink-soft) benutzt – statt 20
       Einzelstellen zu jagen. */
    --ink: var(--box-ink);
    --ink-soft: var(--box-ink-soft);
    color: var(--ink);
}
/* --- EIN ABSTAND ZWISCHEN ALLEN FENSTERN ------------------------------
   Vorher stand der Spalt an fünf Stellen einzeln: `.card + .card`, `.stat-strip`
   (margin .5/.8rem), `.ueber-cols` (margin-top 1.9rem), `.pl-grid` (margin-top
   1rem) – plus zwei Regeln, die genau diese wieder zurücknahmen. Gemessen kam
   dabei sichtbar 5px, 8px, 16px und 19px heraus.

   Jetzt liefert der Container den Spalt (gap), und zwar überall derselbe. Die
   Struktur ist je Seite anders – Übersicht und Markt stapeln in #inv-widget,
   die übrigen direkt in .content –, deshalb stehen beide hier.

   Nur die Seitenleiste bleibt außen vor: sie benutzt dasselbe framed() und damit
   auch .content, hat aber ihre eigene, engere Anordnung. */
.center-col .content, #inv-widget {
    display: flex; flex-direction: column; gap: var(--luecke);
}
/* Schon flex/grid – hier genügt es, den Wert anzugleichen. */
.ueber-cols, .ueber-left, .ueber-right, .pl-grid { gap: var(--luecke); }

/* BADGE-AUSGLEICH: Ein Block, dessen Oberkante ein Badge trägt, frisst
   --badge-ueber vom Spalt. Nur mit Vorgänger, sonst rutschte die erste Karte
   jeder Seite nach unten – und die Vorgängerliste ist bewusst ausgeschrieben:
   :not(:first-child) hätte auch nach den unsichtbaren Datencontainern von
   hub.php (#merc-deployed, #cmp-weakring) gegriffen. */
:is(.card, .stat-strip, .pl-grid, .ueber-cols) ~
:is(.card:has(> .card-title), .card:has(> .card-tabs), .ueber-cols) {
    margin-top: var(--badge-ueber);
}

/* --- Badges auf dem oberen Kartenrand --------------------------------
   EIN Rezept für Titel UND Reiter (Original: .menubutton – dieselbe Textur
   wie die Box, nur r=7px). Bei Karten mit Reitern ist der erste Reiter der
   Titel; eine zusätzliche Reiter-Zeile darunter gibt es dann nicht. */
.card-title,
.badge-tab {
    margin: 0; padding: .2rem .7rem;
    background: var(--box-bg) url('../img/bg_braune_box.jpg') no-repeat;
    border: 2px solid #000;
    border-radius: 7px;
    box-shadow: inset 0 0 0 2px #655e4d, inset 0 0 0 4px var(--dunkel);
    font: inherit; font-size: var(--fs-l); font-weight: 700; line-height: 1.3;
    color: var(--box-ink);
    text-shadow: 0 1px 2px rgba(0, 0, 0, .8);
    white-space: nowrap;
}

/* Einzelner Titel (Karten ohne Reiter, z. B. Händler). */
.card-title { position: absolute; top: -13px; left: 12px; z-index: 2; }

/* Reiter-Reihe sitzt genauso auf dem Rand. */
.card-tabs {
    position: absolute; top: -13px; left: 12px; right: 12px; z-index: 2;
    display: flex; gap: .35rem; flex-wrap: wrap;
}
/* Reiter immer voll deckend (keine Transparenz – sah ausgewaschen aus). Der
   Unterschied aktiv/inaktiv kommt über Textfarbe + grünen Ring, nicht über opacity. */
.badge-tab { cursor: pointer; color: var(--box-ink-soft); }
.badge-tab:hover { color: var(--box-ink); }
.badge-tab.active, .bag-sort:hover { color: #fff; box-shadow: inset 0 0 0 2px var(--green), inset 0 0 0 4px var(--dunkel); }
.badge-tab[data-locked] { color: #8a7a63; }   /* gesperrt: gedämpfte Schrift, kein Durchscheinen */

/* "Sortieren" sitzt rechts außen in der Reiter-Reihe (margin-left:auto schiebt
   es weg von den Taschen-Reitern). Optik wie ein Reiter, nur dezenter. */
.bag-sort {
    margin: 0 0 0 auto; padding: .2rem .7rem;
    background: var(--box-bg) url('../img/bg_braune_box.jpg') no-repeat;
    border: 2px solid #000; border-radius: 7px;
    box-shadow: inset 0 0 0 2px #655e4d, inset 0 0 0 4px var(--dunkel);
    font: inherit; font-size: var(--fs-m); font-weight: 700; line-height: 1.3;
    color: var(--box-ink-soft); text-shadow: 0 1px 2px rgba(0, 0, 0, .8);
    white-space: nowrap; cursor: pointer;
}
.bag-sort:active { transform: translateY(1px); }
/* Ohne Inventartrank trägt der Knopf nur die Krone statt des Sortier-Symbols –
   keine Ausblassung, kein disabled (siehe .vendor-sklaven oben). Der Klick läuft
   durch und der Server sagt, was fehlt. */

/* Taschenseite mit RESTBESTAND nach Trankablauf: sichtbar und benutzbar, nimmt aber
   nichts Neues auf. Warnfarbe statt Gold – Gold heißt im Spiel „kaufbar", hier geht
   es um „räum das auf". Reiter und Hinweiszeile teilen denselben Ton. */
.badge-tab.is-rest { box-shadow: inset 0 0 0 2px var(--schlecht-flaeche), inset 0 0 0 4px var(--dunkel); }
.bag-rest {
    grid-column: 1 / -1;
    margin: 0 0 .3rem; padding: .25rem .5rem; border-radius: 6px;
    background: var(--schlecht-flaeche); color: #fff;
    font-size: var(--fs-xs); font-weight: 700; text-align: center;
    text-shadow: 0 1px 2px rgba(0, 0, 0, .6);
}

/* ohne Box (z. B. Paperdoll) */
.card--bare {
    background: none; border: 0; box-shadow: none; padding: 0;
    /* HIER MÜSSEN LITERALE STEHEN, keine Variablen: Diese Zeile SETZT --ink zurück,
       weil der Bereich auf hellem Pergament liegt und nicht im dunklen Kasten.
       `--ink: var(--ink)` wäre ein Selbstbezug – in CSS ungültig, die Variable
       behielte den geerbten hellen Wert und die Schrift verschwände (passiert am
       18.08.2026 genau so: Kontrast 1,07 in der Set-Liste). */
    --ink: #241a0e; --ink-soft: #6b5236;   /* steht auf Pergament, nicht auf Braun */
    color: var(--ink);
}
.muted { color: var(--ink-soft); }
.small { font-size: var(--fs-s); }
.center { text-align: center; }
/* Text auf eine Zeile zwingen und mit … abschneiden. Das Muster stand an sechs Stellen
   ausgeschrieben; als Klasse ist es eine Entscheidung statt einer Wiederholung.
   `min-width: 0` gehört dazu: In einem Flex- oder Rasterfeld schrumpft ein Kind sonst
   nicht unter seine Inhaltsbreite, und dann greift das Abschneiden gar nicht erst. */
.kurz { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* Text, der NUR auf breiten Schirmen erscheint. Statt Beschriftungen zu verkleinern
   oder zwei Fassungen zu pflegen, fällt auf dem Handy das entbehrliche Wort weg:
   „Empfohlen ab Level 35" wird zu „ab Level 35". Gemessen: In eine Plakette passen
   dort 131px – die lange Fassung braucht 140 und bräche um. */
/* `revert` statt `inline`: So behält jedes Element seine natürliche Darstellung –
   ein <span> bleibt inline, ein <p> bleibt Block. Mit `inline` war das Paar auf
   Textschnipsel beschränkt. */
.nur-breit { display: revert; }
@media (max-width: 560px) { .nur-breit { display: none; } }
/* Gegenstück: erscheint NUR auf schmalen Schirmen – das Symbol, das dort die
   weggefallene Beschriftung ersetzt. Hieß früher .dn-go-ic und galt nur für den
   Dungeon-Knopf; .dn-go-txt daneben war Zeichen für Zeichen dasselbe wie
   .nur-breit, samt gleichem Breakpunkt. Ein Paar statt vier Klassen. */
.nur-schmal { display: none; }
@media (max-width: 560px) { .nur-schmal { display: revert; } }
.hint { margin-top: 1.5rem; text-align: center; font-size: var(--fs-s); color: var(--ink-soft); }
/* Zwei Varianten statt eigener Klassen je Seite: `--eng` rückt näher an das Element
   darüber, `--kraeftig` ist der dunklere Ton für hellen Pergamentgrund (gemessen
   5,45:1 statt 3,93:1 – der Standardton ist dort zu schwach). */
.hint--eng      { margin-top: .6rem; }
.hint--kraeftig { color: #523e28; }


/* zweispaltiges Layout ab Tablet */
.split { display: grid; gap: 1rem; }

/* --- Kopfzeile des Hauptfensters ------------------------------------------
   Name, Level, Fortschritt und Ehre in EINER Zeile ganz oben – vorher stand das
   als Schild über der Puppe (.nameplate) und war zweizeilig kaum lesbar.

   NICHT das alte Schild (charnameusw.png): Das ist 244px breit und müsste über
   die Fensterbreite gezogen werden – Faktor 2,7, die Kanten schmieren. Statt-
   dessen die Holztextur, die .card und die Reiter ohnehin schon tragen.        */
/* Nur der Platzhalter, den die OOB-Beilage von hub.php trifft. `display: contents`
   löst ihn aus dem Layout auf, damit .kopf ein direktes Kind des Spalten-Flex
   bleibt und dessen `gap` weiter greift. */
.kopf-slot { display: contents; }

.kopf {
    position: relative;
    display: flex; align-items: center; gap: .7rem;

    /* Volle Breite ist der Normalfall: Unter 701px ist die Puppe ein Raster über
       die ganze Fläche – „so breit wie die Puppe" heißt dort 100 %. Darüber
       greift --puppe-breite, siehe den Media-Block weiter unten. */
    width: 100%;

    /* EIN Wert oben wie unten: --kopf-luft. Abgezogen wird nur, was der
       Container ohnehin schon stellt – oben die Innenpolsterung des Fensters
       (hier 1rem, ab 1280px mehr, siehe unten), unten der Spalt `var(--luecke)`
       des Spalten-Flex `.center-col .content`. Ohne den Abzug käme beides oben
       drauf und aus 10px würden 16 bzw. 26. */
    margin: calc(var(--kopf-luft) - 1rem) auto calc(var(--kopf-luft) - var(--luecke));
    padding: .45rem 1.3rem calc(.45rem + 4px);
    border-radius: 4px;
    /* DIESELBE HOLZTEXTUR wie .card und die Reiter-Badges – ein Bild statt eines
       eigenen Verlaufs, damit die Leiste zur übrigen Oberfläche gehört. Die
       Rückfallfarbe --box-bg (#402f21) trifft den Mittelwert des Bildes
       (gemessen rgb 64/47/32), also fällt kein Rand auf, wo es nicht hinreicht.
       Schrift #f4ead2 darauf: 10,2:1. */
    background: var(--box-bg) url('../img/bg_braune_box.jpg') no-repeat;
    box-shadow: inset 0 1px 0 rgba(156, 155, 154, .35),
                inset 0 -1px 0 rgba(0, 0, 0, .9),
                0 0 0 1px #000,
                0 3px 7px rgba(0, 0, 0, .55);
    color: #f4ead2; text-shadow: 0 1px 2px rgba(0, 0, 0, .85);
}
/* Söldner haben weder Erfahrung noch Ehre – übrig bleiben Name und Platz, und
   die gehören in die Mitte statt an den linken Rand. */
.kopf--merc { justify-content: center; }

.kopf .k-name { font-weight: 800; font-size: var(--fs-l); white-space: nowrap;
                overflow: hidden; text-overflow: ellipsis; }
.kopf .k-lvl  { font-weight: 800; font-size: var(--fs-s); white-space: nowrap; flex: none; }
.kopf .k-ehre { margin-left: auto; font-weight: 700; font-size: var(--fs-s);
                white-space: nowrap; flex: none; font-variant-numeric: tabular-nums; }

/* --- Erfahrung: ein schmaler Streifen an der Unterkante -------------------
   Der frühere 16px-Balken mitten in der Zeile drängte Name und Ehre auf schmalen
   Schirmen in eine zweite Reihe. Als Streifen an der Kante kostet der Fortschritt
   keine Zeilenbreite mehr – die Zahl steht ohnehin bei „Lv. 19 » 3 %". */
.xpstreifen {
    position: absolute; left: 0; right: 0; bottom: 0; height: 4px;
    border-radius: 0 0 4px 4px; overflow: hidden;
    background: rgba(0, 0, 0, .55);
}
.xpstreifen > i { display: block; height: 100%; background: linear-gradient(90deg, #f4b23a, #ffd98a); }

/* Die genauen Erfahrungspunkte hängen als `title` an der ganzen Zeile – dieselbe
   Sorte Hinweis wie an den Symbolen der Seitenleiste, vom Browser gezeichnet.
   Auf dem Handy gibt es keins; dort steht der Prozentwert ohnehin bei „Lv. 19 » 3 %".
   (Eine eigene CSS-Blase am Element ginge NICHT: Nach oben verschwindet sie hinter
   dem Steinbalken des Rahmens – .frame-edge liegt mit z-index 2 über dem Inhalt,
   und .panel trägt `isolation: isolate`.) */

/* --- Paperdoll: Fläschchen | Ausrüstung | Charakter | Ausrüstung | Fläschchen */
/* mobile-first kompakt: passt hochkant, Fläschchen als Reihe unten */
/* Kein padding-top mehr: Der Platz war für das Namensschild über der Puppe
   reserviert (.nameplate, hing mit `bottom: 100%` darüber). Name und Level
   stehen jetzt in der Kopfzeile des Fensters – auch beim Söldner, siehe
   kopf_html() in helpers.php. */
.paperdoll { display: flex; justify-content: center; align-items: flex-start; gap: .3rem; flex-wrap: wrap; }

/* HOCHKANT: Die Fläschchen wandern unter die Puppe. Sie flankierten sie links und
   rechts und kosteten damit zwei Spalten Breite – zusammen mit Slots und
   Charakterbild ergab das eine Mindestbreite von rund 380 px, unter die die Seite
   nicht schrumpfen konnte. Darunter nebeneinander gelegt kosten sie gar keine
   Breite mehr, und Puppe wie Inventar bekommen den Platz.
   (Die Regeln dazu stehen weiter unten, HINTER den Grundgrößen der Puppe – sonst
   überschreiben die Grundregeln sie wieder.) */

/* Kein Flex-Element darf die Seite auseinanderdrücken: `min-width: auto` lässt ein
   Flex-Kind nicht unter die Mindestbreite seines Inhalts schrumpfen – dadurch wurde
   die ganze Seite breiter als der Bildschirm und rechts abgeschnitten. */
.cols, .center-col, .center-win, .center-win > .panel { min-width: 0; }
/* `flex: 1 1 45%` ist der eigentliche Schalter, und zwar OHNE eine einzige
   Pixelgrenze: Zwei Gruppen zu je 45 % passen niemals neben die Puppe, also brechen
   sie unter sie um; auf der eigenen Zeile wachsen sie dann zu je einer Hälfte.
   Erst das Desktop-Layout (>=1280px) stellt sie mit `flex: 1 1 0` an die Seiten.

   Warum nicht 50 %: Zwei halbe Breiten plus der Abstand dazwischen sind ein paar
   Pixel zu viel für eine Zeile – die zweite Gruppe rutschte dann auf eine dritte
   Zeile. Mit 45 % ist der Abstand egal, egal wie er später eingestellt wird.

   Die Gruppen rücken zur Mitte zusammen (flex-end/flex-start), sonst klaffte
   zwischen den beiden Dreierpäckchen eine Lücke quer über die Breite. */
.flasks { display: flex; flex-direction: row; order: 3; gap: .3rem;
          justify-content: center; flex: 1 1 45%; }
.paperdoll .flasks:first-of-type { justify-content: flex-end; }
.paperdoll .flasks:last-of-type  { justify-content: flex-start; }
.flask img { width: clamp(22px, 7vw, 36px); display: block; }
/* Nicht aktives Fläschchen: FARBE BLEIBT, nur auf 50% Deckkraft heruntergesetzt
   (Betreiber-Wunsch 17.08.; 20% war auf dem fast schwarzen Untergrund praktisch
   unsichtbar) – man soll erkennen, WELCHER Trank fehlt, nicht bloß
   dass da eine graue Flasche steht. Kein Entsättigen, kein Abdunkeln.
   Eine Bilddatei je Fläschchen: vorher lag daneben ein zweites "_inv"-PNG, doppelt
   so viele Dateien für denselben Zustand, und beim Bildertausch musste man beide
   finden. */
.flask.is-aus img { opacity: .5; }
/* Läuft in unter 24 h ab: LANGSAMES, schwaches Pulsen. Bewusst kein Ausrufezeichen
   und kein Kaufknopf – der Hinweis soll auffallen, wenn man hinschaut, und sonst
   nicht stören (Betreiber: "keine aggressive Kaufaufforderung"). 4s-Takt, damit es
   im Augenwinkel nicht flackert. */
.flask.is-endet img { animation: flask-endet 4s ease-in-out infinite; }
@keyframes flask-endet {
    0%, 100% { opacity: 1;   filter: none; }
    50%      { opacity: .62; filter: brightness(1.15); }
}
@media (prefers-reduced-motion: reduce) {
    /* Ohne Bewegung bleibt der Hinweis als Dauerzustand sichtbar, statt zu verschwinden. */
    .flask.is-endet img { animation: none; opacity: .8; }
}
.eq-col { display: flex; flex-direction: column; gap: .3rem; }
.eq-left, .eq-right { display: flex; gap: .3rem; }
/* MITSCHRUMPFEN statt feste 38 px: Zusammen mit Charakterbild und Fläschchen ergab
   die Puppe sonst eine Mindestbreite von rund 380 px. `.center-win` ist ein
   Flex-Element und kann nicht unter die Mindestbreite seines Inhalts – dadurch war
   die ganze Seite auf schmalen Handys zu breit und die Inventarkarte rechts
   abgeschnitten. Ab 1280 px setzt der Desktop-Block eigene Größen. */
.paperdoll .slot { width: clamp(26px, 8vw, 38px); }
.char-center { display: flex; flex-direction: column; align-items: center; margin: 0 .15rem; }
.char-head { margin-bottom: .3rem; font-size: var(--fs-l); }
.char-frame { position: relative; display: inline-block; line-height: 0; }
/* KULISSE für freigestellte Charakterbilder: der Hauptcharakter ist ein PNG ohne
   Hintergrund und stünde sonst vor der braunen Karte. Söldnerbilder sind
   undurchsichtig und decken die Kulisse einfach ab. Fehlt die Datei, sieht es aus
   wie vorher – der Browser zeigt dann schlicht keinen Hintergrund. */
.char-big { display: block; width: clamp(92px, 32vw, 224px); border-radius: 12px;
    box-shadow: 0 0 0 2px var(--wood);
    background: rgba(0, 0, 0, .1) var(--charbg) no-repeat center / cover; }

/* HOCHKANT: Die Puppe als RASTER über die volle Breite – sieben gleiche Spalten,
   davon je zwei für die Ausrüstung links und rechts und drei fürs Charakterbild.
   Dadurch ist das Bild exakt so hoch wie drei gestapelte Slots plus deren Lücken:
   Ausrüstung und Bild schließen oben UND unten bündig ab, ohne eine einzige feste
   Pixelangabe. Die Fläschchen liegen darunter in einer Reihe und kosten keine
   Breite mehr (vorher flankierten sie die Puppe und erzwangen ~380 px Mindestbreite). */
/* AB 701px: Slots nehmen die volle Höhe des Charakterbildes an. Das stand früher
   erst im Desktop-Block (>=1280px) – dazwischen blieben die Plätze auf Handygröße,
   obwohl längst Platz war. Die Fläschchen sind hier NICHT dabei: Sie liegen in
   diesem Bereich unter der Puppe und richten sich nicht nach ihrer Höhe.

   224px ist die GEMESSENE Höhe des Charakterbildes (.char-big und .char-frame, beide
   224). Früher standen hier 234 – die Zahl stammte aus einem angenommenen Holzrahmen,
   den es nicht gibt, und ließ die Ausrüstungsspalten oben und unten je 5px überstehen.
   Die Zahl gilt ab hier für ALLE Breiten; der Desktop-Block setzt sie nicht mehr neu. */
@media (min-width: 701px) {
    /* --char-h und --eq-gap stehen in :root – dieselben Zahlen braucht die
       Kopfzeile für --puppe-breite. */
    .paperdoll { gap: .5rem; }

    /* KOPFZEILE GENAU SO BREIT WIE DIE PUPPE (Charakterbild + alle Slots, ohne
       die Fläschchen). Ab hier ist die Puppe eine Flex-Reihe mit festen Größen –
       darunter ist sie ein Raster über die volle Breite, dort gilt die 100 % aus
       der Grundregel. */
    .kopf { width: var(--puppe-breite); }
    .eq-col, .eq-left, .eq-right { height: var(--char-h); }
    .eq-col { align-items: center; gap: var(--eq-gap); }
    .paperdoll .slot {
        flex: none; width: auto; aspect-ratio: 1 / 1;
        height: calc((var(--char-h) - 2 * var(--eq-gap)) / 3);
    }
}

@media (max-width: 700px) {
    /* VIERZEHN Spalten, nicht sieben: Bei sieben liegt die Grenze zwischen Spalte 4
       und 5 bei 57 % – die Fläschchenreihe säße dann sichtbar rechts von der Mitte.
       Mit vierzehn fällt die Mitte genau auf eine Spaltengrenze. */
    .paperdoll { display: grid; grid-template-columns: repeat(14, 1fr); gap: .35rem; align-items: start; }
    .eq-left, .eq-right {
        display: grid; grid-column: span 4; height: auto; gap: .35rem;
        grid-template-columns: repeat(2, 1fr);
        /* Spaltenweise füllen wie vorher – sonst wandern die Slots zeilenweise
           durcheinander und Helm, Brille & Co. sitzen an anderer Stelle. */
        grid-template-rows: repeat(3, auto); grid-auto-flow: column;
    }
    .eq-col { display: contents; }
    .paperdoll .slot { width: 100%; height: auto; }
    .char-center { grid-column: span 6; margin: 0; min-width: 0; }
    .char-frame { display: block; }
    .char-big { width: 100%; aspect-ratio: 1 / 1; }

    /* Alle sechs Flaschen geschlossen in der MITTE: Die linke Gruppe schiebt sich
       nach rechts, die rechte nach links – sonst klaffte zwischen den beiden
       Dreierpäckchen eine Lücke quer über den Bildschirm. */
    .paperdoll .flasks { display: flex; gap: .4rem; }
    .paperdoll .flasks:first-of-type { grid-column: 1 / span 7; justify-content: flex-end; }
    .paperdoll .flasks:last-of-type  { grid-column: 8 / span 7; justify-content: flex-start; }
}

/* Icons ins Bild gelegt: mein Char links, Söldner rechts (unten) */
.merc-row { position: absolute; left: 5px; right: 5px; bottom: 5px; display: flex; justify-content: space-between; align-items: flex-end; }
.merc-group { display: flex; gap: 1px; }
/* NUR DER KOPF: Die Bilder sind Brustbilder bis zur Hüfte – auf 22 Pixeln ist der
   Kopf darin winzig und niemand erkennt, wer da steht. Statt eigener Porträtgrafiken
   wird in den oberen Bildbereich HINEINGEZOOMT und der Rest abgeschnitten: Rahmen,
   Größe und Pergament sitzen am umschließenden Element, das Bild darin wird
   vergrößert und oben ausgerichtet.
   `transform-origin: 50% 14%` trifft die Kopfhöhe der Vorlagen (Kopf sitzt bei allen
   auf derselben Höhe, weil alle im gleichen Ausschnitt erzeugt wurden). */
.mainchar, .merc { display: block; overflow: hidden; border-radius: 5px;
    box-shadow: 0 0 0 1px var(--wood);
    background: var(--parch) url('../img/pergament.jpg') no-repeat center / cover; }
/* `flex: 0 0 auto` ist Pflicht: Beide sind Flex-Kinder in .merc-row bzw. .merc-group
   und würden sonst zusammengedrückt, sobald es eng wird – die Größe lag früher am
   Bild, das schrumpfte nicht mit. */
/* Größe skaliert mit der Bildschirmbreite: Feste 34/40 px ragten auf dem Handy aus
   dem Charakterbild heraus und machten die Seite breiter als den Bildschirm – daher
   das waagerechte Scrollen und die verrutschte Menüleiste. */
.merc     { width: clamp(20px, 5.4vw, 34px); height: clamp(20px, 5.4vw, 34px); flex: 0 0 auto; }
.mainchar { width: clamp(24px, 6.4vw, 40px); height: clamp(24px, 6.4vw, 40px); flex: 0 0 auto; }
.mainchar img, .merc img, .q-team img { width: 100%; height: 100%; object-fit: cover; display: block;
    transform: scale(2.1); transform-origin: 50% 14%; }

/* Träger der Puppe: eigener Char oder Söldner. Der grüne Ring markiert, wessen
   Ausrüstung oben gerade zu sehen ist – nicht mehr fest "mein Char". */
/* Hover und Markierung liegen am RAHMEN, nicht am Bild – am Bild würden sie den
   Kopf-Zoom (transform) überschreiben. */
.carrier { cursor: pointer; transition: box-shadow .12s, transform .12s; }
.carrier:hover { transform: scale(1.12); }
.carrier.active { box-shadow: 0 0 0 2px var(--green); }
/* Leerer Platz: blass, aber anklickbar – seine Ausrüstung liegt ja weiter dort. */
.merc.is-empty img { opacity: .45; }
.char-big.is-empty { opacity: .3; filter: grayscale(1); }

/* --- Statuswerte ----------------------------------------------------- */
/* kompakte Werte-Liste (einspaltig, schmale Zeilen – spart Platz) */
/* Eine Zeile je Attribut: Icon, Attribut mit Grundwert, rechts bündig die
   Wirkung. Dunkle Plakette mit Goldkante links – dieselbe Sprache wie die
   Menü-Buttons darüber. Vorher lagen hier helle Pergament-Kärtchen
   (bg_box_set.jpg) aus dem alten, hellen Design; neben den dunklen Plaketten
   wirkte das wie zwei verschiedene Spiele.
   Wird AUCH von der Arena-Kampfvorschau benutzt (arena-preview.php) – die
   Plakette bringt ihren eigenen dunklen Grund mit und funktioniert deshalb auf
   hellem wie dunklem Untergrund. */
.stat {
    display: flex; align-items: center; gap: .5rem;
    padding: .3rem .5rem .3rem .45rem; border-radius: 7px;
    background: linear-gradient(#4a3a26, #2e2317);
    box-shadow: inset 3px 0 0 var(--gold), inset 0 0 0 1px var(--tief), 0 1px 2px rgba(0, 0, 0, .5);
    color: #f2e4c6;
}
/* Abstand zwischen den Zeilen kommt vom Container (.side-stats bzw. .ap-stats,
   beide flex mit gap) – hier keiner, sonst zählt er doppelt. */
.stat-ico { width: 26px; height: 26px; flex: 0 0 auto; filter: drop-shadow(0 1px 1px #000); }
.stat-txt { flex: 1; min-width: 0; font-size: var(--fs-xs); color: #bda67c; }
.stat-txt b { color: #f2e4c6; font-size: var(--fs-m); }
.stat-eff {
    flex: 0 0 auto; display: flex; align-items: center; gap: .2rem;
    font-size: var(--fs-s); font-weight: 700; color: var(--gold-hell); text-shadow: 0 1px 2px #000;
}
/* Von einem Premium-Trank angehobener Wert: GRÜN statt Gold (web/PREMIUM.md).
   var(--gut-plakette), nicht var(--green): Das Token (var(--green)) kommt auf der braunen Plakette
   nur auf 3,28:1 Kontrast, das jetzige Gold liegt bei 9,34:1. Gemessen, nicht
   geschätzt – dieses Grün erreicht 8,69:1 und ist damit genauso gut lesbar.
   Die genaue Zusammensetzung steht im title (siehe sidestats.php). */
.stat-eff.is-prem { color: var(--gut-plakette); }
.stat-eff i { font-style: normal; font-weight: 400; font-size: var(--fs-xs); color: #bda67c; }
.stat-ico-w { width: 14px; height: 14px; flex: 0 0 auto; }   /* kleines Wirkungs-Icon */

/* Übersicht: links Inventar, rechts Statuswerte — gleich hoch. Medaillen darunter. */
/* Abstand nach oben kommt vom Container-gap plus --badge-ueber (die Reiter-Badges
   der ersten Zeile ragen hinaus). Gilt für Übersicht UND Markt. */
.ueber-cols { display: grid; }
.ueber-left, .ueber-right { display: flex; flex-direction: column; min-width: 0; }
/* Karten füllen ihre Spalte -> Inventar und Statuswerte sind gleich hoch */
.ueber-left > .card, .ueber-right > .card { flex: 1; }
@media (min-width: 780px) { .ueber-cols { grid-template-columns: 1fr 1fr; } }

/* Reiter (Medaillen/Info) */
/* Reiter tragen jetzt .badge-tab (Badge auf dem Kartenrand) – die alte
   Pillen-Optik hier wäre doppelt und würde dagegen kämpfen. */
.tab-pane.hide { display: none; }

/* Set-Übersicht: welches Set ist das beste? */
/* Charbild des Trägers, der ein Set gerade trägt – klein, klickbar, sitzt direkt
   vor dem "Set anlegen"-Button. Klick wechselt zu diesem Träger (.carrier). */
/* Charbild + Button bleiben zusammen (kein getrennter Umbruch), ganz rechts. */
/* KEIN KNOPF MEHR (28.08.2026). Die ganze Zeile legt das Set an, und ob es getragen
   wird, sagt das GESICHT des Trägers in der Ecke – ein „angelegt"-Knopf daneben wäre
   dieselbe Aussage zweimal. Zeilen ohne Träger bleiben leer; das Auge findet dadurch
   sofort, was man anhat.

   Die Marke ist die Arena-Marke (.wc-badge), Maße und Neigung übernommen. Ihr
   Leuchtrand trägt die SETFARBE – dieselben Töne wie die Slots in der Puppe. */
.setrow-marke {
    /* SENKRECHT MITTIG statt oben angeheftet: Die Zeilen sind je nach Bonuszahl
       verschieden hoch, und eine Marke, die immer oben klebt, sitzt bei der hohen
       Zeile zu hoch und bei der flachen mittig – wirkt also zufällig. */
    position: absolute; top: 50%; right: 8px; z-index: 2;
    transform: translateY(-50%) rotate(5deg);
    width: 3.4rem; aspect-ratio: 1 / 1; border-radius: 8px; overflow: hidden;
    background: linear-gradient(160deg, #322619, #1c140c);
    cursor: pointer; transition: transform .12s;
}
.setrow-marke:hover { transform: translateY(-50%) rotate(5deg) scale(1.08); }
.setrow-marke.is-green  { box-shadow: 0 2px 6px rgba(0,0,0,.55), 0 0 0 2px var(--set-green),  0 0 9px rgba(41,218,32,.45); }
.setrow-marke.is-orange { box-shadow: 0 2px 6px rgba(0,0,0,.55), 0 0 0 2px var(--set-orange), 0 0 9px rgba(224,145,32,.45); }
/* KOPF-AUSSCHNITT wie in der Puppe (.mainchar img / .merc img): dieselben Werte,
   damit dasselbe Gesicht überall gleich sitzt – nicht ab der Hüfte. */
.setrow-marke img { width: 100%; height: 100%; object-fit: cover; display: block;
    transform: scale(2.1); transform-origin: 50% 14%; }

/* PLAKETTEN statt Fließtext. Zwei Reihen, getrennt allein durch ABSTAND: oben, wo das
   Set steht (Setkraft, angelegt), unten, was es bringt. Ein Trennstrich stand hier im
   Entwurf und las sich auf dem Pergament wie ein Kratzer – die Reihen unterscheiden
   sich ohnehin durch die Farbe der Plaketten. */
.setrow-info { min-width: 0; }
.setrow-chips { display: flex; flex-wrap: wrap; gap: .25rem; }
.setrow-wirkung { margin-top: .45rem; }
.chip { font-size: var(--fs-xs); font-weight: 700; padding: .12rem .45rem; border-radius: 999px;
        background: rgba(0, 0, 0, .10); color: var(--ink-soft);
        box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12); }
.chip-kraft { background: rgba(0, 0, 0, .18); color: var(--ink); }
/* Das WERT-SYMBOL aus der Statusanzeige ersetzt den Namen. Ohne das brachen vier Boni
   in der schmalen Übersicht-Spalte auf eine dritte Zeile um; mit Symbol passen sie in
   beiden Breiten in eine. Der Name steht im title. */
.chip-stat { display: inline-flex; align-items: center; gap: .22rem;
             background: rgba(31, 82, 20, .14); color: var(--gut-pergament); }
/* `.chip-orange` teilt sich die Farbe mit den orangenen Wert-Plaketten – dieselbe
   Aussage („das kommt vom Suffix"), also derselbe Ton, und der Wert steht nur einmal. */
.chip-stat.is-orange, .chip-orange { background: rgba(224, 145, 32, .22); color: #7a4d08; }
.chip-stat img { width: 13px; height: 13px; flex: 0 0 auto; }
.setlist { display: flex; flex-direction: column; gap: .5rem; }
/* AUFKLAPPER der Set-Liste (sets.php). Das <details> bekommt bewusst KEINE Klasse:
   Mit display:flex zählt der versteckte Inhaltsbereich als zweites Flex-Element, und
   unter dem zugeklappten Balken stand ein `gap` ins Nichts (gemessen 8px). Die
   Flex-Liste sitzt deshalb darin.

   Das summary trägt .btn; damit wird aus display:list-item ein block, und das native
   Dreieck fällt weg. Ohne eigenes Zeichen sähe man dem Balken nicht an, dass sich
   etwas dahinter verbirgt. */
.setlist > details > summary { cursor: pointer; list-style: none; }
.setlist > details > summary::-webkit-details-marker { display: none; }
.setlist > details > summary::after { content: ' ▾'; }
.setlist > details[open] > summary::after { content: ' ▴'; }
/* Abstand wie das gap der Liste. Greift nur im offenen Zustand – zugeklappt ist die
   innere Liste display:none und trägt keine Höhe bei. */
.setlist > details > .setlist { margin-top: .5rem; }
/* Helle Pergament-Kärtchen auf dem braunen Kartenbrett (statt milchigem Glas,
   das auf Braun grau wirkte). Dunkler Text -> --ink/--ink-soft lokal umdefiniert. */
.setrow {
    padding: .5rem .6rem; border-radius: 8px;
    /* Bezugspunkt für die Marke, die absolut in der Ecke sitzt. Rechts Platz für sie
       freihalten, sonst läuft der lange Setname darunter. */
    position: relative; padding-right: 4.2rem;
    /* 100% auto, NICHT 100% 100%: Die Breite füllt die Box, die Höhe folgt dem
       Seitenverhältnis. Das zweite 100% erzwang die Höhe und verzerrte das Motiv –
       bei einer Zeile, die mal 860 und mal 340 breit ist, wurden die Ranken oval.
       Verankert oben rechts, damit das Motiv dort sitzt und nach unten wegläuft. */
    background: var(--parch) url('../img/bg_box_set.jpg') no-repeat right top / 100% auto;
    box-shadow: inset 0 0 0 1px rgba(90, 60, 25, .5), 0 1px 3px rgba(0, 0, 0, .35);
    /* HIER MÜSSEN LITERALE STEHEN, keine Variablen: Diese Zeile SETZT --ink zurück,
       weil der Bereich auf hellem Pergament liegt und nicht im dunklen Kasten.
       `--ink: var(--ink)` wäre ein Selbstbezug – in CSS ungültig, die Variable
       behielte den geerbten hellen Wert und die Schrift verschwände (passiert am
       18.08.2026 genau so: Kontrast 1,07 in der Set-Liste). */
    --ink: #241a0e; --ink-soft: #6b5236;
    color: var(--ink);
}
.setrow.is-active { box-shadow: inset 0 0 0 2px var(--green), 0 1px 3px rgba(0, 0, 0, .35); }
/* ANKLICKBAR: Zeilen, die etwas anzulegen haben, tragen data-prefix. Der goldene Rand
   beim Überfahren ist der einzige Hinweis darauf – ohne Knopf gibt es sonst keinen. */
.setrow[data-prefix] { cursor: pointer; transition: box-shadow .12s; }
.setrow[data-prefix]:hover { box-shadow: inset 0 0 0 2px var(--gold), 0 2px 6px rgba(0, 0, 0, .45); }
.setrow.is-active[data-prefix]:hover { box-shadow: inset 0 0 0 2px var(--green), 0 2px 6px rgba(0, 0, 0, .45); }

/* --- MEDAILLEN-PLAKETTE: gezeichnete Medaille statt Text-Chip -------------------
   SIEBZEHN DATEIEN STATT SECHSUNDSECHZIG. Eine Medaille je Material (6) und ein
   Gold-Icon je Medaille (11) – nicht jede Kombination als eigenes Bild. Das Icon
   liegt auf der Medaille und übernimmt per `mix-blend-mode: luminosity` deren
   Farbton und Sättigung, behält aber seine eigene Helligkeit: Derselbe Pokal wird
   auf Holz braun, auf Silber silbern, auf Diamant blau. Eine neue Medaille kostet
   damit genau eine Datei.

   `isolation: isolate` ist PFLICHT und keine Vorsichtsmaßnahme: Ohne eigenen
   Stapelkontext mischt sich das Icon mit allem, was hinter der Kachel liegt – dem
   Pergament, dem Rahmen, dem Seitenhintergrund – statt nur mit der Medaille.

   Die Größe kommt von außen über --medaille-gr, damit dieselbe Plakette im Reiter und
   im Kampfergebnis unterschiedlich groß sein kann, ohne zweites Regelwerk. */
.medaille { position: relative; display: block; flex: 0 0 auto; isolation: isolate;
    width: var(--medaille-gr, 88px); height: var(--medaille-gr, 88px); }
.medaille img { position: absolute; display: block; }
.medaille-grund { inset: 0; width: 100%; height: 100%; }
/* 62 % statt 50 %: Das Band sitzt oben, die Scheibe liegt darunter aus der Mitte.
   42 % Breite ist gemessen – ab 46 % ragt das Icon über den Scheibenrand auf den
   Lorbeerkranz. */
.medaille-icon { left: 50%; top: 62%; width: 42%; transform: translate(-50%, -50%);
    mix-blend-mode: luminosity; }
/* NOCH NICHT VERLIEHEN: nur das Icon, entfärbt und blass – die Kachel bleibt damit
   lesbar (man sieht, WORUM es geht), ohne eine Medaille vorzutäuschen, die keiner
   hat. Ohne Medaille darunter gibt es nichts zum Mischen, also zurück auf normal. */
.medaille.is-leer .medaille-icon { top: 50%; width: 56%; mix-blend-mode: normal;
    filter: grayscale(1); opacity: .34; }

/* --- TAGESAUFGABEN --------------------------------------------------------------
   Jede Aufgabe ist eine `.kopf`-PLATTE – dasselbe Bauteil wie die Kopfzeile mit
   Name, Level und Ehre über der Paperdoll, samt `.xpstreifen` an der Unterkante.
   Deshalb steht hier fast nichts: Grund, Schrift, Kanten und Streifen bringt
   .kopf mit.

   Das löst nebenbei ein Problem, an dem eine eigene Zeile scheiterte: Die Liste
   steht einmal auf hellem Pergament (Seitenleiste) und einmal auf dunklem Braun
   (Seite). Jede selbst gesetzte Schriftfarbe ist an einem der beiden Orte falsch –
   die Platte bringt ihren eigenen dunklen Grund mit und ist an beiden richtig.

   PRÄFIX `tag-`, NICHT `ta-`: Das war schon vergeben – die Ablaufmeldung eines
   Tranks benutzt .ta-block/.ta-kopf/.ta-bild (siehe .trank-aus weiter unten). */

/* Die Überschrift ist eine `.badge-tab`-Plakette – dasselbe Rezept wie die Titel
   und Reiter auf den Kartenrändern. Als blanker Text war sie auf dem Pergament der
   Seitenleiste kaum zu lesen; die Plakette bringt ihren eigenen dunklen Grund und
   --box-ink gleich mit. Hier steht nur, dass sie mittig sitzt. */
.tag-titel { text-align: center; margin-bottom: .5rem; }
/* Die Plakette ist ein Link auf die ausführliche Seite. Ohne das hier zöge der
   Browser seinen Standardstrich unter die Schrift – die Farbe kommt schon von
   .badge-tab und sticht die Browservorgabe. */
.tag-titel .badge-tab { text-decoration: none; }

/* GLEICHER EINZUG WIE DIE MENÜKNÖPFE: --einzug kommt von .panel.pad und gilt dort
   für alles Gestapelte (Buttons, Statuswerte, Ressourcen). Ohne ihn standen die
   Aufgaben breiter als die Knöpfe darüber und darunter. Auf der Seite gibt es
   .panel.pad nicht, dort greift der Rückfall 0 und die Liste füllt die Karte. */
.tag-liste { display: flex; flex-direction: column; gap: .5rem;
    margin-inline: var(--einzug, 0); }
.tag-zeile { display: flex; flex-direction: column; gap: .15rem; }
.tag-text  { margin: 0 0 0 .2rem; }

/* `.kopf.kopf--tag`, nicht nur `.kopf--tag`: Ab 701px setzt eine Medienregel
   `.kopf { width: var(--puppe-breite) }` – die Kopfzeile ist dort so breit wie die
   Paperdoll. In der 340px-Leiste ragte die Platte damit weit über ihren Kasten
   hinaus. Zwei Klassen schlagen die eine, unabhängig von der Reihenfolge.
   Modifikator statt eigener Klasse – wie .kopf--merc. */
.kopf.kopf--tag { width: 100%; max-width: 100%; margin: 0;
    gap: .5rem; padding-left: .7rem; padding-right: .7rem; }
/* Der Lohn darf schrumpfen: Name und Stand sind wichtiger, und in der schmalen
   Leiste ist er der Erste, dem der Platz ausgeht. */
.kopf--tag .k-ehre { font-weight: 800; min-width: 0; overflow: hidden;
    text-overflow: ellipsis; }
.kopf--tag .k-name { flex: 0 1 auto; }

/* Erledigt: bleibt blass stehen, bis der Tag um ist. Wegnehmen wäre falsch, man
   will sehen, was man geschafft hat; die ganze BOX verschwindet ohnehin, sobald
   alle drei fertig sind. */
.tag-zeile.is-fertig { opacity: .5; }

/* Der Würfel ist ein blanker Knopf: In der schmalen Leiste wäre ein voller .btn
   neben dem Namen zu breit, und er soll auch nicht wie die Hauptsache aussehen –
   er ist der Notausgang für eine Aufgabe, die man nicht erfüllen kann. */
.tag-form { margin: 0; flex: 0 0 auto; line-height: 1; }
.tag-wuerfel { background: none; border: 0; padding: 0; cursor: pointer;
    font-size: var(--fs-m); line-height: 1; opacity: .8; }
/* Kostet der Wurf etwas, steht der Preis am Würfel – nicht blass machen, sondern
   sagen was es kostet (dieselbe Regel wie bei der Krone an Premium-Knöpfen). */
.tag-wuerfel.kostet { display: inline-flex; align-items: center; gap: .12rem; }
.tag-preis { font-size: var(--fs-2xs); font-weight: 700; color: var(--gold-hell); }
.tag-wuerfel:hover { opacity: 1; transform: rotate(-15deg); }

.tag-serie { margin: .6rem 0 0; text-align: center; font-size: var(--fs-s); color: var(--ink); }
.tag-meldung { color: var(--schlecht); font-size: var(--fs-s); font-weight: 700; }

/* --- SCHAMANENHÜTTE (/schamane) -------------------------------------------------
   FAST NICHTS EIGENES. Der Vorrat benutzt .aw-kopf/.aw-zahl aus der Arena-Bilanz, der
   Sammel-Auflöser .mv-* vom Schnellverkauf des Händlers – beides sind dieselben
   Aufgaben, und eine zweite Optik dafür wäre eine, die auseinanderläuft.

   Übrig bleiben drei Dinge, die es sonst nirgends gibt: die Fehlerzeile, das Raster
   der einzelnen Teile und ihr Ausbeute-Knopf. */
.sch-fehler { color: var(--schlecht); font-weight: 700; font-size: var(--fs-s); }

/* KLEINERE KACHELN als in den Taschen (Betreiber 26.08.2026): Hier stehen alle
   auflösbaren Teile auf einmal, oft mehrere Dutzend – in Taschengröße wäre die Spalte
   eine Bildschirmlänge Scrollen.

   ABER MIT DERSELBEN MECHANIK wie .bag-grid: feste Spaltenzahl, Deckel nur 72 statt
   110px. Der erste Versuch stand auf `auto-fill, minmax(56px, 72px)` und schrumpfte
   damit NICHT mit – gemessen am 26.08.2026: Auf einer 340px-Karte waren die Taschen
   bei 54px, die Hütte bei 72. „Kleiner" wurde auf dem Handy also größer. */
/* Überschrift je Materialsorte. Der Farbstein ist die Item-Kachel in klein –
   dieselbe Seltenheitsoptik wie in den Taschen. .item-tile ist sonst 100 % breit
   mit 1:1-Seitenverhältnis; zwei Klassen, weil .item-tile weiter unten im Blatt
   steht und bei gleicher Spezifität gewinnen würde. */
.sch-gruppe { display: flex; align-items: center; gap: .4rem; margin: .9rem 0 .3rem; }
.sch-gruppe .sch-stein { width: 20px; height: 20px; aspect-ratio: auto; cursor: default;
    flex: 0 0 auto; }

.sch-liste { display: grid; grid-template-columns: repeat(4, minmax(0, 72px));
    justify-content: center; gap: .5rem; margin-top: .8rem; }
@media (min-width: 360px) { .sch-liste { grid-template-columns: repeat(5, minmax(0, 72px)); } }
.sch-teil { display: flex; flex-direction: column; gap: .2rem; min-width: 0; }
.sch-teil form { margin: 0; }
.sch-los:disabled { opacity: .45; cursor: not-allowed; }
/* Der Knopf nennt die AUSBEUTE, nicht „Auflösen" – das ist die Entscheidung, und sie
   gehört auf den Knopf statt in einen Hinweis daneben. */
.sch-los { width: 100%; padding: .15rem .2rem; border: 0; border-radius: 6px;
    cursor: pointer; font-size: var(--fs-xs); font-weight: 700; line-height: 1.1;
    background: var(--holz-dunkel); color: var(--box-ink);
    box-shadow: 0 2px 0 var(--tief); }
.sch-los:hover { background: #6b4f2a; }

/* --- Werkbank -------------------------------------------------------------------
   Die beiden Ablagefelder sind .equip-slot – dieselben Kästen wie auf der Puppe und
   unter den Tränken. Sie sagen optisch schon „hier gehört etwas hinein", und den
   Puls (.slot-hint) gibt es auch schon. Hier steht nur die Anordnung. */
.sch-bank { display: flex; gap: .8rem; justify-content: center; margin: .6rem 0 .8rem; }
/* .slot bringt Kasten, Rundung und Innenkante mit – aber keine Größe: Auf der Puppe
   kommt die aus dem Raster. Hier braucht der Platz sie selbst. */
.sch-platz { display: flex; flex-direction: column; align-items: center; gap: .2rem;
    width: 88px; }
.sch-platz .equip-slot { width: 100%; }
.sch-rolle { font-weight: 800; font-size: var(--fs-s); }
/* Die Auswahlliste erbt die Schriftgröße nicht von selbst – ein <select> bringt
   seine eigene mit und stünde sonst größer da als der Text darüber. */
.sch-ziel { margin-top: .3rem; width: 100%; font: inherit; font-size: var(--fs-s);
    padding: .2rem .3rem; border-radius: 6px; }

.sch-rezepte { display: flex; flex-direction: column; gap: .45rem; }
.sch-rezept { display: flex; flex-direction: column; gap: .15rem; margin: 0;
    padding: .45rem .6rem; border-radius: 10px; background: rgba(255, 255, 255, .1); }
.sch-rezept .btn { margin: .3rem 0 0; }
/* Unmögliche Rezepte bleiben STEHEN, nur blass und mit Grund. Ein Rezept, das
   einfach fehlt, lässt den Spieler rätseln, warum es fehlt. */
.sch-rezept.is-zu { opacity: .5; }

/* Ausgewähltes Teil in der Liste: derselbe Goldring wie ein angehaktes Item beim
   Schnellverkauf (.mv-cb:checked ~ .item-tile). */
.sch-teil.is-gewaehlt .item-tile { box-shadow: 0 0 0 2px var(--gold); }

/* KOSTENAMPEL der Rezepte: grün = im Vorrat, rot = fehlt. Farben wie im Vorrat
   darüber, damit „grün" auf der Seite überall dasselbe heißt (--gut-plakette).
   Fett, weil die Zahl die Aussage trägt und nicht der Name daneben. */
.sch-mat { font-weight: 700; }
.sch-mat.is-win  { color: var(--gut-plakette); }
.sch-mat.is-loss { color: var(--schlecht-plakette); }

/* --- ERTRAGSREGLER der Quests (/quests) ----------------------------------------
   Ein Schieber zwischen Gold und Erfahrung. Die Schiene zeigt beides zugleich: der
   Verlauf sagt, wohin man schiebt, die fünf Striche sagen, wo die runden Werte
   liegen. Das EINRASTEN steckt in quests.js – CSS kann Aussehen, nicht Verhalten.

   Farben aus dem Bestand: --gold für die Goldseite, --gut-plakette für die
   Erfahrungsseite (dieselbe Bedeutung wie überall auf braunem Grund). */
.q-fokus { margin-top: .9rem; }
.q-fokus-kopf { display: flex; align-items: baseline; justify-content: space-between;
    gap: .5rem; font-size: var(--fs-s); margin-bottom: .35rem; }
.q-fokus-kopf .q-fokus-l { color: var(--gold-hell); }
.q-fokus-kopf .q-fokus-r { color: var(--gut-plakette); }
.q-fokus-wert { color: var(--box-ink); font-size: var(--fs-s); }

.q-fokus-reg { -webkit-appearance: none; appearance: none; width: 100%;
    height: 22px; background: transparent; cursor: pointer; }
/* Die Schiene: Verlauf plus fünf Striche. Der Verlauf liegt UNTEN im Stapel, die
   Striche darüber – sonst verschluckt die Farbe sie. */
.q-fokus-reg::-webkit-slider-runnable-track { height: 8px; border-radius: 999px;
    background:
        repeating-linear-gradient(90deg, transparent 0 calc(25% - 1px),
            rgba(0,0,0,.55) calc(25% - 1px) calc(25% + 1px), transparent calc(25% + 1px) 25%),
        linear-gradient(90deg, var(--gold) 0%, var(--gut-plakette) 100%);
    box-shadow: inset 0 1px 2px rgba(0,0,0,.5); }
.q-fokus-reg::-moz-range-track { height: 8px; border-radius: 999px;
    background:
        repeating-linear-gradient(90deg, transparent 0 calc(25% - 1px),
            rgba(0,0,0,.55) calc(25% - 1px) calc(25% + 1px), transparent calc(25% + 1px) 25%),
        linear-gradient(90deg, var(--gold) 0%, var(--gut-plakette) 100%);
    box-shadow: inset 0 1px 2px rgba(0,0,0,.5); }
/* Der Griff: gleiche Sprache wie die Rahmen – Goldrand auf dunklem Kern. */
.q-fokus-reg::-webkit-slider-thumb { -webkit-appearance: none; appearance: none;
    width: 20px; height: 20px; margin-top: -6px; border-radius: 50%;
    background: var(--dunkel); border: 2px solid var(--gold);
    box-shadow: 0 1px 3px rgba(0,0,0,.6); }
.q-fokus-reg::-moz-range-thumb { width: 20px; height: 20px; border-radius: 50%;
    background: var(--dunkel); border: 2px solid var(--gold);
    box-shadow: 0 1px 3px rgba(0,0,0,.6); }
.q-fokus-reg:focus-visible { outline: 2px solid var(--gold-hell); outline-offset: 3px; }

.q-fokus-marken { display: flex; justify-content: space-between;
    font-size: var(--fs-xs); color: var(--box-ink); opacity: .75; margin-top: .2rem; }



/* --- MEDAILLENSCHRANK (/medaillen) ---------------------------------------------
   Eine Zeile je Medaille: links die große Plakette mit Namen, rechts die volle
   Stufenleiter. Zwei Spalten am Desktop, untereinander auf dem Handy – die Leiter
   braucht die Breite, der Kopf nicht.

   SECHS SPALTEN, fest. Mit umbrechendem Flex fielen je nach Fensterbreite fünf
   Felder in die erste und eines allein in die zweite Reihe – der schlechteste
   aller Umbrüche. Sechs Spalten gehen für jede Medaille auf: die meisten haben
   genau sechs Stufen, Setträger achtzehn und damit drei volle Reihen. */
.med-summe { margin: 0 0 .8rem; color: var(--ink-soft); }
.med-liste { display: flex; flex-direction: column; gap: .7rem; }
/* RASTER, nicht Flex: Links die Plakette über beide Zeilen, rechts oben die
   Stufenleiter und darunter der Erklärtext – der bleibt also in der RECHTEN Spalte
   und läuft nicht unter die Plakette. Mit Flex bräuchte das eine Hilfsbox um
   Leiter und Text; im Raster sind es zwei Zeilenangaben. */
.med-reihe { display: grid; grid-template-columns: 150px minmax(0, 1fr);
    gap: .5rem .8rem; align-items: start;
    padding: .7rem; border-radius: 12px; background: rgba(255, 255, 255, .28); }
.med-kopf { grid-column: 1; grid-row: 1 / span 2; align-self: center;
    display: flex; flex-direction: column; align-items: center; gap: .2rem;
    text-align: center; }
.med-text { grid-column: 2; grid-row: 2; margin: 0; }
.med-name { font-size: var(--fs-m); }
.med-stand { font-size: var(--fs-s); color: var(--ink-soft); }
.med-stufen { grid-column: 2; grid-row: 1;
    display: grid; grid-template-columns: repeat(6, minmax(0, 1fr));
    gap: .4rem; min-width: 0; }

/* Ein Stufenfeld. NICHT ERREICHT ist der Normalfall – deshalb trägt DER die
   Grundregel und `is-hab` hebt hervor, nicht umgekehrt. Entfärbt und blass zeigt
   die Leiter, was noch kommt, ohne dass es wie ein Besitz aussieht. */
.med-stufe { display: flex; flex-direction: column; align-items: center; gap: .1rem;
    min-width: 0; padding: .35rem .2rem; border-radius: 9px; text-align: center;
    font-size: var(--fs-s); line-height: 1.15; color: var(--ink-soft);
    background: rgba(0, 0, 0, .07); }
/* Größe kommt über --medaille-gr aus dem Markup (die Plakette rechnet damit auch
   die Lage des Icons aus). Hier nur noch der Zustand: künftige Stufen entfärbt. */
.med-stufe .med-mini { filter: grayscale(1); opacity: .38; }
.med-stufe.is-hab { color: var(--ink); background: rgba(224, 160, 32, .16);
    box-shadow: inset 0 0 0 1px var(--gold); }
.med-stufe.is-hab .med-mini { filter: none; opacity: 1; }
/* GROSS BEIM ÜBERFAHREN – nur bei erreichten Stufen. Die künftigen bleiben stumm:
   Dort gibt es nichts zu betrachten, und eine Reaktion würde Besitz vortäuschen.

   Das Stufenfeld IST hier schon die Fühlfläche (es liegt in einer Rasterzelle und
   wächst nicht mit), deshalb braucht es keine zusätzliche Zelle wie bei den Reihen –
   nur `pointer-events: none` am Bild, damit die große Medaille den Weg zum Nachbarn
   nicht blockiert. 34px × 5,5 = 187, wieder die echte Auflösung der Datei. */
.med-stufe .med-mini { pointer-events: none; transition: transform .12s ease; }
.med-stufe.is-hab { position: relative; }
.med-stufe.is-hab:hover { z-index: 999; }
.med-stufe.is-hab:hover .med-mini { transform: scale(5.5); }
.med-ziel { font-weight: 700; }
.med-datum, .med-lohn, .med-selten { font-size: var(--fs-xs); }
/* Eigener dunkler Goldton, KEIN --gold (#e0a020): Der Akzentton ist für Rahmen auf
   dunklem Grund gedacht und käme hier auf dem goldgetönten Feld auf 1,7:1. Gemessen
   gegen den tatsächlichen Untergrund des erreichten Feldes (#ecd7ab): 5,4:1. */
.med-selten { color: var(--gold-pergament); font-weight: 700; }

@media (max-width: 560px) {
    /* Eine Spalte: Kopf, Leiter, Text untereinander. Der Kopf legt sich dabei
       quer – Plakette links, Name und Stand rechts daneben. Die Zeilen-/Spalten-
       Angaben von oben müssen dafür zurückgenommen werden, sonst bliebe der Kopf
       auf zwei Zeilen gespannt und Leiter wie Text stünden neben ihm. */
    .med-reihe { grid-template-columns: minmax(0, 1fr); }
    .med-kopf, .med-stufen, .med-text { grid-column: 1; grid-row: auto; }
    .med-kopf { flex-direction: row; text-align: left; gap: .5rem; align-items: center; }
    .med-kopf .medaille { --medaille-gr: 72px; }
    /* Drei statt sechs: Sechs Felder nebeneinander wären auf dem Handy je gut
       vierzig Pixel breit – schmaler als „45.000" darin. Drei teilen jede Leiter
       sauber (6 = 2 Reihen, 18 = 6 Reihen). */
    .med-stufen { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
/* Kopf der Set-Zeile: Punkt + Name, eine Zeile, kein Umbruch. */
.setrow-head { display: flex; align-items: center; gap: .45rem; flex-wrap: nowrap; margin-bottom: .3rem; }
.setrow-head b { flex: 1 1 auto; }
.setrow-head .dot { width: 10px; height: 10px; border-radius: 50%; flex: 0 0 auto; box-shadow: 0 0 0 1px rgba(0, 0, 0, .25); }
.dot-g { background: var(--set-green); }
.dot-o { background: var(--set-orange); }
.dot-n { background: rgba(0, 0, 0, .25); }
/* --- Statuswerte ------------------------------------------------------- */
/* Volle Box in der linken Seitenleiste (nur Desktop – .sidebar ist sonst aus).
   Inhalt wird beim Söldner-Wechsel per JS getauscht (syncSideStats). */
/* Einzug wie die Buttons im Kasten darüber (--einzug). Die Trennlinie liegt
   dadurch ebenfalls auf dieser Kante statt über die volle Breite zu laufen. */
.side-stats { margin: .6rem var(--einzug, 15px) 0; border-top: 1px solid rgba(0, 0, 0, .15); padding-top: .5rem;
              display: flex; flex-direction: column; gap: .3rem; }

/* Kompakte Zeile unter der Puppe – der Handy-Ersatz für die Seitenleiste.
   Zeigt nur die Wirkung (Angriff/Leben/…), der Grundwert steht im title. */
/* RASTER statt Fließtext: Jeder Wert hat einen festen Platz, dadurch stehen Icons,
   Bezeichnungen und Zahlen in beiden Zeilen bündig untereinander. Vorher rutschte
   alles je nach Zahlenlänge, und mit vierstelligen Werten sah die Leiste zerrissen
   aus. Vier Spalten, zwei Zeilen – sieben Werte, das achte Feld bleibt frei. */
.stat-strip {
    /* Vier Spalten: Bei sieben Werten ergibt das genau ZWEI volle Zeilen (4 + 3).
       Mit drei Spalten stand in der dritten Zeile ein einzelner Wert allein. */
    display: grid; grid-template-columns: repeat(4, 1fr); gap: .3rem .5rem;
    padding: .5rem .5rem; border-radius: 8px;   /* Abstand: Container-gap */
    cursor: pointer;   /* aufklappbar – siehe #stat-voll */
    background: rgba(255, 255, 255, .28); font-size: var(--fs-s);
}
.ss-item { display: flex; align-items: center; justify-content: center; gap: .35rem; min-width: 0; }
.ss-ico { width: 22px; height: 22px; flex: 0 0 auto; }
/* Bezeichnung nur als Tooltip – das Icon sagt, worum es geht. Ohne Text passen
   Symbol und Zahl groß nebeneinander und alle Spalten sind gleich breit. */
.ss-item i { display: none; }
.ss-item b { white-space: nowrap; font-size: var(--fs-l); color: var(--ink); }
/* Vom Premium-Trank angehoben – DUNKLES Grün, weil dieser Streifen (Handy/Tablet)
   auf hellem Pergament liegt und nicht auf der braunen Plakette. Das helle Grün der
   Seitenleiste (var(--gut-plakette)) käme hier auf 1,06:1 Kontrast, wäre also unsichtbar.
   Gemessen: #1f5214 kommt auf 5,97:1 gegen den Untergrund und 1,85 gegen die
   normale Schrift (var(--ink)) – lesbar UND klar als andere Farbe erkennbar.
   Die zwei Grüntöne sind deshalb kein Versehen, sondern zwei Untergründe. */
.ss-item.is-prem b { color: var(--gut-pergament); }
.ss-who { font-weight: 700; color: var(--gut); }

/* --- Upgrade-Marker: weißer Doppelpfeil mit 1px schwarzer Kontur, oben links --- */
/* Kein Badge mehr. Oben links ist frei (Stufe oben rechts, Sterne unten). Die
   Kontur entsteht im SVG: dicke schwarze Linie drunter, dünne weiße drüber. */
.up-arrow {
    position: absolute; top: 2px; left: 2px; z-index: 4;
    width: 13px; height: 16px;
    background: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 15'%3E%3Cpath d='M2 6.5L6 3L10 6.5M2 11.5L6 8L10 11.5' fill='none' stroke='%23000' stroke-width='4' stroke-linecap='round' stroke-linejoin='round'/%3E%3Cpath d='M2 6.5L6 3L10 6.5M2 11.5L6 8L10 11.5' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") center / contain no-repeat;
    font-size: 0; pointer-events: none;
    animation: up-arrows 1.5s ease-in-out infinite;
}
/* Sanftes Auf-Wippen – dezent, „hebt sich = besser". */
@keyframes up-arrows { 0%, 100% { transform: translateY(0); } 50% { transform: translateY(-1.5px); } }

/* --- Marker oben RECHTS: Gegenstück zum Upgrade-Pfeil oben links -------------
   Drei Zeichen teilen sich diesen Platz, weil nie zwei davon gleichzeitig auftreten:
   das Sperr-Schloss (nur eigener Besitz), sowie 💎 und 🪙 für die Kaufbarkeit beim
   Händler (dort ist nichts gesperrt).

   Gleiche Höhe und Größe wie .up-arrow, damit beide Marker auf einer Linie
   sitzen. Der Textschatten ersetzt die Kontur, die der Pfeil im SVG hat – ohne
   ihn verschwindet das Schloss auf hellen Item-Grafiken. Nicht animiert: Der
   Pfeil wippt, um Aufmerksamkeit zu holen, das Schloss ist nur ein Zustand. */
.item-lock, .item-nurdia, .item-teuer {
    position: absolute; top: 1px; right: 2px; z-index: 4;
    font-size: 12px; line-height: 1;
    text-shadow: 0 0 2px #000, 0 0 3px #000, 0 1px 1px #000;
    pointer-events: none;
}

/* --- Sperrmodus ------------------------------------------------------------
   Der Umschalter trägt die Reiter-Optik (.badge-tab-Verwandtschaft über
   .bag-sort), sitzt aber LINKS in der Reihe direkt bei den Taschen-Reitern –
   .bag-sort schiebt sich mit margin-left:auto selbst nach rechts weg. */
.bag-lockmode {
    padding: .2rem .55rem;
    background: var(--box-bg) url('../img/bg_braune_box.jpg') no-repeat;
    border: 2px solid #000; border-radius: 7px;
    box-shadow: inset 0 0 0 2px #655e4d, inset 0 0 0 4px var(--dunkel);
    font: inherit; font-size: var(--fs-m); line-height: 1.3;
    text-shadow: 0 1px 2px rgba(0, 0, 0, .8);
    white-space: nowrap; cursor: pointer;
}
.bag-lockmode:hover { box-shadow: inset 0 0 0 2px var(--green), inset 0 0 0 4px var(--dunkel); }
/* Aktiv = deutlich anders, nicht nur „etwas heller": Solange der Modus läuft,
   tun Klicks etwas ANDERES als sonst. Das muss man auf einen Blick sehen. */
.bag-lockmode[aria-pressed="true"] {
    box-shadow: inset 0 0 0 2px var(--gold), inset 0 0 0 4px var(--dunkel),
                0 0 8px rgba(224, 160, 32, .55);
}
/* Raster im Modus warm überlagert – Zeichen, dass hier gerade nicht verschoben,
   sondern gesperrt wird. Nur die Taschen, nicht die Puppe: Angelegtes lässt
   sich zwar auch sperren, aber verkauft wird aus der Tasche. */
/* SPERRMODUS-Kennzeichnung.

   :not(.vendor-grid) ist Pflicht: Das Händlerregal benutzt DIESELBE Klasse
   (`class="bag-grid vendor-grid"` in haendler.php), weil es dasselbe Spaltenraster
   braucht. Ohne den Ausschluss wird auch das Regal umrandet – dort gibt es aber
   nichts zu sperren, die Ware gehört dem Spieler noch nicht.

   `outline` statt `box-shadow: inset`: Der inset-Ring lag an der INNENkante des
   Rasters, und genau darauf sitzen die äußeren Kacheln – mit vollen Taschen war er
   unsichtbar. Ein outline wird AUSSERHALB gezeichnet und von nichts verdeckt. */
.is-sperrmodus .bag-grid:not(.vendor-grid),
.is-sperrmodus .sch-liste {
    outline: 2px solid rgba(224, 160, 32, .85);
    outline-offset: 3px;
    border-radius: 8px;
}
/* Zusätzlich jede ANKLICKBARE Kachel ringen – nur in Taschen- und Ausrüstungs-Slots.
   Das Regal des Händlers bleibt außen vor. So sieht man nicht nur „ich bin im
   Sperrmodus", sondern auch WORAUF man klicken kann. */
.is-sperrmodus .bag-slot .item-tile,
.is-sperrmodus .sch-teil .item-tile,
.is-sperrmodus .equip-slot .item-tile {
    cursor: pointer;
    box-shadow: 0 0 0 2px rgba(224, 160, 32, .9);
}
/* Kacheln im Regal verhalten sich im Sperrmodus wie immer – kein Zeigefinger, der
   eine Klickmöglichkeit verspricht, die es nicht gibt. */
.is-sperrmodus .vendor-slot .item-tile { cursor: grab; }

/* --- Bestätigungsdialog (statt des hässlichen Browser-confirm) --------- */
.ape-modal-ov {
    position: fixed; inset: 0; z-index: 200;
    display: flex; align-items: center; justify-content: center;   /* Fallback-Mitte */
    background: rgba(0, 0, 0, .45);
}
/* .card gibt Textur + Ringe + hellen Text; hier fix positioniert statt im Fluss. */
.ape-modal {
    position: fixed; max-width: min(320px, calc(100vw - 16px));
    padding: 1rem; text-align: center;
    box-shadow: inset 0 0 0 2px #655e4d, inset 0 0 0 4px var(--dunkel), 0 12px 32px rgba(0, 0, 0, .5);
}
.am-msg { margin: 0 0 .9rem; font-size: var(--fs-l); line-height: 1.4; }
.am-warn { display: inline-block; margin-top: .3rem; font-size: var(--fs-s); color: #ff9a3d; }
/* flex-wrap, weil diese Reihe auch die Item-Knöpfe auf /markt trägt (inventar.js
   showInfo): „Verkaufen für 🪙 12.480" neben „Anlegen" passt auf schmalen Handys
   sonst nicht mehr in eine Zeile und liefe aus der Karte. Im Dialog ändert sich
   nichts – dort brechen die kurzen Ja/Nein-Knöpfe nie um. */
.am-btns { display: flex; flex-wrap: wrap; gap: .6rem; justify-content: center; }
.am-btns .badge-tab { position: static; cursor: pointer; padding: .3rem 1rem; }
.am-yes { background-color: var(--green); box-shadow: inset 0 0 0 2px var(--green-d), inset 0 0 0 4px var(--dunkel); }

/* --- Markt (rechte Spalte auf /markt, statt der Statuswerte) ----------- */
/* Erbt das Kachelraster der Taschen – Händler-Ware sieht aus wie Inventar-Ware
   und wird genauso bedient (antippen -> Info-Feld -> Kaufen). Händler nutzt
   dieselbe .bag-grid-Regel wie das Inventar, damit Waren und Taschen-Items
   GLEICH groß sind. Keine eigene Spaltenregel nötig.
   Vendor::SLOTS ist auf die Spaltenzahl abgestimmt: 15 Waren = 3 volle Reihen
   zu 5 Spalten (unter 960px fällt das Raster auf 4 Spalten zurück, dann bleibt
   die letzte Reihe angebrochen – das ist die schmale Ansicht, dort zählt die
   Slot-Größe mehr als ein glattes Rechteck). */

/* Der Knopf ist ein Rasterfeld und hängt fest in den letzten beiden Spalten.
   Dadurch füllt er die Lücke, die verkaufte Ware hinterlässt, statt eine eigene
   Zeile zu belegen – und rutscht bei vollem Regal sauber in die nächste.
   Spaltennummern müssen zur Spaltenzahl des Rasters passen (siehe .bag-grid). */

/* Muss zur Spaltenzahl passen und wandert deshalb auf dieselbe Grenze wie das
   Raster (vorher 960px, seit dem 25.08.2026 sind es ab 360px fünf Spalten).
   Unter 360px bleibt es bei vier – dort greift die Grundregel darüber. */
@media (min-width: 360px) { .btn-vendor { grid-column: 4 / span 2; } }   /* 5-Spalten-Raster: wieder die letzten beiden */
/* Auswahl der Händler-Sorte, links neben dem Nachschub-Knopf. Sitzt im selben
   Raster und nimmt den Platz ein, den der Knopf übrig lässt. */
.vendor-sklaven {
    grid-column: 1 / 3; align-self: center;
    display: flex; align-items: center; gap: .35rem;
    font-size: var(--fs-s); font-weight: 700; line-height: 1.15; cursor: pointer;
    color: var(--box-ink-soft);
}
.vendor-sklaven input { accent-color: var(--gold); cursor: pointer; flex: 0 0 auto; }
.vendor-sklaven:hover { color: var(--box-ink); }
/* Ohne Händlertrank: NICHT ausgeblendet und nicht gesperrt – die Krone im Text ist
   das ganze Signal (Betreiber-Vorgabe 17.08.). Blasse Bedienelemente lesen sich wie
   ein Fehler; und ein `disabled`-Element zeigt in vielen Browsern keinen Tooltip,
   der Hinweis käme also gar nicht an. Wer es trotzdem versucht, bekommt beim
   Nachschub eine klare Meldung vom Server. */
/* Dieselbe Grenze wie Raster und Knopf: Bei fünf Spalten sitzt der Knopf auf 4–5,
   die Auswahl muss also bis Spalte 3 reichen. Blieb das bei 960px stehen, klaffte
   zwischen 360 und 959px eine leere dritte Spalte. */
@media (min-width: 360px) { .vendor-sklaven { grid-column: 1 / 4; } }

/* --- Adminbereich ----------------------------------------------------- */
.adm-ok  { color: var(--gut); font-weight: 700; }
.adm-err { color: var(--schlecht); font-weight: 700; }
.adm-h4  { margin: 1.2rem 0 .4rem; font-size: var(--fs-m); }
.adm-tbl { width: 100%; border-collapse: collapse; margin: .8rem 0; font-size: var(--fs-m); }
.adm-tbl th, .adm-tbl td { text-align: left; padding: .3rem .5rem; border-bottom: 1px solid rgba(0, 0, 0, .12); }
.adm-tbl th { color: var(--ink-soft); font-size: var(--fs-s); text-transform: uppercase; letter-spacing: .04em; }
.adm-sample { padding: .5rem .6rem; border-radius: 8px; background: rgba(255, 255, 255, .28); margin-bottom: .4rem; font-size: var(--fs-m); }

/* Grafiken-Übersicht (/admin/grafiken): Tabelle wie .adm-tbl, aber die
   Bild-Zellen wachsen beim Hover per CSS-Transform auf die native Auflösung
   (Quellbilder sind 256px) – kein JS, kein zweiter Request. KEIN overflow-x
   hier (bewusst, anders als andere Tabellen): overflow:auto + ein Kind, das
   per transform über den Rand hinauswächst, lässt Chrome/Firefox einen
   Scrollbalken einblenden, obwohl optisch nichts abgeschnitten ist – die
   Tabelle ist mit 6 schmalen Spalten eh schmal genug fürs Desktop-Admin-Backend. */
.gal-wrap { overflow-x: visible; }
.gal-tbl td, .gal-tbl th { white-space: nowrap; }
.gal-cell { width: 64px; text-align: center; overflow: visible; }
/* Die Entwicklungsstufen in den Einstellungen hängen sich an DIESELBEN Regeln – ein
   Selektor mehr statt einer zweiten Lupe. Die Maße hier stören dort nicht: `.ein-stufe img`
   steht weiter unten und ist genauer (0,1,1 gegen 0,1,0), überschreibt Breite, Höhe,
   Rundung und Hintergrund also ohnehin. Übrig bleibt genau das Gewünschte – Zeiger,
   Übergang und die Vergrößerung beim Überfahren. */
.gal-thumb, .ein-stufe img {
    width: 64px; height: 64px; object-fit: contain; border-radius: 6px;
    background: rgba(255, 255, 255, .1); cursor: zoom-in;
    transition: transform .12s ease; transform-origin: center;
    /* Maus "spürt" nur die schmale Zelle, nicht das (beim Hover riesige) Bild
       selbst – sonst blockiert das vergrößerte Bild den Wechsel zur Nachbarzelle,
       bis man seine ganze aufgeblähte Fläche verlassen hat. */
    pointer-events: none;
}
/* Stapel-Priorität sitzt an der ZELLE, nicht am Bild – z-index auf einzelnen
   Bildern verhält sich in Tabellen unzuverlässig (Nachbarzeilen können
   durchblitzen), auf der Zelle ist es robust. */
.gal-cell:hover, .ein-stufe:hover { position: relative; z-index: 999; }
/* … das allein reicht aber NICHT: `.panel` hat `isolation: isolate` und bildet damit
   einen eigenen Stapelkontext. Darin liegt `.content` (mit der Galerie) auf 1, die
   Rahmenkanten auf 2 und die Ecken auf 3. Ein z-index am Bild – und sei es 999 –
   sortiert nur INNERHALB von `.content` und kommt nie über den Rahmen; das vergrößerte
   Bild verschwand deshalb dahinter.

   Also hebt sich `.content` selbst, solange ein Bild überfahren wird. `:has()` prüft
   das am Elternteil (im Projekt schon bei .pl-komplett in Gebrauch). 5 liegt über den
   Ecken; danach fällt es sofort wieder zurück, der Rahmen liegt also nur während des
   Überfahrens hinten. */
.content:has(.gal-cell:hover), .content:has(.ein-stufe:hover) { z-index: 5; }
.gal-cell:hover .gal-thumb, .ein-stufe:hover img {
    transform: scale(4);
    box-shadow: 0 12px 32px rgba(0, 0, 0, .65); background: rgba(20, 15, 8, .95);
}
/* Gleiche Ring-Verkleinerung wie im Spiel (.item-tile[data-type="6"] .art) –
   die Übersicht soll zeigen, wie's tatsächlich aussieht. Beim Hover-Zoom aber
   NICHT gedämpft: da will man das Bild in echter Detailgröße sehen. */
.gal-thumb.is-ring { transform: scale(.7); }
.gal-cell:hover .gal-thumb.is-ring { transform: scale(4); }
.adm-diff { display: flex; flex-wrap: wrap; gap: .3rem .8rem; margin-top: .25rem; }
.adm-stat { font-size: var(--fs-s); white-space: nowrap; }
.adm-stat i { color: var(--ink-soft); font-style: normal; margin-right: .2rem; }
.adm-new { color: var(--gut); font-style: normal; font-weight: 700; font-size: var(--fs-xs); }
/* Handlungsknöpfe stehen MITTIG. Ein einzelner Knopf am linken Rand sieht aus wie
   vergessen; in der Mitte ist er die Aufforderung, die er sein soll. Gilt für alle
   Formulare dieser Art – Quest-Kampf, Schattenkampf, Adminaktionen. */
.adm-form { margin-top: 1.2rem; display: flex; align-items: center; justify-content: center;
    gap: .8rem; flex-wrap: wrap; }
.adm-warn { font-size: var(--fs-s); color: var(--schlecht); }

/* --- Reiter-Leiste. Trotz des adm-Namens NICHT mehr im Adminbereich (der hat
   seit 2026-08-13 echte Menüpunkte) – nur noch die Rangliste nutzt sie für ihre
   Listen- und Seitengrößen-Umschalter (rangliste.php, .rl-size). --- */
.adm-nav { display: flex; gap: .5rem; margin-bottom: 1rem; flex-wrap: wrap; }
.adm-navlink {
    padding: .3rem .8rem; border-radius: 7px; font-size: var(--fs-m); font-weight: 700;
    color: var(--box-ink-soft); text-decoration: none;
    box-shadow: inset 0 0 0 2px rgba(0, 0, 0, .2);
}
.adm-navlink:hover { color: var(--box-ink); }
.adm-navlink.active { color: #fff; background: var(--green); box-shadow: none; }

/* --- Balance-Editor ---
   Die Reiter-Regeln (.adm-tabs/.adm-tab/.adm-pane) sind entfallen: jede Gruppe
   ist seit 2026-08-13 eine eigene Seite und ein eigener Menüpunkt. */
.adm-intro { font-size: var(--fs-m); margin: 0 0 .8rem; }
.adm-grid {
    display: grid; gap: .7rem .9rem;
    grid-template-columns: repeat(auto-fill, minmax(210px, 1fr));
}
.adm-fld { display: flex; flex-direction: column; gap: .18rem; font-size: var(--fs-s); }
.adm-lbl { color: var(--ink-soft); font-weight: 700; }
/* select und textarea SEHEN AUS WIE input. Vorher galt die Regel nur für `input`,
   und ein Auswahlfeld daneben trug das Aussehen des Betriebssystems – auf braunem
   Grund ein Fremdkörper. */
.adm-fld input, .adm-fld select, .adm-fld textarea {
    padding: .35rem .5rem; border-radius: 6px;
    border: 1px solid rgba(0, 0, 0, .25); background: rgba(255, 255, 255, .55);
    font: inherit; font-size: var(--fs-m); color: #241a0d; width: 100%;
}
.adm-fld input:focus, .adm-fld select:focus, .adm-fld textarea:focus {
    outline: 2px solid var(--green); border-color: var(--green);
}

/* GESTAPELTES FORMULAR über die volle Breite. Der Balance-Editor ordnet viele kleine
   Zahlenfelder im Raster an (.adm-grid); ein Formular mit Betreff und Fließtext
   braucht das Gegenteil – untereinander, jedes so breit wie die Karte. */
.nl-form { display: flex; flex-direction: column; gap: .9rem; margin-top: 1rem; }
.nl-form textarea { min-height: 22rem; resize: vertical; line-height: 1.45; }
.adm-help { color: var(--ink-soft); font-size: var(--fs-s); line-height: 1.25; }
.adm-save { margin-top: 1.2rem; display: flex; align-items: center; gap: .8rem; flex-wrap: wrap; }

/* Söldner ins Inventar – kleines Icon oben rechts am Charbild.
   Nur auf Handy/Tablet: am Desktop zieht man den Söldner ins Inventar. */
.merc-out {
    position: absolute; top: 3px; right: 3px; z-index: 3;
    width: 20px; height: 20px; padding: 0; border: 0; border-radius: 50%;
    cursor: pointer; font: 700 13px/1 system-ui, sans-serif;
    color: #fff; background: rgba(58, 40, 20, .85); box-shadow: 0 0 0 1px var(--wood);
}
.merc-out:active { background: var(--green); }

/* Kompakte Variante von .btn – die Zeile ist nur ~70px hoch. */


/* Seltenheits-Textfarben */
.r1 { color: var(--r1); } .r2 { color: var(--r2); } .r3 { color: var(--r3); }
.r4 { color: var(--r4); } .r5 { color: var(--r5); }

/* --- Item-Kachel: Seltenheits-Slot per CSS-Verlauf (kein Bild) ------- */
.item-tile {
    position: relative; display: block;
    width: 100%; aspect-ratio: 1 / 1;
    cursor: grab; overflow: hidden;
    border-radius: 6px;
    border: 2px solid var(--holz-dunkel);                 /* Holzrahmen */
    /* Seltenheit = schräger Verlauf aus der OBEN-LINKS-Ecke, dunkle Grundfläche
       sonst. Linear statt radial (2026-08-11, Betreiber-Wunsch: keine "Kugel",
       sondern eine Schräge) – bei "to bottom right" liegt die Gradient-Linie
       exakt auf der Eck-zu-Eck-Diagonale, ein Stop bei 50% landet also genau in
       der Kachel-Mitte. NIE über 50% hinaus (sonst reicht der Glow über die
       Mitte hinaus in die gegenüberliegende Ecke). */
    background:
        linear-gradient(to bottom right, var(--rar, #6b5a3e) 0%, transparent 50%),
        #241009;
    box-shadow: inset 0 0 6px rgba(0, 0, 0, .55);
}
.item-tile.r1 { --rar: var(--rar1-glow); }
.item-tile.r2 { --rar: var(--rar2-glow); }
.item-tile.r3 { --rar: var(--rar3-glow); }
.item-tile.r4 { --rar: var(--rar4-glow); }
.item-tile.r5 { --rar: var(--rar5-glow); }
/* Set-Bonus = zweiter Verlauf aus der UNTEN-RECHTS-Ecke (grün oder orange),
   spiegelbildlich zur Seltenheit – trifft sich mit ihr exakt in der Mitte. */
.item-tile.set-green {
    background:
        linear-gradient(to top left, var(--set-green) 0%, transparent 50%),
        linear-gradient(to bottom right, var(--rar, #6b5a3e) 0%, transparent 50%),
        #241009;
}
.item-tile.set-orange {
    background:
        linear-gradient(to top left, var(--set-orange) 0%, transparent 50%),
        linear-gradient(to bottom right, var(--rar, #6b5a3e) 0%, transparent 50%),
        #241009;
}
.item-tile .art { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; padding: 5px; pointer-events: none; }
/* Ringe (type 6) wirken bei gleicher Füllung optisch schwerer als andere Slots,
   weil ein Ring seinen Rahmen als runde/kompakte Form viel dichter ausfüllt als
   z. B. eine Hose (die zwischen den Beinen/an der Taille viel Leerraum hat) –
   bei gleicher Kantenlänge sieht er trotzdem "massiver" aus. Deshalb hier eine
   Anzeige-Regel statt die Dateien selbst zu verkleinern: jedes künftig
   hochgeladene Ring-Bild bekommt das automatisch, ohne dass man beim Zuschneiden
   an einen Sonderfall denken muss. */
.item-tile[data-type="6"] .art { transform: scale(.7); }
.item-tile .stars {
    position: absolute; bottom: 1px; pointer-events: none;
    white-space: nowrap;   /* nie umbrechen – sonst stehen 3 Sterne in zwei Reihen */
    font-size: 11px; line-height: 1; letter-spacing: -1px;
    color: var(--gold-hell); text-shadow: 0 1px 1px rgba(0, 0, 0, .9);
}
.item-tile .stars.l { left: 3px; } .item-tile .stars.r { right: 3px; }

/* --- MARKER SKALIEREN MIT DEM SLOT ------------------------------------------
   Schloss, Upgrade-Pfeil und Sterne standen in festen Pixeln. Solange die Slots
   70–110px breit waren, ging das auf; seit das Raster auf schmalen Schirmen fünf
   Spalten hat (62px je Slot), überlappten sich links und rechts drei Sterne.

   `cqw` misst in Prozent der TILE-Breite, nicht des Fensters – deshalb wird jeder
   Slot einzeln richtig bedient, ob im Regal, in der Tasche oder auf der Puppe.
   Die Werte sind auf die native Slotgröße geeicht (--slot: 70px): 13/70 = 18,6cqw,
   12/70 = 17,1cqw, 11/70 = 15,7cqw.

   `min(…)` deckelt nach oben auf die bisherigen Maße: Auf breiten Slots (110px am
   Desktop) bleibt damit ALLES wie es war, geschrumpft wird nur darunter. Und wo
   ein Browser `cqw` nicht kennt, ist die ganze Deklaration ungültig und die
   Pixelangabe von oben bleibt stehen – ohne @supports, von selbst. */
.item-tile { container-type: inline-size; }
.up-arrow { width: min(13px, 18.6cqw); height: min(16px, 22.9cqw); }
.item-lock, .item-nurdia, .item-teuer { font-size: min(12px, 17.1cqw); }
.item-tile .stars { font-size: min(11px, 15.7cqw); letter-spacing: max(-1px, -1.43cqw); }
.item-tile .stars.l { left: min(3px, 4.3cqw); }
.item-tile .stars.r { right: min(3px, 4.3cqw); }
.item-tile.sel { outline: 3px solid var(--green); outline-offset: 1px; }
.item-tile:active { cursor: grabbing; }

/* --- Inventar: Paperdoll + Taschen ---------------------------------- */
.inv-stats { display: grid; grid-template-columns: repeat(4, 1fr); gap: .5rem; margin-bottom: 1rem; text-align: center; }
.inv-stats .box { padding: .5rem; border-radius: 8px; background: rgba(255, 255, 255, .35); box-shadow: inset 0 0 0 1px rgba(120, 90, 50, .4); }
.inv-stats .k { font-size: var(--fs-2xs); color: var(--ink-soft); }
.inv-stats .v { font-weight: 700; }

.equip-grid { display: grid; grid-template-columns: repeat(3, minmax(0, var(--slot))); justify-content: center; gap: .5rem; }
.slot {
    aspect-ratio: 1 / 1;
    display: flex; align-items: center; justify-content: center;
    border-radius: 8px;
    background: rgba(90, 60, 25, .12);
    box-shadow: inset 0 0 0 1px rgba(120, 90, 50, .5);
}
/* Hervorgehobener Platz, solange man ein passendes Item anschaut (items.js).
   Beim Ring pulsieren alle vier – man sieht sofort, dass es mehrere Plätze gibt.
   BEWUSST nur Bewegung, keine Farbe/Kontur: Ein belegter Platz ist von der
   Item-Kachel vollständig verdeckt, Hintergrund oder inset-Schatten wären dort
   unsichtbar – das Pulsieren sieht man dagegen immer. */
.equip-slot.slot-hint {
    position: relative; z-index: 2;   /* beim Wachsen über die Nachbarn legen */
}
.equip-slot.slot-hint > * { animation: slotHintPuls .9s ease-in-out infinite alternate; }
@keyframes slotHintPuls { from { transform: scale(1); } to { transform: scale(1.09); } }

/* Die Beschriftung eines LEEREN Platzes steht schräg (.sil, rotate(-45deg)).
   `transform` ist eine einzige Eigenschaft – ein reines scale() in den Keyframes
   würde die Drehung mit überschreiben und das Wort geradrücken. Deshalb eine
   eigene Kurve, die die Schräge mitführt. Gleiche Dauer und Amplitude wie oben,
   damit leerer und belegter Platz identisch pulsieren. */
.equip-slot.slot-hint > .sil { animation-name: slotHintPulsSchraeg; }
@keyframes slotHintPulsSchraeg {
    from { transform: rotate(-45deg) scale(1); }
    to   { transform: rotate(-45deg) scale(1.09); }
}

@media (prefers-reduced-motion: reduce) {
    .equip-slot.slot-hint > * { animation: none; transform: scale(1.06); }
    .equip-slot.slot-hint > .sil { transform: rotate(-45deg) scale(1.06); }
}

.slot .sil {
    font-size: clamp(.5rem, 1.9vw, .82rem);
    line-height: 1; color: var(--ink-soft);
    white-space: nowrap;
    transform: rotate(-45deg);   /* schräg im 45°-Winkel */
    pointer-events: none;
}

/* Inventar UND Händler: feste Spaltenzahl, die die freie Breite füllt (Items so
   groß wie möglich, gedeckelt bei 110px – dann zentriert). Gleicher Gap überall
   (.5rem), dadurch sind Taschen- und Händler-Slots gleich groß.
   NICHT auf auto-fill umstellen: 2026-08-12 kurz versucht, damit wurde die
   Spaltenzahl vom verfügbaren Platz abhängig – bei der echten Fensterbreite kamen
   dabei WENIGER Spalten raus (mehr Zeilen, weniger auf einen Blick sichtbar),
   genau das Gegenteil vom Ziel. Spaltenzahl bleibt fest, Slots werden NICHT
   künstlich vergrößert. */
/* VIER Spalten nur noch auf den wirklich schmalen Geräten (unter 360px, also
   iPhone SE und Ähnliches). Ab 360px sind es FÜNF – Betreiber-Wunsch 25.08.2026.

   Der Grund ist die Regalgröße: Vendor::SLOTS ist 15, und 15 Waren ergeben bei
   fünf Spalten drei VOLLE Reihen. Bei vier war die letzte immer angebrochen; im
   Stylesheet stand das seit dem 12.08. als bewusst hingenommen, weil damals die
   Slotgröße schwerer wog.

   Was es kostet, gemessen am echten Markup (343px Inhaltsbreite = 375px-Handy):
   Die Slots fallen von 79,8 auf 62,2px und damit unter ihre native Größe
   (--slot: 70px), werden also leicht herunterskaliert. Ab Tablet ändert sich gar
   nichts, dort greift ohnehin der 110px-Deckel – nur die Reihenzahl sinkt.

   Händler und Inventar teilen sich dieses Raster (`bag-grid vendor-grid`), damit
   Waren und Taschen-Items gleich groß sind. Eine Regel, beide Seiten. */
.bag-grid { display: grid; grid-template-columns: repeat(4, minmax(0, 110px)); justify-content: center; gap: .5rem; position: relative; }
@media (min-width: 360px) { .bag-grid { grid-template-columns: repeat(5, minmax(0, 110px)); } }
.bag-grid.hide { display: none; }
.bag-lock {
    position: absolute; inset: 0; z-index: 10; border-radius: 10px;
    display: flex; align-items: center; justify-content: center; text-align: center;
    background: rgba(30, 20, 8, .8); color: var(--box-ink); font-size: var(--fs-m);
}

/* Item-Info: eingebettetes Panel + schwebender Tooltip (dunkel) */
.info, .tooltip { color: #f2e8d4; }
.info {
    margin-top: .75rem; padding: .75rem; border-radius: 12px;
    background: rgba(24, 16, 6, .92); box-shadow: inset 0 0 0 1px rgba(255, 255, 255, .1);
}
.tooltip {
    /* über dem Belohnungsfenster (z-index 200) – dort werden Beute-Items gehovert */
    position: fixed; z-index: 300; width: auto; max-width: min(96vw, 520px); padding: .75rem;
    border-radius: 12px; border: 2px solid var(--wood);
    background: rgba(24, 16, 6, .95); box-shadow: 0 8px 24px rgba(0, 0, 0, .4);
    pointer-events: none;
    -webkit-backdrop-filter: blur(4px); backdrop-filter: blur(4px);
}
.info.hidden, .tooltip.hidden { display: none; }
.ii-name { font-weight: 700; margin: 0 0 .1rem; line-height: 1.25; }
/* im Vergleich: Name-Höhe angleichen, damit die Stat-Zeilen beider Items auf einer Linie liegen */
.ii-compare .ii-name { min-height: 3.75em; }
.ii-sub { font-size: var(--fs-s); color: #c9b891; margin: 0 0 .4rem; }
.ii-row { display: flex; justify-content: space-between; gap: 1.5rem; font-size: var(--fs-l); }
.ii-row .ii-k { color: #c9b891; }
/* Item-Tooltip: Vergleich (zwei Panels nebeneinander) */
/* Hinweis im Tooltip, wenn der Vergleich am fehlenden Ausrüstungstrank hängt.
   Goldton passend zum Kronen-Symbol; Kontrast gegen die Tooltip-Innenfläche gemessen:
   8,26:1 (die normale Unterzeile .ii-sub liegt bei 9,63:1). Abgesetzt durch eine
   Trennlinie, damit es nicht wie ein Item-Wert gelesen wird. */
.ii-premium {
    margin: .45rem 0 0; padding-top: .35rem;
    border-top: 1px solid rgba(224, 160, 32, .35);
    font-size: var(--fs-s); color: var(--gold);
}
.ii-compare { display: flex; flex-wrap: wrap; gap: 1rem; }
.ii-panel { width: 210px; }

/* Werte-Tabelle (Wert | grüne Range | orange Range) */
.ii-tbl { width: 100%; border-collapse: collapse; margin: .15rem 0 .1rem; font-size: var(--fs-m); }
.ii-tbl td { padding: .05rem .35rem; white-space: nowrap; vertical-align: top; }
.ii-tbl .ii-k { color: #c9b891; padding-left: 0; }
.ii-tbl .ii-v { font-weight: 700; text-align: right; }
/* Set-Ranges: hell = aktives Set, dunkel = potenziell (Original-Farben) */
.ii-tbl .ii-g  { color: var(--gut); }        /* potenziell grün – Tooltip ist dunkel */
.ii-tbl .ii-ga { color: var(--set-green); }  /* aktives grünes Set = die Set-Farbe */
.ii-tbl .ii-o  { color: var(--gold); padding-right: 0; }     /* potenziell orange */
.ii-tbl .ii-oa { color: var(--set-orange); padding-right: 0; }  /* aktives oranges Set = die Set-Farbe */
/* Wert-Farben: aktives Set (hell) bzw. Vergleich (besser/schlechter) */
.ii-tbl .ii-v-ga { color: var(--set-green); }
.ii-tbl .ii-v-oa { color: var(--set-orange); }
.ii-tbl .ii-v-up { color: var(--gut); }   /* besser als angelegt */
.ii-tbl .ii-v-dn { color: var(--schlecht); }   /* schlechter als angelegt */
.ii-wert { margin-top: .25rem; }

/* Set-Fortschritt im Tooltip: grün (Prefix) + orange (Suffix), Punkt + Text im
   echten Neon (wie die Slot-Ecke), damit man den Set-Fortschritt sofort sieht. */
.ii-set { margin-top: .45rem; padding-top: .4rem; border-top: 1px solid rgba(255, 255, 255, .16); font-size: var(--fs-s); }
.ii-set-line { display: flex; align-items: center; gap: .35rem; font-weight: 700; line-height: 1.35; }
.ii-set-line .ii-dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; box-shadow: 0 0 5px currentColor; }
.ii-set-g { color: var(--set-green); } .ii-set-g .ii-dot { background: var(--set-green); }
.ii-set-o { color: var(--set-orange); } .ii-set-o .ii-dot { background: var(--set-orange); }
.ii-up { color: var(--gut); } .ii-dn { color: var(--schlecht); }
.ii-note { margin-top: .3rem; font-size: var(--fs-2xs); color: #9c8a66; }

/* --- Buttons --------------------------------------------------------- */
.btn {
    display: inline-block; cursor: pointer; border: 0;
    padding: .55rem 1.1rem; border-radius: 10px;
    font: inherit; font-weight: 700; color: #fff; background: var(--green);
    box-shadow: 0 2px 0 var(--green-d);
}
/* HOVER ÜBER HELLIGKEIT, nicht über eine zweite Farbe. Vorher standen dafür vier
   Töne einzeln im Blatt (#57a340 für .btn und die Ampelstufe „leicht", dazu #9bb832,
   #ea9020, #d4432f für die übrigen drei Stufen) – jede eine Farbe, die niemand mehr
   nachzog, wenn sich ihre Grundfarbe änderte.

   brightness(1.12) trifft alle vier auf 1 bis 10 von 255 genau, also unsichtbar, und
   gilt automatisch auch für jede künftige Knopfvariante. Dasselbe Mittel benutzt
   .gast-banner:hover schon länger. */
.btn:hover { filter: brightness(1.12); }
.btn.full { display: block; width: 100%; text-align: center; }
.btn.ghost { background: var(--holz-dunkel); box-shadow: 0 2px 0 #3a2814; }
/* ROTER KNOPF für endgültige Handlungen (Konto löschen). Nutzt --schlecht-flaeche,
   den vorhandenen Ton für negative FLÄCHEN – gemessen 5,33:1 für die weiße Schrift
   darauf und 3,72:1 der Fläche gegen das Pergament, also klar erkennbar. Bewusst
   KEIN neuer Farbwert: Rot gibt es im Blatt schon dreimal, ein vierter Ton hätte nur
   eine weitere Variante desselben Gedankens geschaffen. */
.btn.btn-gefahr { background: var(--schlecht-flaeche); box-shadow: 0 2px 0 #7c2a1a; }

/* ANKER DER EINSTELLUNGEN. Nach dem Speichern leitet die Seite auf #newsletter,
   #email oder #loeschen um – ohne diesen Abstand landete die Karte unter der festen
   Kopfleiste. Derselbe Wert wie beim #self-Sprung der Rangliste, gleiche Ursache. */
.card[id] { scroll-margin-top: 4.5rem; }
/* Ein gesperrter Knopf darf NIE grün leuchten – sonst sieht er einladend aus und
   der Klick tut nichts. Steht bewusst hier an der Grundregel und nicht je
   Sonderfall: Vorher hatten nur Dungeon und Quest einen Deaktiviert-Stil, jeder
   andere gesperrte .btn blieb grün (aufgefallen am „Angreifen" in der Arena).
   Schlägt auch Ampel- und ghost-Farben, weil es später steht. */
.btn:disabled, .btn[disabled] {
    background: #6f6455; box-shadow: none; color: #cfc6b6;
    cursor: not-allowed; opacity: .75;
}
/* filter:none, sonst hellte die Regel oben auch den gesperrten Knopf auf – und
   „reagiert auf die Maus" ist genau das Gegenteil dessen, was er sagen soll. */
.btn:disabled:hover, .btn[disabled]:hover { background: #6f6455; filter: none; }

/* REIHENFOLGE IST HIER ENTSCHEIDEND: Diese beiden Zusätze müssen NACH .btn stehen.
   Bei gleicher Gewichtung gewinnt die spätere Regel – standen sie davor, überschrieb
   .btn ihre Polsterung und Schriftgröße wieder, und der Händlerknopf wurde plötzlich
   so groß wie ein normaler. */

/* ZUSATZ zu .btn, kein eigener Knopf: Im Markup steht `class="btn btn-vendor"`.
   Hier stehen nur noch die Unterschiede – Rasterplatz, kleinere Schrift, einzeilig.
   Farbe, Rundung, Schatten, Hover und der Deaktiviert-Stil kommen aus .btn.

   Vorher wiederholte diese Regel den halben .btn und brachte eigene ghost- und
   disabled-Varianten mit. Beim Ändern der Knopffarbe hätte man zwei Stellen
   pflegen müssen – und die zweite übersehen. */
.btn-vendor {
    grid-column: 3 / span 2; align-self: center;
    font-size: var(--fs-s); line-height: 1.15; padding: .4rem .5rem;
    white-space: nowrap;   /* einzeilig – der Knopf hat zwei Spalten Platz */
}


/* --- Login ----------------------------------------------------------- */
.login { max-width: 380px; margin: 3rem auto; }
.login h1 { text-align: center; margin: 0; font-size: var(--fs-3xl); }
.login .lead { text-align: center; color: var(--ink-soft); margin: .25rem 0 1.5rem; }
.field { display: block; margin-bottom: 1rem; }
.field span { display: block; font-size: var(--fs-l); font-weight: 600; margin-bottom: .25rem; }
.field input {
    width: 100%; padding: .7rem .9rem; font: inherit;
    border: 1px solid var(--wood); border-radius: 10px; background: rgba(255, 255, 255, .7);
}
/* „Angemeldet bleiben" – Häkchen neben dem Text, nicht darüber wie bei .field. */
.field-check { display: flex; align-items: center; gap: .5rem; margin-bottom: 1rem; cursor: pointer; }
.field-check input { width: 1.05rem; height: 1.05rem; accent-color: var(--green); flex: 0 0 auto; }
.field-check span { font-size: var(--fs-l); font-weight: 600; }

/* --- Rechtliches: Fußzeile + Textseiten --- */
.legal-footer { text-align: center; padding: 1rem .5rem calc(1rem + env(safe-area-inset-bottom));
    font-size: var(--fs-s); color: #e8ddc7; text-shadow: 0 1px 2px rgba(0, 0, 0, .6); }
.legal-footer a { color: #fff; text-decoration: none; }
.legal-footer a:hover { text-decoration: underline; }
.legal-footer span { margin: 0 .4rem; opacity: .6; }
.in-game .legal-footer { padding-bottom: calc(1rem + 58px + env(safe-area-inset-bottom)); }

.legal { line-height: 1.55; }
.legal p { margin: .3rem 0 .9rem; }
.legal a { color: inherit; }
.legal .adm-h4 { margin: 1.3rem 0 .2rem; }
.legal-list { margin: .3rem 0 .9rem; padding-left: 1.2rem; }
.legal-list li { margin-bottom: .45rem; }
.legal code { font-size: .9em; padding: .05rem .25rem; border-radius: 4px; background: rgba(0, 0, 0, .18); }
.legal-back { margin-top: 1.5rem; }

/* Hinweisband für Gastkonten (ganz oben, über der Topbar-Navigation) */
.gast-banner {
    display: block; margin: 0 0 .6rem; padding: .5rem .8rem; border-radius: 10px;
    background: linear-gradient(#ffe08a, #f4b23a); color: #4a2b00;
    font-size: var(--fs-m); font-weight: 700; text-decoration: none; text-align: center;
    box-shadow: 0 2px 0 #c98d1e;
}
.gast-banner:hover { filter: brightness(1.06); }
/* Als Formular trägt das Banner einen Knopf (E-Mail erneut senden). Keine eigene
   Klasse dafür – nur der Abstand zum Text, alles andere kommt von .btn. */
.gast-banner .btn { margin-left: .5rem; vertical-align: middle; }
.konto-form { max-width: 22rem; }

.err { background: rgba(180, 40, 20, .12); color: #922; padding: .5rem .75rem; border-radius: 8px; font-size: var(--fs-l); margin-bottom: 1rem; }

/* =====================================================================
   Handy + Tablet (<1200px), eingeloggt: randloses "Papier-App"-Layout.
   Pergament füllt vollflächig zwischen oberer Leiste und unterem Menü,
   kein Hintergrund sichtbar.
   ===================================================================== */
@media (max-width: 1279px) {
    .in-game .wrap { display: flex; flex-direction: column; min-height: 100vh; min-height: 100dvh; padding: 0; }
    .in-game .topbar { max-width: none; margin: 0; border-radius: 0; padding-right: 3.4rem; }   /* bündig oben, Platz rechts fürs Menü */

    .in-game .cols { flex: 1 1 auto; min-height: 0; display: flex; }
    .in-game .center-col { flex: 1; min-width: 0; display: flex; }
    .in-game .center-win { flex: 1; max-width: none; display: flex; }
    /* :not(.reward-win) überall hier: das Belohnungsfenster ist zwar auch ein
       .panel (es trägt denselben Rahmen), aber ein frei schwebendes Overlay –
       es darf sich NICHT wie die seitenfüllenden Fenster verhalten und behält
       seinen Rahmen auch auf dem Handy. */
    .in-game .panel:not(.reward-win) {
        flex: 1; margin: 0; max-width: none;
        /* Ohne sichtbaren Rahmen (siehe unten) gibt es keinen Stein, von dem der
           Inhalt Abstand halten müsste – das rahmenbezogene Padding wird hier
           auf normale Seitenränder zurückgesetzt. Unten zusätzlich Platz für das
           fixe Untermenü. */
        padding: 1rem 1rem calc(1.4rem + 58px + env(safe-area-inset-bottom));
    }
    /* randloses Pergament auf dem Handy, kein Dschungelrahmen – die Overlay-Teile
       sind reine Deko-Kinder, einfach ausblenden statt Rahmenwerte zurückzusetzen. */
    .in-game .panel:not(.reward-win) > .frame-corner,
    .in-game .panel:not(.reward-win) > .frame-edge { display: none; }
    /* Füllung wieder auf den ganzen Kasten – ohne Rahmen gibt es keine Balken-
       Innenkante, an der sie enden müsste. */
    .in-game .panel:not(.reward-win) > .frame-fill { top: 0; right: 0; bottom: 0; left: 0; }
}

/* =====================================================================
   Ab Desktop (>=1280px): Dschungel-Rahmen mit Seitenleisten.
   Erst ab 1280px ist genug Platz für 2x Seitenleiste (224px) + Mittelbox
   inkl. 12-Slot-Paperdoll UND flankierende Fläschchen; darunter greift das
   kompakte Ein-Spalten-Layout (Tablet + Handy), das sauber mitskaliert.
   ===================================================================== */
@media (min-width: 1280px) {
    /* KOPFZEILE DICHT AN DEN RAHMEN. Erst hier ist der Dschungelrahmen überhaupt
       sichtbar; darunter blendet ihn das kompakte Layout aus und die 1rem
       Innenpolsterung des Fensters ist der richtige Abstand.

       Die Balken-INNENKANTE liegt --fc * (--i-t - --o-t) unter der Boxkante –
       dieselbe Rechnung wie bei .frame-fill, gemessene 25px. Ausgerechnet statt
       eingetippt: Kommen neue Rahmenbilder, wandert die Zeile von selbst mit.
       --kopf-luft darunter, minus der Polsterung, ergibt den negativen Rand. */
    .kopf { margin-top: calc(var(--kopf-luft) + var(--fc) * (var(--i-t) - var(--o-t)) - var(--pad-oben)); }

    /* Oben nur so viel Reserve, wie die Icon-Reihe darüber braucht (top 24px +
       54px Icon = 78px, plus ein wenig Luft). Die früheren 208px stammten noch
       vom Affen über dem Fenster – der ist raus, die Fläche stand seither leer.
       Wer wieder mehr Himmel sehen will: hier und bei .iconmenu top im
       Gleichschritt erhöhen, die 10px Abstand dazwischen bleiben dann erhalten. */
    .wrap, .in-game .wrap { padding: 88px 16px 32px; }
    .topbar, .bottomnav, .iconmenu-toggle { display: none; }

    /* Söldner-Rausnehmen geht hier per Drag & Drop ins Inventar – Icon überflüssig. */
    .merc-out { display: none; }

    /* Statuswerte stehen hier links im Rahmen und schalten beim Söldner-Wechsel
       mit (inventar.js: syncSideStats) – die Zeile unter der Puppe wäre doppelt.
       Das aufklappbare Feld dazu ebenso: Es beantwortet dieselbe Frage. */
    .stat-strip, .stat-voll { display: none; }

    /* Desktop: Die Symbol-Leiste ist die ERSTE BOX der Seitenleiste – ganz normal im
       Fluss, kein Overlay. Vorher lag sie absolut über dem Fenster und die Seitenleiste
       brauchte einen von Hand passend gerechneten Abstand darunter: zwei Zahlen, die
       zueinander passen mussten und beim nächsten Umbau auseinandergelaufen wären.
       Jetzt regelt es der Abstand zwischen den Boxen, wie bei jeder anderen auch. */
    .iconmenu {
        position: static; display: block; z-index: auto;
        max-width: none; margin: 0; padding: 0;
        background: none; border: 0; box-shadow: none;
        pointer-events: auto;
    }
    .iconmenu > .panel > .content {
        display: flex; flex-direction: row; justify-content: center; align-items: center;
        gap: .4rem; flex-wrap: nowrap;
    }
    /* 44px statt 54: Fünf Symbole zu 54px brauchen 302px, in der 340px breiten
       Seitenleiste bleiben nach Rahmenbalken und Innenabstand aber nur rund 280 –
       sie brachen auf zwei Zeilen um und die Box wurde doppelt so hoch. */
    .iconmenu-item .ico { width: 44px; height: 44px; }
    /* Auf dem Desktop stehen NUR die Symbole (die Beschriftung ist ausgeblendet) –
       ein Knopfrahmen um ein freigestelltes Bild sähe nach Rahmen um Rahmen aus.
       Die Knopf-Optik gilt deshalb nur im Aufklappmenü des Handys. */
    .iconmenu-item { padding: 0; border: 0; background: none; box-shadow: none; text-shadow: none; }
    .iconmenu-item:hover { background: none; border: 0; }
    .iconmenu-item { padding: 0; pointer-events: auto; }
    /* Abmelden ans andere Ende – der auto-Rand nimmt den ganzen freien Platz auf.
       Trennt es zugleich von den Werkzeugen, sodass man es nicht versehentlich
       trifft (vorher stand hier eine feste 300px-Lücke, Rest vom entfernten Affen). */
    /* Der Zwischenraum entsteht durch `margin-left: auto` am ERSTEN Punkt der rechten
       Gruppe. Ist der Adminknopf da, schiebt er – und das Abmelden rückt direkt neben
       ihn. Fehlt er (alle Nichtadmins), übernimmt das Abmelden das Schieben wieder.
       Deshalb beide Regeln, plus die Rücknahme für den Fall, dass beide da sind. */
    /* GRUPPENTRENNUNG statt „ganz nach rechts": Werkzeuge (Premium, Rangliste,
       Einstellungen) | Verwaltung und Abmelden. Ein fester Abstand, kein `auto` –
       seit die Reihe mittig sitzt, würde `auto` sie wieder auseinanderziehen.
       Den Abstand trägt der ERSTE Punkt der zweiten Gruppe; fehlt der Adminknopf
       (alle Nichtadmins), übernimmt ihn das Abmelden. */
    /* Kein Gruppenabstand mehr: In der schmalen Seitenleiste ist der Platz für fünf
       Symbole ohnehin knapp, und in einer eigenen Box braucht es die Trennung nicht –
       sie sind dort schon vom Menü darunter abgesetzt. */
    .iconmenu-item.ico-logout,
    .iconmenu-item.ico-admin,
    .iconmenu-item.ico-spiel { margin-left: 0; }
    .iconmenu-item .lbl { display: none; }                 /* nur Icons */
    .iconmenu-item .ico { width: 54px; height: 54px; transition: transform .15s ease; filter: drop-shadow(0 2px 4px rgba(0, 0, 0, .45)); }
    .iconmenu-item:not(.disabled):hover .ico { filter: drop-shadow(0 2px 4px rgba(0, 0, 0, .45)) brightness(1.12); transform: translateY(-2px); }
    .iconmenu-item.disabled .ico { filter: drop-shadow(0 2px 4px rgba(0, 0, 0, .45)) grayscale(.35); opacity: .6; }

    /* Als Variablen, weil die Icon-Reihe dieselben Maße braucht, um über der
       Content-Box zu sitzen. Zwei Literale, die auseinanderlaufen können, waren
       genau der Fehler, den wir hier nicht wollen. */
    .in-game, .cols { --seitenleiste: 340px; --spaltenluecke: 40px; }
    .cols { display: flex; align-items: flex-start; gap: var(--spaltenluecke); }
    /* 340px statt vorher 280px: bei 55px Rahmendicke auf JEDER Seite frisst der
       Rahmen allein schon 110px – bei 280px blieben dem Menü/der Charakter-Karte
       innen nur ~140px, sichtbar zu knapp. Mit 340px bleiben ~200px nutzbare
       Breite, die Buttons/Statuswerte skalieren automatisch mit (kein eigenes
       max-width auf .nav-btn/.stat).

       Ein Ausgleich für die Rahmen-Oberkanten ist NICHT mehr nötig: seit dem
       9-Slice liegt die Balken-Aussenkante bei beiden Rahmen genau auf der
       Boxkante, beide Spalten starten also von selbst auf einer Linie. */
    .sidebar { display: block; width: var(--seitenleiste); flex: 0 0 var(--seitenleiste);
               position: relative; }
    .sidebar > .panel { display: block; }   /* Gegenstück zur Handy-Regel */
    .center-col { min-width: 0; flex: 1 1 auto; }
    /* Nutzt die volle Breite, die .cols nach Abzug der Seitenleiste übrig lässt
       (gedeckelt nur durch .wrap max-width: 1420px) – kein eigener 854px-Deckel
       mehr wie zu Bambus-Zeiten, als rechts noch eine zweite Seitenleiste stand. */
    .center-win { width: 100%; margin: 0 auto; }

    .split { grid-template-columns: 1fr 1fr; align-items: start; }

    /* Paperdoll am Desktop: Slots + Fläschchen nehmen die volle Char-Höhe an.
       Char-Bild ist 224px hoch (gemessen, kein zusätzlicher Rahmen).
       Slot-Höhe exakt: (224 - 2 Lücken) / 3 -> 3 Quadrate + 2 Lücken = volle Höhe.

       KEIN padding-top mehr: Die 25px waren Platz für die Namensplakette, die
       per position:absolute über dem Char-Bild hing. Das Schild ist weg, die
       Reservierung stand noch – dadurch waren es unter der Kopfzeile 35px statt
       der gewünschten 10. */
    /* KEIN eigenes --char-h mehr: Es gilt dieselbe Höhe wie im Zwischenbereich,
       weil das Charakterbild überall gleich hoch ist. Hier standen früher 234px
       mit der Begründung „Bild 224 + 2px Holzrahmen oben/unten" – die stimmt nicht:
       .char-frame misst nachgemessen ebenfalls 224px. Die Ausrüstungsspalten
       ragten dadurch oben UND unten je 5px über das Bild hinaus. */
    .paperdoll { align-items: center; }
    .flasks { height: var(--char-h); }
    /* flankierend: schrumpfen bei wenig Platz (min 42), gedeckelt bei viel (max 84) */
    .flasks { flex: 1 1 0; min-width: 42px; max-width: 84px; flex-direction: column; order: 0; gap: var(--eq-gap); }
    .paperdoll .flasks:first-of-type,
    .paperdoll .flasks:last-of-type { justify-content: center; padding: 0; }
    .flasks .flask {
        flex: none; display: flex; align-items: center; justify-content: center;
        height: calc((var(--char-h) - 2 * var(--eq-gap)) / 3);
    }
    .flasks .flask img { width: auto; max-height: 100%; max-width: 100%; }
    .char-center { margin: 0 .4rem; justify-content: center; }
    .char-big { width: 224px; }              /* volle Naturgröße */
    .merc-group { gap: 4px; }
    .merc img { width: 38px; height: 38px; }
    .mainchar img { width: 46px; height: 46px; }   /* mein Char größer */
}

/* Grün/Rot = stärker/schwächer (Item- und Söldner-Vergleich teilen sich die Töne) */
.mc-up { color: var(--gut); }
.mc-dn { color: var(--schlecht); }

/* --- Arena / Kampf --- */
.arena-unit { padding: .3rem .5rem; border-radius: 8px; background: rgba(255, 255, 255, .28);
    margin-bottom: .35rem; display: flex; flex-direction: column; gap: .1rem; }
.arena-unit.is-dead { opacity: .5; }
.arena-unit.is-dead b { text-decoration: line-through; }
.arena-teams { display: grid; grid-template-columns: 1fr; gap: 1rem; margin: .5rem 0; }
@media (min-width: 560px) { .arena-teams { grid-template-columns: 1fr 1fr; } }
.arena-log { max-height: 320px; overflow-y: auto; font-size: var(--fs-m);
    border-radius: 8px; background: rgba(0, 0, 0, .06); padding: .4rem .6rem; }
.arena-round { font-weight: 700; color: var(--ink-soft); margin: .5rem 0 .2rem; }
.arena-round:first-child { margin-top: 0; }
.arena-line { padding: .1rem 0; line-height: 1.35; }
.arena-line.is-att { color: var(--gut-pergament); }
.arena-line.is-def { color: var(--schlecht-hell); }
.arena-line em { color: var(--ink-soft); font-style: normal; }
.arena-reward { font-weight: 700; color: var(--gut-pergament); }
/* GEGNERLISTE: eine Zeile je Gegner mit Porträt vor seinem Lagerplatz – dieselbe
   Bildsprache wie die Dungeon-Kacheln, nur einzeilig. Die ganze Zeile ist der Link. */
.arena-opps { display: flex; flex-direction: column; gap: .5rem; margin-top: .6rem; }
.arena-opp { display: flex; align-items: center; gap: .8rem; flex-wrap: wrap;
    padding: .45rem .6rem; border-radius: 10px; background: rgba(255, 255, 255, .28);
    color: inherit; text-decoration: none; transition: background .15s;
    scroll-margin-top: 4.5rem; }   /* #self-Sprung in der Rangliste darf nicht unter dem sticky-Topbar landen */
.arena-opp:hover { background: rgba(255, 255, 255, .42); }
.arena-opp-img { width: 62px; height: 62px; flex: 0 0 auto; object-fit: cover;
    border-radius: 9px; box-shadow: 0 0 0 2px var(--wood);
    background: var(--charbg) no-repeat center / cover; }
.arena-opp-who { display: flex; flex-direction: column; min-width: 8rem; }
/* Arena: feste Breite für die Namensspalte. Ohne sie richtet sich ihre Breite
   nach Name und Level-Zeile (gemessen 128-166px Spannweite) – das Badge dahinter
   ("ehrlos" / "Rache") stand dadurch in jeder Zeile woanders. 11.5rem fasst die
   längste Zeile ("Level 38 · 1.042 Ansehen · ⚑ 73"); was darüber hinausgeht,
   bekommt Auslassungspunkte statt umzubrechen.
   Die Rangliste bleibt außen vor: die regelt ihre Spalten in .rl-list selbst. */
.arena-opps .arena-opp-who { flex: 0 0 11.5rem; min-width: 0; }

/* Rangliste: Porträt-Spalte fest breit (größtes Rangliste-Bild = Platz 1), damit
   Name/Level immer an derselben Stelle anfangen, egal wie groß das Porträt in
   dieser Zeile ist (Platz 1-3 sind größer, Rest bleibt bei 62px). Badge klebt
   immer rechts, unabhängig von Namenslänge – gleiches Prinzip wie .arena-opp-wc. */
.arena-opp-imgwrap { width: 92px; height: 92px; flex: 0 0 auto; display: flex; align-items: center; justify-content: center; }
.arena-opp .badge { margin-left: auto; flex: 0 0 auto; }
/* Handy: die Rangliste-Zeile hat mehr Inhalt (Rang-Symbol + größeres Podium-Bild)
   als die normale Gegnerliste – die geerbte .arena-opp-Regel „center + umbrechen"
   (siehe unten im Original-Media-Query) würde jedes umgebrochene Stück einzeln
   zentrieren und alles zerreißen. Rangliste bleibt deshalb IMMER einzeilig,
   das Podium-Bild schrumpft stattdessen mit, statt umzubrechen. */
@media (max-width: 560px) {
    .rl-list .arena-opp, .arena-opps .arena-opp { flex-wrap: nowrap; justify-content: flex-start; gap: .5rem; padding: .4rem .5rem; }
    .rl-list .arena-opp-rank { width: 1.3rem; font-size: var(--fs-m); }
    .rl-list .arena-opp-imgwrap { width: 56px; height: 56px; }
    .rl-list .arena-opp.is-rank1 .arena-opp-img { width: 56px; height: 56px; }
    .rl-list .arena-opp.is-rank2 .arena-opp-img { width: 50px; height: 50px; }
    .rl-list .arena-opp.is-rank3 .arena-opp-img { width: 46px; height: 46px; }
    .rl-list .arena-opp-img, .arena-opps .arena-opp-img { width: 42px; height: 42px; }
    .rl-list .arena-opp-who, .arena-opps .arena-opp-who { min-width: 0; flex: 1 1 auto; overflow: hidden; }
    .rl-list .arena-opp-who b, .arena-opps .arena-opp-who b { display: block; font-size: var(--fs-m); }
    .rl-list .badge { font-size: var(--fs-xs); padding: .1rem .4rem; }
}
/* Vorwarnung: dieser Angriff wäre ehrlos. Zurückhaltend, aber nicht zu übersehen. */
.arena-opp.is-ehrlos { background: rgba(217, 83, 79, .16); }
.arena-opp.is-ehrlos:hover { background: rgba(217, 83, 79, .26); }
/* Vollton statt halbtransparent: Die Zeile hat selbst schon einen roten Schleier
   (.is-ehrlos) über dem dunkelbraunen Kartengrund – dunkelrote Schrift auf
   halbdurchsichtigem Rot ergab darauf nur 2:1 Kontrast und war kaum lesbar.
   Jetzt heller Text auf sattem Rot (5,4:1), gleicher Ton wie .badge.is-bad. */
.arena-opp-warn { font-size: var(--fs-s); font-weight: 800; color: #fff; cursor: help;
    padding: .1rem .4rem; border-radius: 6px; background: var(--schlecht-flaeche);
    text-shadow: 0 1px 1px rgba(0, 0, 0, .5); }
/* Grüne Variante: Die Marke zeigt seit dem 20.08.2026 den Ehre-Ertrag eines
   Sieges statt eines Urteils – positiv gehört sie nicht in Rot. Weiß auf --green
   erreicht 4,55:1 (siehe Kommentar am Token). */
.arena-opp-warn.is-gut { background: var(--green); }
.arena-opp-wc { margin-left: auto; font-weight: 800; padding: .15rem .5rem; border-radius: 7px; }
.arena-opp form { margin: 0; }
.arena-opp .btn { margin: 0; }
@media (max-width: 560px) {
    /* Handy: EINE Zeile je Gegner. Vorher rutschten Chance und Knopf in eine zweite
       Zeile, was jede Zeile 124px hoch machte – bei fünfzehn Gegnern eine Strecke,
       die niemand durchscrollt. Gemessen bei 375px: gebraucht wurden 504px, verfügbar
       waren 288.

       Das Rezept steht direkt darüber schon für die Rangliste (.rl-list): kleineres
       Bild, kein Umbruch, Name als schrumpfende Spalte. Es gilt jetzt für beide
       Listen – deshalb ist der .rl-list-Block oben mit erweitert.

       Den Rest holen Beschriftungen, die auf dem Handy entbehrlich sind: „ehrlos"
       schrumpft auf sein ⚑, „Ansehen" auf ⚔ (.nur-breit/.nur-schmal im Markup). */
    .arena-opps .arena-opp-who { flex: 1 1 auto; }
    .arena-opp-wc { margin-left: auto; padding: .15rem .35rem; }
    .arena-opp .btn { margin-left: 0; padding-left: .55rem; padding-right: .55rem; }
}

/* Kampf-Vorschau */
.ap-actions { display: flex; align-items: center; justify-content: center; gap: .8rem; flex-wrap: wrap; margin-top: 1.1rem; }
.ap-actions form { margin: 0; }
.merc-row a.carrier { text-decoration: none; }
.ap-stats { display: flex; flex-direction: column; gap: .3rem; max-width: 22rem; margin: 0 auto; }
/* VERDIENTE MEDAILLEN als Reihe – in der Arena die des Gegners, auf der Übersicht
   die eigenen. Zentriert und umbrechend statt Raster: Es sind je nach Spieler null
   bis elf Stück, und ein Raster mit fester Spaltenzahl ließe bei dreien zwei Löcher
   stehen. Kein ap-Präfix mehr, seit es nicht nur die Arena-Vorschau benutzt. */
.medaillen-reihe { display: flex; flex-wrap: wrap; justify-content: center; gap: .45rem;
    padding: .2rem 0; }

/* GROSS BEIM ÜBERFAHREN – dasselbe Verfahren wie in der Grafik-Übersicht des
   Adminbereichs (.gal-cell) und bei den Avatarbildern in den Einstellungen
   (.ein-stufe). Kein eigenes Rezept, nur derselbe Griff an einer dritten Stelle.

   FAKTOR 2,6 IST GEMESSEN, nicht geschätzt: Die Untergründe liegen als 192px-Bilder
   vor und werden hier auf 72 gezeigt. 72 × 2,6 = 187 – knapp unter der echten
   Auflösung, also gestochen scharf. Bei den 4× der Galerie wären es 288 und das
   Bild würde weich.

   Das Anheben von .content ist Pflicht und keine Vorsicht: `.panel` hat
   `isolation: isolate`, darin liegen die Rahmenkanten über dem Inhalt. Ein z-index
   an der Plakette sortiert nur INNERHALB von .content – die vergrößerte Medaille
   verschwände sonst hinter dem Rahmen. Ausführlich steht das bei .gal-cell. */
/* FÜHLFLÄCHE UND VERGRÖSSERUNG SIND GETRENNT – die Zelle bleibt klein, nur die
   Plakette darin wächst. Beides in einem Element war der erste Versuch und fühlte
   sich träge an: Ist die Medaille erst groß, steht der Zeiger mitten in 187 Pixeln
   und muss die ganze Fläche verlassen, bevor die Nachbarin reagiert. Dasselbe
   Problem und dieselbe Lösung wie bei .gal-thumb (`pointer-events: none`). */
.medaille-zelle { position: relative; display: block; flex: 0 0 auto;
    width: var(--medaille-gr, 72px); height: var(--medaille-gr, 72px); }
.medaille-zelle .medaille { pointer-events: none;
    transition: transform .12s ease; transform-origin: center; }
.medaille-zelle:hover { z-index: 999; }
.medaille-zelle:hover .medaille { transform: scale(var(--medaille-zoom, 2.6)); }
/* Auf der Medaillenseite ist die Plakette 104px statt 72 – derselbe Zielwert
   (die echten 192px der Datei), also ein anderer Faktor: 192/104 = 1,85. */
.med-kopf .medaille-zelle:hover .medaille { --medaille-zoom: 1.85; }
/* KEIN Anheben von .content wie bei .gal-cell. Das hebt nicht die Medaille über den
   Rahmen, sondern den GANZEN Fensterinhalt – auf der Übersicht schoben sich damit
   auch Inventar und Sets darüber (gemeldet 25.08.2026). Im Adminbereich fällt es
   nicht auf, weil dort nichts an den Rahmen stößt.

   Etwas Feineres gibt es nicht: `.panel` hat `isolation: isolate`, ein z-index an
   der einzelnen Karte sortiert nur innerhalb von .content. Entweder alles nach vorn
   oder nichts. Also nichts – die Medaillenreihe steht mittig in ihrer Karte, die
   vergrößerte Plakette ragt 57px zur Seite und kommt dem Rahmen gar nicht nahe.
   Der z-index an der Zelle bleibt: Er sorgt dafür, dass die große Medaille über
   ihren NACHBARN liegt, und das geht innerhalb von .content problemlos. */
/* NUR EINMAL definiert. Es gab diese Regel zweimal mit verschiedenen Polstern –
   die spätere gewann, die frühere war wirkungslos. Aufgefallen erst beim Aufräumen. */
.btn-lg { font-size: var(--fs-l); padding: .6rem 1.5rem; }

/* Gewinnchance als Wappen-Marke oben rechts am Gegner-Charakterbild. Dunkler
   Metall-/Pergament-Ton statt Vollton-Sticker, die Ampelfarbe (dg-chance--*,
   gleiche vier Stufen wie bei Dungeon-Chancen) sitzt als Leuchtrand –
   passend zur düsteren Bildsprache statt fröhlichem Grün-Aufkleber. */
.wc-badge {
    position: absolute; top: 6px; right: 6px; z-index: 4;
    transform: rotate(5deg);
    width: 3.9rem; aspect-ratio: 1 / 1;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    border-radius: 8px;
    background: linear-gradient(160deg, #322619, #1c140c);
    box-shadow: 0 2px 6px rgba(0, 0, 0, .55), inset 0 0 0 1px rgba(255, 255, 255, .12);
    color: var(--box-ink); text-align: center; line-height: 1.05;
    text-shadow: 0 1px 2px rgba(0, 0, 0, .7); pointer-events: none;
}
.wc-badge.dg-chance--leicht     { box-shadow: 0 2px 6px rgba(0, 0, 0, .55), 0 0 0 2px var(--gut), 0 0 8px rgba(123, 208, 106, .4); }
.wc-badge.dg-chance--machbar    { box-shadow: 0 2px 6px rgba(0, 0, 0, .55), 0 0 0 2px var(--chance-machbar), 0 0 8px rgba(200, 217, 74, .4); }
.wc-badge.dg-chance--riskant    { box-shadow: 0 2px 6px rgba(0, 0, 0, .55), 0 0 0 2px var(--chance-riskant), 0 0 8px rgba(240, 160, 60, .4); }
.wc-badge.dg-chance--chancenlos { box-shadow: 0 2px 6px rgba(0, 0, 0, .55), 0 0 0 2px var(--chance-aus), 0 0 8px rgba(255, 107, 92, .4); }
.wc-badge .wc-num { display: block; font-weight: 900; font-size: var(--fs-xl); letter-spacing: .01em; }
.wc-badge .wc-lbl { display: block; font-weight: 700; font-size: var(--fs-2xs); letter-spacing: .14em; text-transform: uppercase; opacity: .75; margin-top: .05rem; }
.wc-badge .wc-ehre { display: flex; gap: .35rem; justify-content: center; margin-top: .22rem;
    padding-top: .18rem; border-top: 1px solid rgba(255, 255, 255, .15); font-size: var(--fs-xs); font-weight: 800; }
.wc-badge .wc-ehre .up { color: var(--gut); }
.wc-badge .wc-ehre .dn { color: var(--schlecht); }

/* ===== Söldner-Vergleich im Hover-Tooltip (items.js) ===== */
.tooltip.tip-merc { max-width: min(96vw, 820px); }   /* breiter als das Item-Tooltip */

/* --- Fläschchen-Tooltip: was der Trank tut, mit Restlaufzeit ---------------
   Schmaler als das Item-Tooltip (Fließtext statt Tabelle) und ohne Kaufknopf –
   er erklärt, er verkauft nicht (web/PREMIUM.md: keine aggressive Kaufaufforderung).
   Alle Kontraste gegen die Tooltip-Fläche gemessen, Werte je Regel notiert. */
.tooltip.tip-flask { max-width: min(92vw, 340px); }

/* --- Premium-Laden ---------------------------------------------------------
   Steht auf Pergament, also dunkle Schrift. Kein Drängeln: keine Countdown-Balken,
   keine „nur heute"-Banner – Preis, Wirkung, Knopf. */
.pl-laufzeit { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .6rem; }
/* .badge-tab sitzt sonst als Reiter am Kartenrand (position:absolute im .card-tabs).
   Hier sind es normale Knöpfe in der Zeile, deshalb zurück auf statisch. */
.pl-laufzeit .badge-tab { position: static; text-decoration: none; padding: .25rem .7rem; }

/* Karten in einer Reihe sind gleich hoch (Grid streckt sie). Damit Preis und Knopf
   trotzdem auf einer Linie stehen und die Karte mit der kürzesten Liste nicht
   „hochgezogen" wirkt: Karte als Spalte, die Fähigkeitenliste schluckt den Rest. */
.pl-trank { display: flex; flex-direction: column; }
.pl-grid { display: grid; grid-template-columns: 1fr; }
@media (min-width: 700px)  { .pl-grid { grid-template-columns: 1fr 1fr; } }
@media (min-width: 1280px) { .pl-grid { grid-template-columns: 1fr 1fr 1fr; } }

.pl-kopf { display: flex; align-items: center; gap: .6rem; margin-bottom: .5rem; }
.pl-bild { width: 54px; height: 54px; flex: 0 0 auto; }
.pl-bild.is-aus { opacity: .5; }            /* wie die Fläschchen an der Puppe */
.pl-name { font-weight: 800; font-size: var(--fs-xl); }
.pl-status { font-size: var(--fs-s); color: var(--ink-soft); }
/* HELLES Grün: Diese Karten stehen auf der dunklen Plakette, nicht auf Pergament.
   Das dunkle #1f5214 vom Statusstreifen kommt hier auf 1,38:1 – unlesbar. Gemessen
   gegen den Kartenhintergrund (65,47,33): var(--gut-plakette) erreicht 8,69:1. */
.pl-status.is-an { color: var(--gut-plakette); font-weight: 700; }
/* flex:1 schluckt die Resthöhe – dadurch stehen Preis und Knopf darunter bei allen
   Karten einer Reihe auf derselben Linie, egal wie lang die Liste ist. */
.pl-kann { flex: 1 1 auto; margin: 0 0 .6rem; }
.pl-kann li { color: var(--ink); }          /* .card definiert --ink hell um */
.pl-kann li::before { color: var(--gold); }     /* Gold wie sonst auf der Plakette */
/* Knopf mittig und mit dem Preis darin – „Kaufen · 350 💎" ist eine Handlung mit
   Preisschild, eine separate Preiszeile darüber wäre beziehungslos. */
.pl-form { margin-top: auto; text-align: center; }
.pl-form .btn { white-space: normal; }

/* Ersparnis als goldene Plakette statt als Kleingedrucktes – sie ist das
   Verkaufsargument. Dunkle Schrift auf Gold: 7,50:1 gemessen. */
.pl-sparzeile { display: flex; flex-wrap: wrap; align-items: center; justify-content: center;
                gap: .6rem; margin: .2rem 0 .7rem; }
/* --tief statt --ink: Diese Plakette liegt auf GOLD, aber innerhalb einer .card –
   und die definiert --ink auf ihren hellen Wert um. Ergebnis war Creme auf Gold mit
   1,70:1. --tief definiert keine Karte um und kommt auf 8,22:1. Gilt genauso für
   .dp-bonus. Dieselbe Falle wie bei den Pergament-Farben. */
.pl-spar {
    padding: .25rem .7rem; border-radius: 999px;
    background: var(--gold); color: var(--tief);
    font-size: var(--fs-l); font-weight: 800;
    box-shadow: 0 1px 4px rgba(0, 0, 0, .35);
}
.pl-alt { font-size: var(--fs-s); color: #c9b891; text-decoration: line-through; }

/* Über dem Komplettpaket-Knopf schweben = alle sechs Tränke hervorheben. Möglich,
   weil .pl-komplett und .pl-grid GESCHWISTER sind; :has() grenzt es auf den Knopf
   ein, statt die ganze Karte als Auslöser zu nehmen. Die Fläschchen gehen dabei auf
   volle Deckkraft – man soll sehen, was man bekommt. */
.pl-komplett:has(.btn:hover) ~ .pl-grid .pl-trank {
    box-shadow: inset 0 0 0 2px var(--gold), inset 0 0 0 4px var(--dunkel), 0 0 12px rgba(224, 160, 32, .35);
}
.pl-komplett:has(.btn:hover) ~ .pl-grid .pl-bild { opacity: 1; }
.pl-trank, .pl-bild { transition: box-shadow .15s ease, opacity .15s ease; }

.pl-komplett { border-left: 4px solid var(--gold); }

/* --- Diamantpakete ---------------------------------------------------------
   Der Bonus ist die einzige Zahl, die hier wirklich vergleicht – deshalb als
   goldene Ecke, nicht als Fußnote. Dunkle Schrift auf Gold: 7,50:1 (gemessen). */
/* Feste Spaltenzahl statt auto-fit: Bei sechs Paketen packte auto-fit fünf in die
   erste Reihe und ließ eines allein darunter stehen. Gerade Reihen lassen sich
   vergleichen, eine Waise sieht nach Fehler aus. */
.dp-grid { display: grid; gap: .7rem; grid-template-columns: repeat(2, 1fr); margin-top: .7rem; }
@media (min-width: 560px)  { .dp-grid { grid-template-columns: repeat(3, 1fr); } }
@media (min-width: 1100px) { .dp-grid { grid-template-columns: repeat(6, 1fr); } }
.dp-karte {
    position: relative; padding: .8rem .6rem .7rem; text-align: center;
    border: 2px solid #000; border-radius: 8px;
    background: var(--box-bg) url('../img/bg_braune_box.jpg') no-repeat;
    box-shadow: inset 0 0 0 2px #655e4d, inset 0 0 0 4px var(--dunkel);
}
.dp-karte.hat-bonus { box-shadow: inset 0 0 0 2px var(--gold), inset 0 0 0 4px var(--dunkel); }
.dp-bonus {
    position: absolute; top: -9px; right: -6px;
    padding: .1rem .45rem; border-radius: 999px;
    background: var(--gold); color: var(--tief);
    font-size: var(--fs-s); font-weight: 800;
}
.dp-dias  { font-size: var(--fs-l); font-weight: 800; color: var(--box-ink); }
.dp-preis { margin-top: .15rem; font-size: var(--fs-m); color: var(--gut-plakette); font-weight: 700; }
.dp-hinweis { margin-top: .2rem; font-size: var(--fs-s); color: var(--box-ink-soft); }
/* Auswählbare Paketkarte. Der gewählte Zustand ist ein GOLDRING, keine Ausblassung
   der anderen – dieselbe Regel wie bei den Premium-Knöpfen. */
.dp-waehlbar { cursor: pointer; }
.dp-waehlbar:hover { box-shadow: inset 0 0 0 2px var(--gold), inset 0 0 0 4px var(--dunkel); }
.dp-karte.ist-gewaehlt {
    box-shadow: inset 0 0 0 2px var(--gold), inset 0 0 0 4px var(--dunkel), 0 0 10px rgba(224,160,32,.45);
}
/* Der Kaufblock steht mittig unter dem Paketraster: Er gehört keiner einzelnen Kachel,
   sondern der Auswahl darüber. Kein `display: flex` hier – das würde das `hidden` des
   Blocks aushebeln, solange kein Paket gewählt ist. */
.dp-kauf { margin-top: 1rem; padding-top: .8rem; border-top: 1px solid rgba(101,94,77,.5);
           max-width: 30rem; margin-inline: auto; }
.dp-gewaehlt { font-weight: 800; margin: 0 0 .5rem; text-align: center; }
/* Zustimmung: bewusst normal groß und nicht kleingedruckt – sie ist der Grund, warum
   das Widerrufsrecht erlischt, und muss gelesen werden können. */
.dp-zustimmung { display: flex; gap: .5rem; align-items: flex-start; margin-bottom: .8rem;
                 font-size: var(--fs-m); line-height: 1.45; }
.dp-zustimmung input { margin-top: .15rem; accent-color: var(--gold); flex: 0 0 auto; }
/* Der Zustimmungstext bleibt linksbündig, auch wenn drumherum zentriert wird –
   mehrzeiliger Fließtext mit flatterndem linken Rand liest sich schlecht, und
   ausgerechnet dieser Absatz muss gelesen werden. */
.dp-buttons { max-width: 320px; margin-inline: auto; }
.dp-meldung { margin: .6rem 0 0; font-size: var(--fs-m); min-height: 1.2em; text-align: center; }
.dp-meldung.ist-ok     { color: var(--gut-plakette); font-weight: 700; }
.dp-meldung.ist-fehler { color: var(--schlecht); font-weight: 700; }

/* --- Adminliste der Diamantpakete ------------------------------------------
   Eine Tabelle, weil man Preise nebeneinander vergleicht. „je €" und „Bonus"
   sind GERECHNET und deshalb keine Eingabefelder – sie sollen gar nicht erst
   den Eindruck erwecken, man könne sie setzen. */
.pk-tbl { width: 100%; border-collapse: collapse; margin: .6rem 0; font-size: var(--fs-m); }
.pk-tbl th { text-align: left; padding: .3rem .4rem; color: var(--box-ink-soft);
             font-size: var(--fs-s); font-weight: 700; border-bottom: 1px solid rgba(101, 94, 77, .5); }
.pk-tbl td { padding: .3rem .4rem; border-bottom: 1px solid rgba(101, 94, 77, .25); }
.pk-in { width: 100%; padding: .2rem .35rem; font: inherit; font-size: var(--fs-m);
         color: var(--box-ink); background: var(--dunkel); border: 1px solid #655e4d; border-radius: 4px; }
.pk-kurz  { width: 4.5rem; }
.pk-breit { min-width: 11rem; }
.pk-rech  { color: var(--box-ink-soft); white-space: nowrap; }
.pk-mitte { text-align: center; }
.pk-neu td { background: rgba(224, 160, 32, .07); }   /* Anlege-Zeile abgesetzt */

/* --- Serverzustand im Adminbereich ----------------------------------------
   Rot/grün nur am Rand, nicht als Flächenfarbe: Die Tabelle soll überflogen werden
   können, ohne dass zehn grüne Balken schreien. Auffallen muss nur, was rot ist. */
.sys-tbl { width: 100%; border-collapse: collapse; font-size: var(--fs-m); }
.sys-tbl th { width: 12rem; text-align: left; padding: .25rem .5rem; font-weight: 600;
              color: var(--box-ink-soft); border-left: 3px solid transparent; }
.sys-tbl td { padding: .25rem .5rem; }
.sys-tbl tr.ist-gut th     { border-left-color: var(--green); }
.sys-tbl tr.ist-schlecht th { border-left-color: var(--schlecht-flaeche); }
.sys-tbl tr.ist-schlecht td b { color: var(--schlecht); }
.sys-hinweis { color: var(--box-ink-soft); font-size: var(--fs-s); }

/* Muster-Widerrufsformular: Festbreitenschrift, damit die Ausfülllinien untereinander
   stehen. Scrollt bei schmalen Schirmen für sich, statt die Seite breit zu machen. */
.wr-muster {
    margin: .5rem 0; padding: .7rem .8rem; overflow-x: auto;
    background: rgba(0, 0, 0, .18); border-left: 3px solid #8a3b22; border-radius: 6px;
    font-family: ui-monospace, "Courier New", monospace; font-size: var(--fs-s);
    line-height: 1.5; white-space: pre;
}

/* --- Ablaufmeldung eines Tranks (steht auf Pergament, nicht im Tooltip) ------
   Rote Kante links als Signal „hier ist etwas weggefallen", ohne die ganze Karte
   einzufärben – der Text muss auf Pergament lesbar bleiben. Kein Kaufknopf. */
.trank-aus { border-left: 4px solid var(--schlecht-flaeche); }
.ta-block + .ta-block { margin-top: .9rem; padding-top: .9rem; border-top: 1px solid rgba(90, 60, 25, .3); }
.ta-kopf { display: flex; align-items: flex-start; gap: .6rem; }
.ta-bild { width: 46px; height: 46px; flex: 0 0 auto; filter: grayscale(1) opacity(.5); }
.ta-name { font-weight: 800; font-size: var(--fs-l); }
.ta-folge { font-size: var(--fs-m); margin-top: .1rem; }
.ta-hatte { margin: .5rem 0 .25rem; font-size: var(--fs-s); color: var(--ink-soft); }
/* Die Fähigkeitenliste teilt sich das Aussehen mit dem Tooltip (.ft-liste), braucht
   hier aber die dunkle Schrift des Pergaments statt der hellen. */
.trank-aus .ft-liste li { color: var(--ink); }
.trank-aus .ft-liste li::before { color: #8a3b22; }
.ft-name {
    font-weight: 800; font-size: var(--fs-xl); color: #f0e2c4;   /* 14,68:1 */
    margin: 0 0 .15rem;
}
.ft-rest { font-size: var(--fs-s); font-weight: 700; color: var(--gut-plakette); }        /* aktiv, 12,89:1 */
.ft-rest--bald { color: var(--gold-hell); }                                       /* < 24 h, 10,21:1 */
.ft-rest--aus  { color: #b09a78; font-weight: 400; }                     /* inaktiv, 6,94:1 */
.ft-kopf {
    margin: .5rem 0 .2rem; padding-top: .4rem;
    border-top: 1px solid rgba(224, 160, 32, .3);
    font-size: var(--fs-xs); color: #c9b891;                                    /* 9,63:1 */
}
/* Eigene Aufzählungspunkte: der Standard-Marker sitzt außerhalb und würde bei
   mehrzeiligen Punkten aus dem Tooltip ragen. */
.ft-liste { margin: 0; padding: 0; list-style: none; }
.ft-liste li {
    position: relative; padding-left: .85rem; margin: 0 0 .22rem;
    font-size: var(--fs-s); line-height: 1.35; color: #e0d3b8;                /* 12,71:1 */
}
.ft-liste li::before {
    content: '•'; position: absolute; left: .15rem; top: 0;
    color: var(--gold); font-weight: 700;
}
.ft-liste li:last-child { margin-bottom: 0; }
.mci-tbl { border-collapse: collapse; font-size: var(--fs-m); color: var(--box-ink); }
.mci-tbl th, .mci-tbl td { padding: .28rem .5rem; text-align: center; white-space: nowrap; }
.mci-tbl thead th { vertical-align: bottom; font-weight: 700; }
.mci-tbl thead img {
    width: 38px; height: 38px; object-fit: cover; border-radius: 7px;
    box-shadow: inset 0 0 0 2px #000, 0 0 0 2px #655e4d; display: block; margin: 0 auto .2rem;
}
.mci-nm { display: block; max-width: 6rem; overflow: hidden; text-overflow: ellipsis; }
.mci-tbl thead em { display: block; font-style: normal; font-size: var(--fs-xs); color: var(--box-ink-soft); }
.mci-tbl thead .mci-ref .mci-nm { color: var(--gut); }
.mci-tbl tbody th { text-align: left; color: var(--box-ink-soft); font-weight: 700; }
.mci-tbl tbody tr { border-top: 1px solid rgba(255, 255, 255, .10); }
.mci-tbl .mci-ref { background: rgba(77, 145, 57, .20); }
.mci-power td, .mci-power th { font-weight: 800; }
.mci-hint { margin: .5rem 0 0; font-size: var(--fs-s); color: var(--box-ink-soft); max-width: 26rem; white-space: normal; }
.mci-hint .mc-up, .mci-hint .mc-dn { font-weight: 700; }

/* ===== Massenverkauf beim Händler (.mv) =====
   Eine ankreuzbare Kachel je Seltenheit, die in den Taschen liegt, und EIN Knopf
   für alles Angehakte. Die Kachel ist dieselbe wie im Inventar – die Farbe sagt,
   was weggeht, ohne dass man den Namen lesen muss.
   Die echte Checkbox bleibt im DOM (Tastatur, Screenreader), sichtbar ist statt
   ihrer die Kachel: angehakt = heller Rahmen + Häkchen in der Ecke. */
.mv { margin-top: 1rem; padding-top: .8rem; border-top: 1px solid rgba(0, 0, 0, .25); }
.mv-kopf { font-weight: 800; font-size: var(--fs-m); margin-bottom: .5rem; }
.mv-wahl { display: flex; flex-wrap: wrap; gap: .4rem; }

.mv-opt {
    display: flex; flex-direction: column; align-items: center; gap: .15rem;
    padding: .3rem .35rem; border-radius: 8px; cursor: pointer;
}
.mv-opt:hover { background: rgba(255, 255, 255, .14); }
/* Checkbox nur optisch weg – nicht display:none, sonst ist sie nicht fokussierbar. */
/* .sch-cb ist dieselbe Optik, aber ein anderer Anker: inventar.js hängt seinen
   Schnellverkauf an .mv-cb/.mv-go, und die Schamanenhütte steckt seit dem Umbau im
   selben #inv-widget. Gleiche Klasse hieße dort: gleiches Verhalten – und das wäre
   Verkaufen statt Auflösen. */
.mv-cb, .sch-cb { position: absolute; opacity: 0; width: 0; height: 0; }

.mv .item-tile {
    position: relative; width: 34px; border-width: 1px; border-radius: 5px; cursor: pointer;
    box-shadow: 0 0 0 2px transparent;
    transition: box-shadow .12s, transform .12s;
}
/* Angehakter Zustand: Goldring auf der KACHEL, nicht am Label – deshalb reicht
   der Geschwister-Selektor und es braucht kein :has() (das erst ab Firefox 121
   sitzt; für ein Browsergame zu neu). Bedingung dafür: die Checkbox muss im
   Markup VOR der Kachel stehen. Bitte die Reihenfolge in haendler.php nicht
   umdrehen, sonst verschwindet die Markierung lautlos.
   Rein in CSS gelöst, damit die Rückmeldung auch dann sofort da ist, wenn das
   Skript noch lädt. */
.mv-cb:checked ~ .item-tile,
.sch-cb:checked ~ .item-tile { box-shadow: 0 0 0 2px var(--gold); transform: translateY(-1px); }
/* Söldner haben keine Seltenheit und damit keine Farbe aus r1..r5 – eigener Ton,
   der sich klar von den fünf Stufen absetzt, damit die Kachel nicht wie eine
   sechste Seltenheit gelesen wird. */
.mv-tile.mv-merc { background: linear-gradient(to top left, var(--wood) 0%, #2e2317 70%); }
.mv-cb:checked ~ .item-tile::after {
    content: '✓'; position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    font-size: var(--fs-l); font-weight: 900; color: #fff;
    text-shadow: 0 1px 2px #000, 0 0 4px #000;
}
.mv-cb:checked ~ .mv-name { color: var(--gold-hell); }
.mv-cb:focus-visible ~ .item-tile { outline: 2px solid var(--gold-hell); outline-offset: 2px; }

/* Menge sitzt auf der Kachel statt daneben – nebeneinander wäre je Stufe eine
   eigene Spalte nötig und die Reihe würde zu breit. */
.mv-n {
    position: absolute; right: -3px; bottom: -3px;
    min-width: 15px; padding: 0 2px; border-radius: 999px;
    background: #2e2317; color: var(--gold-hell);
    font-size: var(--fs-2xs); font-weight: 800; line-height: 15px; text-align: center;
    box-shadow: 0 0 0 1px var(--tief);
}
.mv-name { font-weight: 700; font-size: var(--fs-s); }

/* Voller Breite und deutlich abgesetzt: Ein Verkaufsknopf, den man aus Versehen
   streift, ist das Letzte, was man hier will. Gesperrt, solange nichts angehakt
   ist – inventar.js schreibt die laufende Summe hinein. */
.mv-go, .sch-go { margin: .5rem 0 0; width: 100%; font-size: var(--fs-m); padding: .35rem .6rem; }
.mv-go:disabled, .sch-go:disabled { opacity: .45; cursor: not-allowed; }

/* ===== Arena-Kampf-Animation (arena-fight.js) ===== */
.fight-arena {
    position: relative; display: flex; align-items: flex-start; gap: .4rem;
    padding: 1.4rem 0 .2rem;   /* Luft oben für aufsteigende Trefferzahlen */
}
/* KULISSE: der Schauplatz liegt NUR HINTER DEN PORTRÄTS, nicht hinter dem ganzen
   Fenster. Der Hauptcharakter ist freigestellt (PNG ohne Hintergrund) und steht
   dadurch in der Szene des Dungeons; Söldner und Gegner bringen ihr eigenes Bild
   mit und decken die Kulisse einfach ab. Ohne .has-kulisse bleibt alles wie vorher. */
/* Weltkämpfe (Dungeon/Quest): die freigestellten Porträts der EIGENEN Seite rund
   15 % kleiner – Spieler wie Söldner. Sie füllen ihren Rahmen sonst ganz aus, während
   die Gegner ihre Umgebung mit im Bild haben und darin kleiner wirken. Gegnerbilder
   (img/dungeon/, img/quest/) bleiben unverändert. */
.fight-arena.is-welt .ft-img[src*="/img/char/"],
.fight-arena.is-welt .ft-img[src*="/img/soeldner/"] { transform: scale(.82); transform-origin: bottom center; }

.fight-arena.has-kulisse .ft-stage,
.fight-arena.has-kulisse .rd {
    background-image: var(--kulisse); background-size: cover; background-position: center;
}
/* Die kleinen Roster-Bildchen unter dem Kämpfer sind genau wie das große Porträt
   freigestellt (kein eigener Hintergrund) – ohne Kulisse standen sie nur auf dem
   schwachen rgba(0,0,0,.15) und wirkten "nackt". --kulisse ist als Custom Property
   am .fighter gesetzt und vererbt sich nach unten, hier einfach mitnutzen statt
   die Kulisse zusätzlich in PHP durchzureichen. */
.fighter { flex: 1 1 0; min-width: 0; text-align: center; position: relative; }
.fight-mid { flex: 0 0 12px; align-self: stretch; }

/* Die Team-Leiste steht UNTER dem Porträt: oben verdeckte sie die Sicht auf den
   Kämpfer, der gerade dran ist, und schob ihn nach unten aus dem Blickfeld. */
/* IMMER EINZEILIG, GENAU SO BREIT WIE DAS PORTRÄT DARÜBER: Die Leiste bekommt die
   Bildbreite (dieselbe clamp-Formel wie .ft-img), und die Bildchen teilen sie unter
   sich auf – fünf Kämpfer ergeben fünf schmalere, einer einen großen. Feste Größen
   brachen auf dem Handy um, und zwei Zeilen zerreißen die Reihenfolge des Teams.
   `nowrap` ist die Zusicherung: hier wird nie umbrochen. */
.ft-roster { display: flex; flex-wrap: nowrap; justify-content: flex-start; gap: .3rem;
    width: clamp(80px, 38vw, 300px); max-width: 100%; margin: .5rem auto 0; }
/* FESTE TEILUNG IN FÜNF, egal wie viele Kämpfer dastehen: Ein voller Kader (Held +
   4 Söldner) füllt die Leiste genau aus, und ein einzelner Dungeon-Gegner bekommt
   exakt dasselbe Kästchen – linksbündig, nicht mittig. Würden sich die Bildchen die
   Breite je nach Anzahl teilen, wäre der eine Gegner rechts fünfmal so groß wie die
   Söldner links; nebeneinander sah das aus wie zwei verschiedene Anzeigen.
   Die 5 ist die maximale Teamgröße (Spieler + Inventory::MERCS). */
.rd { flex: 0 0 calc((100% - 4 * .3rem) / 5); min-width: 0; aspect-ratio: 1 / 1;
    border-radius: 6px; overflow: hidden; opacity: .85;
    box-shadow: 0 0 0 1px var(--wood); background: rgba(0, 0, 0, .15); transition: opacity .25s, box-shadow .25s; }
.rd img { width: 100%; height: 100%; object-fit: cover; display: block; }
.rd.cur { opacity: 1; box-shadow: 0 0 0 2px var(--green), 0 0 6px 1px rgba(77, 145, 57, .6); }
.rd.dead { opacity: .3; filter: grayscale(1); }

/* Die BÜHNE trägt Rahmen und Kulisse, nicht mehr das Bild selbst. Nur so kann das
   freigestellte Spielerporträt verkleinert werden, ohne dass Rahmen und Hintergrund
   mitschrumpfen. `width: fit-content` legt die Bühne genau auf die Bildgröße. */
.ft-stage { position: relative; width: fit-content; margin: 0 auto; border-radius: 12px;
    box-shadow: 0 0 0 2px var(--wood); background: rgba(0, 0, 0, .12); }
/* Porträtgröße: 38vw statt 21vw – zwei Porträts nebeneinander füllen damit die
   Handybreite fast aus, statt als Briefmarken in der Mitte ihrer halben Spalte zu
   sitzen. Am Desktop deckelt 300px (die Bilder liegen als 512er vor, werden also
   nie hochskaliert), dort greift die Breite ohnehin nicht mehr.
   `max-width: 100%` ist die Notbremse, falls die Karte doch einmal schmaler ist
   als 2 × 38vw. */
.ft-img { width: clamp(80px, 38vw, 300px); max-width: 100%; aspect-ratio: 1 / 1; object-fit: cover; display: block;
    margin: 0 auto; border-radius: 12px; }
.ft-float { position: absolute; inset: 0; pointer-events: none; z-index: 5; }
.fl { position: absolute; left: 50%; top: 28%; transform: translateX(-50%); font-weight: 800; white-space: nowrap;
    -webkit-text-stroke: 1px #000;                      /* dünne schwarze Kontur für Lesbarkeit */
    paint-order: stroke fill;                           /* Kontur hinter der Füllung -> Zahl bleibt klar */
    text-shadow: 0 0 3px rgba(0, 0, 0, .9), 0 1px 2px rgba(0, 0, 0, .85);   /* Fallback + Tiefe */
    animation: fightFloat 1.3s ease-out forwards; }
.fl--hit   { color: #fff;    font-size: var(--fs-2xl); }
.fl--crit  { color: var(--gold-hell); font-size: var(--fs-3xl); }
.fl--dodge { color: #e4d8bf; font-size: var(--fs-xl); }
.fl--ko    { color: var(--schlecht); font-size: 1.55rem; letter-spacing: .04em; }
@keyframes fightFloat {
    0%   { opacity: 0; transform: translate(-50%, 8px)  scale(.7); }
    15%  { opacity: 1; transform: translate(-50%, -2px) scale(1.12); }
    100% { opacity: 0; transform: translate(-50%, -44px) scale(1); }
}

/* NAMENSBAND NUR FÜR BOSSE – exakt das Band der Dungeon-Detailansicht (.dn-band),
   auf dem Bild statt darunter. Das Fußvolk behält die Zeile unter dem Porträt,
   dadurch sieht man dem Bild sofort an, ob der Boss dran ist.
   Der Wrapper schneidet den seitlichen Überstand des schrägen Bandes ab und
   umschließt bewusst NUR das Band, damit die aufsteigenden Trefferzahlen weiter
   über den Bildrand hinauslaufen dürfen. */
.ft-band { position: absolute; inset: 0; overflow: hidden; border-radius: 12px;
    pointer-events: none; display: none; }
.fighter.is-boss .ft-band { display: block; }
.fighter.is-boss .ft-name { display: none; }
.ft-name { font-weight: 800; margin-top: .55rem; font-size: var(--fs-xl); line-height: 1.2; word-break: break-word; }
.ft-hp { height: 12px; max-width: 180px; margin: .45rem auto 0; border-radius: 7px; overflow: hidden;
    background: rgba(0, 0, 0, .22); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .15); }
.ft-hp i { display: block; height: 100%; width: 100%; border-radius: 7px; background: var(--green); transition: width .32s ease, background .32s; }
.ft-hp i[data-lvl="mid"] { background: var(--gold); }
.ft-hp i[data-lvl="low"] { background: #d23b26; }
.ft-hp-txt { font-size: var(--fs-s); color: var(--ink-soft); margin-top: .2rem; }

/* Patrone fliegt zwischen den Porträts (absolut zur .fight-arena) */
.bullet { position: absolute; top: 40%; left: 22%; width: 14px; height: 14px; border-radius: 50%; z-index: 4;
    background: radial-gradient(circle at 35% 35%, #fff, #f6c445 45%, #b8341f 100%);
    box-shadow: 0 0 8px 2px rgba(250, 180, 60, .85); opacity: 0; pointer-events: none; }
.bullet.go-lr { animation: bulletLR .26s ease-in; }
.bullet.go-rl { animation: bulletRL .26s ease-in; }
@keyframes bulletLR { 0% { left: 24%; opacity: 0; } 12% { opacity: 1; } 100% { left: 72%; opacity: 0; } }
@keyframes bulletRL { 0% { left: 72%; opacity: 0; } 12% { opacity: 1; } 100% { left: 24%; opacity: 0; } }

/* Bewegungen: Angriff-Ausfall, Treffer-Zittern, Ausweichen, Tod, Eintritt */
.fighter.is-left.attacking  .ft-stage { animation: lungeR .26s ease; }
.fighter.is-right.attacking .ft-stage { animation: lungeL .26s ease; }
@keyframes lungeR { 50% { transform: translateX(14px); } }
@keyframes lungeL { 50% { transform: translateX(-14px); } }
/* Treffer-Blitz liegt auf der BÜHNE, nicht auf dem Bild: Seit das Spielerporträt
   verkleinert wird, säße die rote Umrandung sonst mitten in der Kachel statt außen
   am Rahmen. Beide Animationen laufen gleichzeitig (Rütteln + Aufblitzen). */
.fighter.hurt .ft-stage { animation: hurtShake .28s ease, hurtFlash .28s ease; }
@keyframes hurtShake { 0%,100% { transform: translateX(0); } 20% { transform: translateX(-6px); } 40% { transform: translateX(5px); } 60% { transform: translateX(-4px); } 80% { transform: translateX(3px); } }
@keyframes hurtFlash { 50% { box-shadow: 0 0 0 3px var(--schlecht), 0 0 14px 3px rgba(255, 90, 77, .7); } }
.fighter.is-left.dodging  .ft-stage { animation: dodgeL .32s ease; }
.fighter.is-right.dodging .ft-stage { animation: dodgeR .32s ease; }
@keyframes dodgeL { 40% { transform: translateX(-15px) skewX(6deg); } 60% { transform: translateX(-11px); } }
@keyframes dodgeR { 40% { transform: translateX(15px) skewX(-6deg); } 60% { transform: translateX(11px); } }
/* Schattenkampf: die Gegenseite ist eine dunkle Silhouette von dir selbst */
.fighter.is-shadow .ft-img { filter: brightness(.14) grayscale(1) contrast(1.1); }
.rd.shadow img { filter: brightness(.14) grayscale(1) contrast(1.1); }
.fighter.is-shadow .ft-name { color: var(--ink-soft); }

.fighter.is-dead .ft-stage { opacity: .28; filter: grayscale(1); transform: translateY(10px) rotate(3deg); transition: all .4s ease; }
.fighter.is-dead .ft-name, .fighter.is-dead .ft-hp-txt { opacity: .5; }
.fighter.slide-in .ft-stage { animation: fighterIn .34s ease; }
@keyframes fighterIn { from { opacity: 0; transform: translateY(10px) scale(.88); } to { opacity: 1; transform: none; } }

.fight-ctrl { display: flex; gap: .6rem; justify-content: center; flex-wrap: wrap; margin-top: .8rem; }

/* Belohnungsfenster als Overlay: unscharfer, dunkler Hintergrund, Fenster mittig.
   Standard versteckt; JS blendet es am Kampfende per .show ein. */
.reward-overlay { display: none; position: fixed; inset: 0; z-index: 200;
    align-items: center; justify-content: center; padding: 1rem;
    background: rgba(18, 11, 4, .6);
    -webkit-backdrop-filter: blur(5px); backdrop-filter: blur(5px); }
.reward-overlay.show { display: flex; animation: rewardFade .3s ease; }
@keyframes rewardFade { from { opacity: 0; } to { opacity: 1; } }
/* Trägt denselben Dschungelrahmen wie die Fenster dahinter (framed() mit Klasse
   .pad). Pergament, Innenabstand und Rand kommen daher vom Rahmen – hier stünde
   sonst ein zweiter, eckiger Rand mitten im ornamentierten.
   Schatten als drop-shadow statt box-shadow: der folgt der Alpha-Kante der
   Ecken-Ornamente, ein box-shadow läge als Rechteck darum herum. */
.reward-win { width: min(92vw, 370px); text-align: center;
    /* steht auf hellem Pergament -> Text-Token dunkel (die .card ringsum stellt sie hell). */
    /* HIER MÜSSEN LITERALE STEHEN, keine Variablen: Diese Zeile SETZT --ink zurück,
       weil der Bereich auf hellem Pergament liegt und nicht im dunklen Kasten.
       `--ink: var(--ink)` wäre ein Selbstbezug – in CSS ungültig, die Variable
       behielte den geerbten hellen Wert und die Schrift verschwände (passiert am
       18.08.2026 genau so: Kontrast 1,07 in der Set-Liste). */
    --ink: #241a0e; --ink-soft: #6b5236; color: var(--ink);
    filter: drop-shadow(0 14px 26px rgba(0, 0, 0, .55));
    animation: rewardPop .42s cubic-bezier(.2, .9, .3, 1.25); }
@keyframes rewardPop { from { opacity: 0; transform: scale(.85) translateY(12px); } to { opacity: 1; transform: none; } }
/* ÜBERSCHRIFT AUF EIGENEM GRUND. Sie steht am oberen Rand des Fensters, wo
   Pergamenttextur, Vignette und die Laubkante des Rahmens ineinanderlaufen. Gegen
   einen so ungleichmäßigen Untergrund kommt keine Schriftfarbe zuverlässig an –
   was auf dem hellen Pergament trägt, verschwindet auf dem dunklen Laub und
   umgekehrt. Vorher stand hier dunkelgrüne Schrift, und der Betreiber konnte sie
   kaum lesen.

   Die Plakette löst das, indem sie den Untergrund selbst mitbringt. Der Hintergrund
   ringsum bleibt unangetastet – er soll ja bleiben, wie er ist.

   BEWUSST OHNE Gold-Ring und Glow: Den trägt die Level-up-Plakette direkt darunter,
   und „Gold-Glow = das Besondere gerade jetzt" verliert seine Wirkung, wenn es
   zweimal im selben Fenster steht.

   Farben gemessen auf der Plakette: grün 10,1–12,5:1, rot 6,5–8,0:1. */
/* `block` + `width: fit-content`, NICHT `inline-block`: Die Plakette soll sich eng
   um den Text legen UND eine eigene Zeile beanspruchen. Als inline-block tat sie nur
   Ersteres – die Level-Aufstiegs-Plakette rutschte daneben statt darunter (gemeldet
   am 20.08.2026). Vor der Umstellung auf die Plakette war die Überschrift ein
   schlichter Block und schob alles Folgende von selbst nach unten. */
.reward-verdict { display: block; width: fit-content; margin: 0 auto .7rem;
    font-size: var(--fs-3xl); font-weight: 900;
    line-height: 1.1; padding: .25rem 1rem; border-radius: 10px;
    background: var(--plakette); box-shadow: 0 2px 8px rgba(0, 0, 0, .5);
    text-shadow: 0 1px 2px rgba(0, 0, 0, .7); }
.reward-win--win  .reward-verdict { color: var(--gut-plakette); }
.reward-win--lose .reward-verdict { color: var(--schlecht); }
/* Dunkle Plakette + Gold-Rand statt hellem Vollton-Pill – gleiche Sprache wie
   Rangliste-Podium/fertiger Dungeon/das neue Porträt unten: „Gold-Glow = das
   Besondere gerade jetzt", nicht ein fröhlicher Sticker. */
.reward-levelup { display: inline-block; margin: 0 auto .6rem; padding: .3rem .9rem; border-radius: 10px;
    font-weight: 900; font-size: var(--fs-xl); color: var(--gold-hell);
    background: var(--plakette);
    box-shadow: 0 2px 8px rgba(0, 0, 0, .5), 0 0 0 2px var(--gold), 0 0 14px rgba(224, 160, 32, .5);
    text-shadow: 0 1px 2px rgba(0, 0, 0, .7); animation: rewardPop .5s ease .1s both; }
/* Charakterstufe-Wechsel (alle 10 Level): altes Porträt klein/grau, neues groß mit
   demselben Gold-Glow wie Platz 1 der Rangliste / ein fertiger Dungeon – überall
   dieselbe Sprache für „das Beste, was es gerade gibt". */
.reward-stufeup { display: flex; align-items: center; justify-content: center; gap: .9rem; margin: .1rem 0 .5rem; }
.reward-stufeup-old { width: 54px; height: 54px; object-fit: cover; border-radius: 10px;
    box-shadow: 0 0 0 2px var(--wood); opacity: .55; filter: grayscale(.7); }
.reward-stufeup-arrow { font-size: var(--fs-2xl); color: var(--ink-soft); font-weight: 700; }
.reward-stufeup-new { width: 96px; height: 96px; object-fit: cover; border-radius: 12px;
    box-shadow: 0 0 0 3px var(--gold), 0 0 24px rgba(224, 160, 32, .65);
    animation: stufeReveal .6s cubic-bezier(.2, .9, .3, 1.25) .35s both; }
@keyframes stufeReveal { from { opacity: 0; transform: scale(.35) rotate(-10deg); } to { opacity: 1; transform: none; } }
.reward-stufeup-txt { font-weight: 700; color: var(--ink); margin: 0 0 1rem; font-size: var(--fs-l); }
.reward-sub { color: var(--ink); font-weight: 600; font-size: var(--fs-xl); margin: 0 0 1.1rem; }
.reward-loot { display: flex; flex-wrap: wrap; gap: .7rem; justify-content: center; margin: .3rem 0 1.2rem; }
.reward-item { min-width: 5.2rem; padding: .6rem .5rem; border-radius: 12px;
    background: rgba(0, 0, 0, .06); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12);
    display: flex; flex-direction: column; align-items: center; gap: .1rem; }
.reward-item .reward-ico { font-size: 1.5rem; }
.reward-item b { font-size: var(--fs-2xl); color: var(--gut-pergament); }
.reward-item.is-loss b { color: var(--schlecht-hell); }
.reward-item .reward-lbl { font-size: var(--fs-s); font-weight: 700; color: var(--ink); text-transform: uppercase; letter-spacing: .04em; }

/* NEUE MEDAILLEN im Belohnungsfenster. Eigene Zeile über den Zahlen-Kacheln, mit
   Goldrahmen – Gold und XP gibt es in jedem Kampf, eine neue Medaillenstufe vielleicht
   dreimal im Jahr. Ohne Absetzung ginge sie zwischen zwei Zahlenkacheln unter.

   Der Name steht kleiner und in --ink statt im großen Grün der Zahlen-Kacheln:
   „Tiefengänger" in Zahlenschriftgröße sprengte die Kachel. */
.reward-medaillen .reward-item {
    min-width: 8rem; gap: .25rem;
    background: rgba(224, 160, 32, .16);
    box-shadow: inset 0 0 0 2px var(--gold), inset 0 0 0 3px var(--tief);
}
.reward-medaillen .reward-item b { font-size: var(--fs-l); color: var(--ink); }

/* FERTIGE TAGESAUFGABEN im Belohnungsfenster. Bewusst RUHIGER als die Medaillen –
   grüner Ring statt goldenem: Eine Tagesaufgabe schafft man täglich, eine
   Medaillenstufe vielleicht dreimal im Jahr. Stünden beide in Gold, wäre der
   Unterschied weg, und genau der ist die Aussage. */
.reward-aufgaben .reward-item {
    min-width: 8rem; gap: .2rem;
    background: rgba(70, 132, 52, .16);
    box-shadow: inset 0 0 0 2px var(--green), inset 0 0 0 3px var(--tief);
}
.reward-aufgaben .reward-item b { font-size: var(--fs-m); color: var(--ink); }
.tag-haken { font-size: var(--fs-l); color: var(--green); line-height: 1; }
/* Die Serie nur an der Aufgabe, die den Tag vollgemacht hat – deshalb hier eine
   eigene, auffälligere Zeile statt einer weiteren Kachel. */
/* NICHT --gold-hell: Das Belohnungsfenster steht auf Pergament (siehe .reward-lbl
   mit --ink), und #ffd97a käme dort auf 1,2:1. Derselbe dunkle Goldton wie bei der
   Seltenheit im Medaillenschrank – gegen den grün getönten Kachelgrund gemessen
   4,6:1. */
.tag-serie-lohn { font-size: var(--fs-s); font-weight: 700; color: var(--gold-pergament); }
/* Erbeutete Items im Belohnungsfenster (Dungeon). Name in Seltenheitsfarbe –
   die dunklen Töne (--r1..--r5), weil das Fenster auf hellem Pergament steht. */
/* Beute: echte Item-Kacheln (wie Inventar/Händler), nebeneinander. Namen und Werte
   stehen im Tooltip (items.js); der Vergleich zieht sich die angelegten Items aus
   dem versteckten .reward-cmp-Block. */
.reward-drops { display: flex; flex-wrap: wrap; gap: .5rem; justify-content: center; margin: 0 0 1rem; }
/* Wie in Tasche/Regal: der .slot gibt die Größe vor, die Kachel füllt ihn aus. */
.reward-drops .slot { width: var(--slot); flex: 0 0 auto; }
.reward-lost { color: var(--schlecht-hell); font-weight: 700; font-size: var(--fs-m); margin: 0 0 .8rem; }

.reward-go { min-width: 8rem; margin: 0; }
.reward-fallback { text-align: center; display: flex; flex-direction: column; gap: .6rem; align-items: center; padding: 1rem; }

/* ===== Dungeon-Detail, neue Darstellung (dungeon-neu.php) =====
   Drei Spalten: Miniboss – Ebenen – Boss. Auf schmalen Schirmen gestapelt,
   Reihenfolge dort: Miniboss, Ebenen, Boss. */
/* `flex: 1 1 0` + `min-width: 0` am Textblock: Ohne das nimmt er seine
   Wunschbreite, passt neben das Bild nicht mehr hinein und springt komplett
   darunter – neben dem 96px-Bild blieb dann eine große leere Fläche stehen.
   So teilt er sich die Zeile und bricht stattdessen INNEN um. */
.dn-kopf { display: flex; align-items: flex-start; gap: 1rem; flex-wrap: wrap; }
.dn-kopf > div { flex: 1 1 0; min-width: 0; }
/* KEINE eigene Schriftgröße: Ein Einstiegstext ist ein Einstiegstext. Übersicht,
   Arena und Quests nutzen dafür die Grundschrift (16px, `.l2` setzt selbst nichts) –
   und diese Beschreibung ist dasselbe. Hier stand einmal 0,86rem, später var(--fs-m);
   beides war eine erfundene Sonderregel für denselben Zweck und ließ die Kopfbox der
   Detailansicht anders aussehen als jede andere Box im Spiel.
   Übrig bleibt nur der Abstand nach unten, der ist hier enger als sonst. */
/* Absätze in der Kopfbox sitzen enger als sonst – als Kontextregel statt eigener
   Klasse: „Absatz IM Dungeonkopf" ist der Sachverhalt, nicht eine neue Textsorte. */
.dn-kopf p { margin: .1rem 0 .5rem; }

/* Kennzahlen als Plaketten. Drei Angaben, die man einzeln liest und vergleicht –
   als Fließtext mit Trennpunkten verschwammen sie zu einer Zeile Kleingedrucktem.
   Der leichte Rahmen genügt zur Trennung; ein voller Kasten je Zahl wäre lauter
   als der Beschreibungstext darüber, und der soll die Kopfbox tragen. */
.dn-fakten { display: flex; flex-wrap: wrap; gap: .35rem; margin-top: .1rem; }

/* GRÖSSE IST GEMESSEN, nicht geschätzt: Bei der schmalsten Desktop-Breite (1280px)
   stehen 585px zur Verfügung; die längste Beschriftung braucht davon rund 527px.
   Ab einer Stufe größer bricht die Zeile um. Wer die Schrift ändert, misst nach. */
.dn-fakt {
    display: inline-block; padding: .27rem .64rem; border-radius: 999px;
    background: rgba(0, 0, 0, .22); box-shadow: inset 0 0 0 1px rgba(224, 160, 32, .3);
    font-size: var(--fs-m); line-height: 1.3; color: var(--box-ink-soft);
    white-space: nowrap;
}

/* HANDY: Raster statt Nebeneinander.
     Zeile 1:  Bild (120px)  |  Beschreibung – sie füllt die Fläche rechts vom Bild
     Zeile 2:  Plaketten über die volle Breite, zwei nebeneinander

   `display: contents` am Zwischen-<div> ist der Kniff: Ohne das wären Beschreibung
   und Plaketten zusammen EIN Rasterfeld und ließen sich nicht getrennt setzen. Der
   Titel darin stört nicht – er liegt als Reiter absolut über der Box.

   Weder Bild noch Plaketten wachsen: Das Bild bleibt bei 120px, die Beschreibung
   wird auf sechs Zeilen gekürzt. 6 × 20px Zeilenhöhe = genau die Bildhöhe, der
   Block schließt also bündig ab. */
@media (max-width: 560px) {
    .dn-kopf {
        display: grid; grid-template-columns: 120px minmax(0, 1fr);
        gap: .6rem; align-items: start;
    }
    .dn-kopf > div { display: contents; }
    .dn-kopf-img { grid-column: 1; grid-row: 1; width: 120px; height: 120px; align-self: start; }
    .dn-kopf p   { grid-column: 2; grid-row: 1; margin: 0;
                   display: -webkit-box; -webkit-box-orient: vertical;
                   -webkit-line-clamp: 6; overflow: hidden; }
    .dn-fakten   { grid-column: 1 / -1; grid-row: 2; margin-top: 0;
                   display: grid; grid-template-columns: 1fr 1fr; gap: .3rem; }
    .dn-fakt     { white-space: normal; text-align: center; }
}
.dn-fakt b { color: var(--box-ink); font-weight: 700; }
/* Warnende Plakette: gleicher Bau wie die anderen, nur der Rand in Gold – sie soll
   auffallen, ohne aus der Reihe zu springen. `cursor: help` verrät, dass ein
   Erklärtext dahinterliegt; ohne das würde niemand draufzeigen. */
.dn-fakt--warn { box-shadow: inset 0 0 0 1px var(--gold); cursor: help; }

/* Das Bild wächst mit dem Text mit, statt darunter Leere zu lassen – begrenzt, damit
   es bei langer Beschreibung nicht die halbe Box einnimmt. */
@media (min-width: 560px) {
    .dn-kopf { align-items: stretch; }
    .dn-kopf-img { height: auto; align-self: stretch; min-height: 120px; max-height: 190px; }
}
.dn-kopf-img { width: 120px; height: 120px; object-fit: cover; border-radius: 12px;
    box-shadow: 0 0 0 2px var(--wood); background: rgba(0, 0, 0, .15); flex: 0 0 auto; }

/* KACHEL DES DUNGEONS ALS HINTERGRUND DER EBENENLISTE.
   Zwei Ebenen unter dem Inhalt: das Bild, darüber ein dunkler Schleier. Beide tragen
   dieselbe Maske, die links über 50px ausläuft – so scheint dort der Fensterhintergrund
   durch. Läge die Maske nur auf dem Bild, bliebe links ein dunkler Balken stehen.

   `center top`, nicht `center`: Die Kacheln sind quadratisch (512×512), die Fläche ist
   breiter als hoch. Bei `cover` fallen rund 217px Höhe weg – zentriert würde oben UND
   unten beschnitten, so liegt der Beschnitt komplett unten, wo bei diesen Bildern
   ohnehin meist Boden ist.

   SCHLEIER 80–92 %: Das ist der Kompromiss aus zwei gegenläufigen Zielen. Gemessen an
   der hellsten Kachel im Spiel (Silberbastion, Mittel 108 – die dunkelste, Abgrund,
   liegt bei 29) kommt die Schrift damit auf 4,3:1. Schwächer wäre das Bild deutlicher,
   aber die Schrift zu blass; stärker wäre vom Bild nichts mehr übrig.

   Die Zeilen tragen mit 20 % Deckkraft ihren Teil zur Lesbarkeit bei – ohne sie müsste
   der Schleier so kräftig sein, dass das Bild verschwindet. Und gesperrte Ebenen stehen
   auf 72 % statt 55 %: Auf ruhigem Grund war das blasse Grau lesbar, auf einem Bild
   nicht mehr. */
.dn-ebenen { position: relative; isolation: isolate; }
.dn-ebenen::before,
.dn-ebenen::after {
    content: ''; position: absolute; inset: 0;
    -webkit-mask-image: linear-gradient(90deg, transparent 0, #000 50px);
            mask-image: linear-gradient(90deg, transparent 0, #000 50px);
}
.dn-ebenen::before { z-index: -2; background: var(--kachel) center top / cover no-repeat; }
.dn-ebenen::after  { z-index: -1;
    background: linear-gradient(180deg, rgba(24,18,11,.80), rgba(24,18,11,.92)); }

.dn-ebenen .dn-row          { background: rgba(255,255,255,.20); }
.dn-ebenen .dn-row.is-mini  { background: rgba(224,145,32,.26); }
.dn-ebenen .dn-row.is-boss  { background: rgba(224,145,32,.38); }
.dn-ebenen .dn-row.is-off   { opacity: .72; }

/* Ebenen links über die volle Breite, beide Bosse rechts untereinander. */
.dn-grid { display: grid; grid-template-columns: 1fr; gap: 1rem; }
@media (min-width: 860px) { .dn-grid { grid-template-columns: 1fr 220px; align-items: start; } }

.dn-side { display: flex; flex-direction: column; gap: 1.2rem; text-align: center; }
.dn-poster { position: relative; border-radius: 12px; overflow: hidden;
    box-shadow: 0 0 0 2px var(--wood); background: rgba(0, 0, 0, .18); }
.dn-poster img { display: block; width: 100%; aspect-ratio: 1 / 1; object-fit: cover; }
.dn-poster.is-off img { filter: grayscale(.9) brightness(.45); }
/* Die folgenden Größen sind bewusst NICHT aus der Schriftskala: Es sind Symbole,
   keine Schrift. Ein Schloss oder ein Beutesymbol richtet sich nach seinem Kasten,
   nicht nach der Leseschrift – zöge man sie mit, würden sie beim nächsten Dreh an
   der Standardgröße unpassend groß oder klein. */
.dn-schloss { position: absolute; inset: 0; display: flex; align-items: center;
    justify-content: center; font-size: 2.2rem; text-shadow: 0 2px 6px rgba(0, 0, 0, .9); }

/* Schräges Namensband UNTEN auf dem Bild – trägt den Gegnernamen, damit er nicht
   noch einmal darunter stehen muss. */
.dn-band {
    position: absolute; left: -12%; bottom: 18%; width: 124%;
    transform: rotate(-9deg);
    padding: .3rem 0; text-align: center;
    font-weight: 900; font-size: var(--fs-m); font-style: italic; letter-spacing: .03em;
    color: #fff; text-shadow: 0 1px 3px rgba(0, 0, 0, .95);
    background: linear-gradient(rgba(20, 12, 5, .92), rgba(20, 12, 5, .78));
    box-shadow: 0 2px 8px rgba(0, 0, 0, .5);
}

/* (Der frühere Sonderfall für .dn-go/.q-accept ist entfallen – die Grundregel
   .btn:disabled deckt beide ab, mit denselben Werten.) */

.dn-mitte { display: flex; flex-direction: column; gap: .35rem; }
/* RASTER statt Flexbox: So stehen Chance, Status und Knopf in ALLEN Zeilen
   bündig untereinander – bei Flex verschöben sie sich mit der Namenslänge. */
/* Vierte Spalte 6rem statt 7.5rem: Die Statusspalte („3× geschafft") brauchte die
   7.5rem nie, während nebenan drei von zehn Ebenennamen abgeschnitten wurden –
   „Lichtung am Wegrand" fehlten 24px. Bei 6rem passen alle Namen vollständig, und
   der Status wird immer noch nicht eng (nachgemessen 18.08.2026 bei 1280px).
   Weniger als 6rem bringt nichts mehr, die Namen passen dann bereits. */
.dn-row { display: grid; grid-template-columns: 1.7rem minmax(0, 1fr) 3.2rem 6rem auto;
    align-items: center; gap: .6rem;
    padding: .4rem .6rem; border-radius: 9px; background: rgba(255, 255, 255, .28); }
@media (max-width: 560px) { .dn-row { grid-template-columns: 1.6rem minmax(0, 1fr) 3rem 2rem auto; gap: .4rem; } }

/* KNOPF AUF DEM HANDY NUR ALS SCHWERT. Gemessen am 18.08.2026 bei 375px: Der Knopf
   nahm 81px, dem Namen daneben fehlten 67px. Das Symbol kostet rund 26px – die
   Differenz geht vollständig an den Namen, weil seine Spalte `1fr` ist.
   Am Desktop bleibt die Beschriftung; dort ist der Platz da. */
@media (max-width: 560px) {
    .dn-go-btn { padding-left: .5rem; padding-right: .5rem; }
}
/* Miniboss und Boss sind die Etappenziele – höher und kräftiger als die übrigen. */
.dn-row.is-mini, .dn-row.is-boss { padding-top: .75rem; padding-bottom: .75rem; }
.dn-row.is-mini { background: rgba(224, 145, 32, .14); box-shadow: inset 0 0 0 1px rgba(224, 145, 32, .4); }
.dn-row.is-boss { background: rgba(224, 145, 32, .24); box-shadow: inset 0 0 0 1px rgba(224, 145, 32, .6); }
.dn-row.is-mini .dn-name, .dn-row.is-boss .dn-name { font-weight: 800; font-size: var(--fs-l); }
.dn-lock { cursor: help; }
.dn-row.is-off { opacity: .55; }
.dn-nr { width: 1.7rem; height: 1.7rem; flex: 0 0 auto; border-radius: 50%;
    display: flex; align-items: center; justify-content: center;
    font-weight: 800; font-size: var(--fs-m); background: rgba(0, 0, 0, .25); }
/* Lange Ortsnamen kürzen statt die Spalten zu sprengen. */
.dn-name { font-weight: 700; }
.dn-chance { font-weight: 800; font-size: var(--fs-m); text-align: right; }
.dn-status { font-size: var(--fs-m); color: var(--ink-soft); white-space: nowrap; }
.dn-row .dn-go { margin: 0; }
.dn-row .dn-go .btn { width: 100%; padding: .35rem .8rem; font-size: var(--fs-m); }
@media (max-width: 560px) { .dn-status { text-align: center; } }

.dn-unten { display: grid; grid-template-columns: 1fr; gap: .9rem; }
@media (min-width: 700px) { .dn-unten { grid-template-columns: 1fr 1fr; } }
.dn-box { padding: .7rem .8rem; border-radius: 10px; background: rgba(255, 255, 255, .28); }
.dn-box.is-open { background: rgba(224, 160, 32, .16); box-shadow: inset 0 0 0 1px rgba(224, 160, 32, .5); }
.dn-box-titel { font-weight: 800; margin-bottom: .3rem; }
.dn-box .l2 { margin: .2rem 0; }
.rar-5-txt { color: var(--r5); }

/* Mögliche Beute auf der Dungeon-Kachel – EINE Zeile: ⚔ Ebenen | 👑 Boss. */
/* Zwei Gruppen nebeneinander: links die Ebenen, rechts die Erstkill-Belohnung.
   Das Symbol steht ÜBER seiner Gruppe – dadurch liegen alle Rahmen auf einer Linie
   und alle Prozentzahlen darunter. */
.dg-loot { display: flex; gap: .35rem; align-items: flex-start; justify-content: center; flex-wrap: wrap; }
.dg-loot-grp { display: flex; flex-direction: column; align-items: center; gap: .1rem; cursor: help; }
.dg-loot-row { display: flex; gap: .2rem; }
/* Trennstrich NUR, solange beide Gruppen nebeneinander stehen. Auf schmalen Kacheln
   rutschen sie untereinander – dort wäre ein senkrechter Strich sinnlos. Ab drei
   Rasterspalten (620 px) ist die Kachel breit genug für eine Zeile. */
.dg-loot-trenn { display: none; }
@media (min-width: 620px) {
    .dg-loot-trenn { display: block; width: 1px; height: 34px; margin: .6rem .15rem 0;
        background: rgba(255, 255, 255, .22); flex: 0 0 auto; }
}
.dg-loot .item-tile { width: 20px; border-width: 1px; border-radius: 4px; cursor: help; }
/* Symbol MIT Wort: „⚔ Ebenen" und „👑 Bosse" – zwei kurze Wörter kosten kaum Platz,
   nehmen der Kachel aber jedes Rätselraten. */
.dg-loot-ico { font-size: var(--fs-xs); opacity: .85; line-height: 1.1; white-space: nowrap;
    letter-spacing: .01em; }
/* ===== Quests ===== */
.q-energie { margin-top: .6rem; }
.q-en-txt { font-size: var(--fs-l); margin-bottom: .3rem; }
.q-en-bar { height: 12px; border-radius: 7px; overflow: hidden; background: rgba(0, 0, 0, .22);
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .15); }
.q-en-bar i { display: block; height: 100%; border-radius: 7px;
    background: linear-gradient(90deg, #3aa8d8, #7fd8f5); transition: width .3s ease; }

/* Fünf Schwierigkeitsstufen wie im Original */
.q-diffs { display: flex; flex-wrap: wrap; gap: .4rem; margin-top: .6rem; }
.q-diff { flex: 1 1 5.6rem; cursor: pointer; font: inherit; text-align: center;
    padding: .45rem .3rem; border-radius: 9px; border: 0;
    background: rgba(255, 255, 255, .28); color: var(--ink); line-height: 1.15; }
.q-diff .q-stars { display: block; color: #8a7a5a; font-size: var(--fs-m); letter-spacing: .04em; }
.q-diff .q-diff-name { display: block; font-size: var(--fs-s); font-weight: 700; }
.q-diff.active { background: var(--green); color: #fff; box-shadow: 0 2px 0 var(--green-d); }
.q-diff.active .q-stars { color: var(--gold-hell); }
/* Gewinnchance je Stufe – gleiche Ampelfarben wie im Dungeon (.dg-chance--*) */
.q-diff .q-chance { display: block; font-size: var(--fs-s); font-weight: 800; margin-top: .1rem; }
.q-diff.active .q-chance { color: #fff; }

/* Zwei Angebote nebeneinander */
.q-offers { display: grid; grid-template-columns: 1fr; gap: .8rem; }
@media (min-width: 560px) { .q-offers { grid-template-columns: 1fr 1fr; } }
.q-offer { display: flex; flex-direction: column; gap: .5rem; padding: .6rem;
    border-radius: 12px; background: rgba(255, 255, 255, .28); }
.q-img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 10px;
    box-shadow: 0 0 0 2px var(--wood); background: rgba(0, 0, 0, .15); }
.q-meta { display: flex; flex-direction: column; gap: .15rem; }
.q-line { font-size: var(--fs-m); }
.q-offer form { margin: 0; }
.q-count { font-variant-numeric: tabular-nums; }

/* ===== Dungeons ===== */
/* Feste Spaltenzahl statt auto-fill: vier nebeneinander auf dem Desktop gibt jeder
   Kachel spürbar mehr Breite – das Artwork wirkt, statt zu schrumpfen. */
/* `minmax(0, 1fr)` statt `1fr`: Sonst kann eine Spalte nicht unter die Mindestbreite
   ihres Inhalts schrumpfen – die Beutezeile mit den Prozentzahlen hat die Kacheln
   dadurch aus dem Raster geschoben. */
.dg-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: .8rem; }
@media (min-width: 620px) { .dg-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 900px) { .dg-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); } }
/* Set-Dungeons: sechs Stück, drei je Reihe. */
@media (min-width: 900px) { .dg-grid--set { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
.dg-card { display: flex; flex-direction: column; align-items: center; gap: .5rem; text-align: center; min-width: 0;
    padding: .6rem; border-radius: 12px; background: rgba(255, 255, 255, .28); }
.dg-card.is-locked { opacity: .72; }
.dg-card.is-locked .dg-img { filter: grayscale(1); }
/* Rahmen und Rundung sitzen am Wrap, nicht am Bild: Das Namensband ragt seitlich
   über das Bild hinaus und muss am Rand sauber abgeschnitten werden. */
.dg-img-wrap { position: relative; width: 100%; overflow: hidden; border-radius: 10px;
    box-shadow: 0 0 0 2px var(--wood); }
.dg-img { display: block; width: 100%; aspect-ratio: 1 / 1; object-fit: cover;
    background: rgba(0, 0, 0, .15); }
.dg-stars { position: absolute; left: 6px; bottom: 6px; color: var(--gold-hell); font-size: var(--fs-m); letter-spacing: .05em;
    text-shadow: 0 1px 2px rgba(0, 0, 0, .9); }
.dg-locktag { position: absolute; right: 6px; top: 6px; font-size: var(--fs-l); }
/* Das Band ist 1:1 .dn-band (Reisekarte/Boss-Poster); .dg-band existiert nur als
   Anker, falls die Kacheln später doch eine eigene Größe brauchen. */
.dg-band { line-height: 1.1; }
.dg-card .btn { width: 100%; margin: 0; }

/* Fortschritt: Balken + Statuszeile. Beide immer vorhanden -> gleiche Kachelhöhe.
   Die Abschluss-Zahl steht als TEXT (nicht als Eck-Badge), damit auch „20×" passt. */
.dg-bar { width: 100%; height: 6px; border-radius: 4px; overflow: hidden;
    background: rgba(0, 0, 0, .18); box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12); }
.dg-bar i { display: block; height: 100%; width: 0; border-radius: 4px; background: var(--green); transition: width .3s ease; }
.dg-status { font-size: var(--fs-s); font-weight: 700; color: var(--ink-soft); line-height: 1.2; min-height: 1.1em; }

/* Angefangen: Balken grün (Standard). Durchgespielt: alles gold + Rahmen. */
.dg-card.is-done { box-shadow: 0 0 0 2px var(--gold), 0 0 10px rgba(224, 160, 32, .45); background: rgba(224, 160, 32, .14); }
.dg-card.is-done .dg-bar i { background: linear-gradient(90deg, #f4b23a, #ffd98a); }
.dg-card.is-done .dg-status { color: var(--gold-hell); }   /* hell: die Karte ringsum ist dunkelbraun */

/* Ampel auf dem Betreten-Button (Premiumfunktion): Kräfteverhältnis zu der Ebene,
   auf der man in diesem Dungeon steht. Ohne die Info bleibt der Button neutral grün. */
.dg-amp--leicht     { background: var(--green); box-shadow: 0 2px 0 var(--green-d); }
/* DUNKLE Schrift auf den beiden hellen Stufen. Weiß kam auf Oliv nur auf 2,77:1 und
   auf Orange auf 2,91:1. Die Farben selbst bleiben – bei einer Ampel ist der Farbton
   die Aussage, den darf man nicht zum Kontrast hin verbiegen. Dunkle Schrift auf Gelb
   und Orange ist ohnehin das Übliche; grün und rot behalten Weiß. */
.dg-amp--machbar, .dg-amp--riskant { color: var(--tief); }
.dg-amp--machbar    { background: #8aa62a; box-shadow: 0 2px 0 #6b831f; }
.dg-amp--riskant    { background: #d98314; box-shadow: 0 2px 0 #a8650f; }
.dg-amp--chancenlos { background: #c0392b; box-shadow: 0 2px 0 #8f2a20; }

/* Gewinnchance je Ebene in der Dungeon-Detailliste (gleiche Ampelstufen). */
.dg-chance { font-weight: 800; }
.dg-chance--leicht     { color: var(--gut); }
.dg-chance--machbar    { color: var(--chance-machbar); }
.dg-chance--riskant    { color: var(--chance-riskant); }
.dg-chance--chancenlos { color: var(--chance-aus); }
.dg-bilanz { font-weight: 700; }

.dg-levels { display: flex; flex-direction: column; gap: .45rem; margin-top: .6rem; }

.arena-logbox summary { cursor: pointer; font-weight: 700; color: var(--ink-soft); }
.arena-logbox[open] summary { margin-bottom: .5rem; }

@media (prefers-reduced-motion: reduce) {
    .fighter.hurt .ft-stage, .fighter.attacking .ft-stage, .fighter.dodging .ft-stage,
    .fighter.hurt .ft-img, .fighter.slide-in .ft-stage { animation: none; }
}

/* ===== Einstellungen: Aussehen ===== */
/* Zwei große Kacheln zur Wahl des Geschlechts, darunter die Stufenleiste. Die
   Leiste ist reine ANZEIGE – die Stufe folgt dem Level und ist nicht wählbar. */
.ein-wahl { display: flex; gap: 1rem; flex-wrap: wrap; justify-content: center; margin-top: .75rem; }
.ein-kachel { display: flex; flex-direction: column; align-items: center; gap: .4rem;
    padding: .5rem; border: 0; border-radius: 12px; cursor: pointer;
    background: rgba(90, 60, 25, .12); box-shadow: inset 0 0 0 1px rgba(120, 90, 50, .5);
    color: var(--ink); font: inherit; }
.ein-kachel img { width: clamp(110px, 30vw, 190px); aspect-ratio: 1 / 1; object-fit: cover;
    border-radius: 10px; box-shadow: 0 0 0 2px var(--wood); display: block;
    background: var(--charbg) no-repeat center / cover; }
.ein-stufe img { background: var(--charbg) no-repeat center / cover; }
.ein-kachel:hover:not(.active) { background: rgba(90, 60, 25, .22); }
.ein-kachel.active { box-shadow: inset 0 0 0 2px var(--green); cursor: default; }
.ein-kachel.active img { box-shadow: 0 0 0 2px var(--green), 0 0 8px 1px rgba(77, 145, 57, .5); }
.ein-lbl { font-weight: 800; font-size: var(--fs-l); }

.ein-leiste { display: grid; grid-template-columns: repeat(5, 1fr); gap: .4rem; margin-top: .75rem; }
@media (min-width: 620px) { .ein-leiste { grid-template-columns: repeat(10, 1fr); } }
.ein-stufe { position: relative; display: flex; flex-direction: column; align-items: center;
    gap: .15rem; font-size: var(--fs-xs); color: var(--ink-soft); }
.ein-stufe img, .ein-zu { width: 100%; aspect-ratio: 1 / 1; border-radius: 8px; display: flex;
    align-items: center; justify-content: center; object-fit: cover;
    box-shadow: 0 0 0 1px var(--wood); background: rgba(0, 0, 0, .15); }
.ein-stufe.is-zu { opacity: .55; }
.ein-stufe.is-jetzt img { box-shadow: 0 0 0 2px var(--green); }
.ein-stufe.is-jetzt b { color: var(--green); }

/* Unehre neben der Ehre – bewusst unauffällig rot, es ist ein Makel, kein Wert. */
/* Steht überall auf dunklem Grund (Namensplakette, Arena- und Ranglisten-Zeilen).
   #d9534f kam dort nur auf 3,2:1 – für so kleine Schrift zu wenig; #ff8a80
   erreicht 5,5:1 und bleibt derselbe Warnton. */
.np-shame { color: var(--schlecht); font-weight: 700; margin-left: .3rem; cursor: help; }

/* Offene Fehde: Er hat öfter zugeschlagen als du. So oft darfst du zurück, ohne
   Unehre – deshalb warm und einladend statt warnend rot wie „ehrlos". */
.arena-opp.is-fehde { background: rgba(224, 145, 32, .18); }
.arena-opp.is-fehde:hover { background: rgba(224, 145, 32, .3); }
/* Wie .arena-opp-warn Vollton statt halbtransparent – dunkelbraune Schrift auf
   halbdurchsichtigem Orange kam auf dem braunen Kartengrund auf 1,3:1 und sah
   aus wie ein leerer gelber Balken. Umgekehrt gedreht: dunkler Text auf hellem
   Bernstein (5,9:1). Bleibt warm/einladend, nicht warnend wie „ehrlos". */
.arena-opp-rache { font-size: var(--fs-s); font-weight: 800; color: #2e2317; cursor: help;
    padding: .1rem .4rem; border-radius: 6px; background: var(--set-orange); }

/* Gerade selbst überfallen worden und deshalb kurz nicht angreifbar. Bewusst
   sachlich blaugrau statt Warnfarbe: Das ist kein Fehlverhalten des anderen und
   auch kein Fehler – nur ein Zustand, der von allein vergeht. */
.arena-opp-schutz { font-size: var(--fs-s); font-weight: 800; color: #d7e6f2; cursor: help;
    padding: .1rem .4rem; border-radius: 6px; background: #4a6274;
    text-shadow: 0 1px 1px rgba(0, 0, 0, .5); }
/* 24-h-Sperre des Schutztranks. Eigene Farbe (erdig statt blaugrau), weil es etwas
   ANDERES bedeutet als „geschützt": nicht Minuten, sondern Stunden, und der Grund
   liegt beim Angreifer selbst. Kontrast gemessen: 6,68:1 – die Schutz-Marke daneben
   liegt bei 5,01:1, diese ist also sogar etwas besser lesbar. */
.arena-opp-sperre { font-size: var(--fs-s); font-weight: 800; color: #f0e2c4; cursor: help;
    padding: .1rem .4rem; border-radius: 6px; background: #5a4a2e;
    text-shadow: 0 1px 1px rgba(0, 0, 0, .5); }

/* Knopf in der Gegnerliste, wenn ein Angriff gerade nicht möglich ist (eigener
   Cooldown oder Schutzfrist des Gegners). Nur ausgegraut, NICHT gesperrt: Er
   führt in die Vorschau, und die soll man jederzeit ansehen können. */
.arena-opp .btn.is-wartet { background: #6b6257; box-shadow: 0 2px 0 #4a443c; color: #d8d2c7; }

/* Rangliste: eigene Zeile grün markiert, gleiche Sprache wie überall sonst „das bist du". */
.arena-opp.is-self { background: rgba(77, 145, 57, .20); }
.arena-opp.is-self:hover { background: rgba(77, 145, 57, .3); }
.arena-opp-rank { font-weight: 800; width: 1.6rem; flex: 0 0 auto; text-align: center; font-size: var(--fs-l); }

/* Rangliste: Kopfzeile (Platzierung + Seitengröße) und Pager. */
.rl-head { display: flex; align-items: center; justify-content: space-between; gap: .6rem; flex-wrap: wrap; margin: .4rem 0; }
.rl-size { margin: 0; }
.rl-size .adm-navlink { padding: .15rem .55rem; font-size: var(--fs-s); }
.rl-pager { display: flex; align-items: center; justify-content: center; gap: .8rem; margin-top: .8rem; }
.rl-pager .btn.is-off { opacity: .5; pointer-events: none; }

/* Top 3 der Rangliste: gleiche Zeile wie der Rest, nur das Porträt wächst mit
   dem Rang (Siegertreppchen-Effekt), Rahmenfarbe = Medaille. Platz 1 bekommt
   denselben Gold-Glow wie ein abgeschlossener Dungeon (.dg-card.is-done) –
   hier wie dort: „das Beste, was es gerade gibt". */
.arena-opp.is-rank1 .arena-opp-img { width: 92px; height: 92px; box-shadow: 0 0 0 3px var(--gold), 0 0 14px rgba(224, 160, 32, .5); }
.arena-opp.is-rank2 .arena-opp-img { width: 78px; height: 78px; box-shadow: 0 0 0 2px #b9c0c6, 0 0 8px rgba(185, 192, 198, .4); }
.arena-opp.is-rank3 .arena-opp-img { width: 68px; height: 68px; box-shadow: 0 0 0 2px #c98a4b, 0 0 8px rgba(201, 138, 75, .4); }

/* „Während du weg warst" – Angriffe, die man verpasst hat. */
/* ÜBER DIE VOLLE BREITE, Zahl mittig über ihrer Beschriftung. Vorher eine
   linksbündige Reihe – die Zahlen klebten am linken Rand ihres Textes und rechts
   blieb die halbe Karte leer.

   `flex: 1 0 auto` STATT GLEICH BREITER SPALTEN. Ein Raster mit sechs gleichen
   Spalten sieht auf dem Papier sauberer aus, geht hier aber nicht auf: Die
   längste Beschriftung braucht 109px („davon gewonnen", gemessen bei 14px;
   „Angriffe auf dich" 106, „Gold für Abwehr" 104), sechs solche Spalten samt
   Lücken also 752px – die Karte im Hauptfenster ist keine 660 breit. Entweder
   bricht die längste Beschriftung um, oder die sechste Angabe rutscht allein in
   eine zweite Reihe.

   So wächst stattdessen jede Angabe von ihrer eigenen Textbreite aus
   proportional mit, bis die Zeile voll ist. Kein Umbruch (0 als flex-shrink),
   keine Löcher, und auf schmalen Schirmen wandern ganze Angaben in die nächste
   Reihe statt einzelner Wörter. */
.aw-kopf { display: flex; flex-wrap: wrap; gap: .6rem 1rem; margin: .2rem 0 .8rem; }
.aw-zahl { flex: 1 0 auto; display: flex; flex-direction: column;
           align-items: center; text-align: center; }
.aw-zahl b { font-size: var(--fs-2xl); line-height: 1.1; }
.aw-zahl.is-loss b { color: var(--schlecht-flaeche); }
/* --gut-plakette, nicht das frühere #3f7d34: Diese Zahlen stehen auf der braunen
   Karte, dort kam der alte Ton auf 2,55:1 – bei 20px fett gilt 3:1, also durchgefallen.
   Der Token erreicht 8,73:1. */
.aw-zahl.is-win  b { color: var(--gut-plakette); }
.aw-liste { display: flex; flex-direction: column; gap: .35rem; }
.aw-row { display: flex; align-items: center; gap: .6rem; padding: .35rem .5rem;
    border-radius: 8px; background: rgba(255, 255, 255, .28); }
.aw-row img { width: 40px; height: 40px; object-fit: cover; border-radius: 7px;
    box-shadow: 0 0 0 2px var(--wood); background: var(--charbg) no-repeat center / cover; }
.aw-row .aw-txt { display: flex; flex-direction: column; min-width: 7rem; }
.aw-row .aw-txt span { font-size: var(--fs-s); color: var(--ink-soft); }
.aw-row .btn { margin: 0 0 0 auto; padding: .25rem .7rem; font-size: var(--fs-m); }
.aw-erg { font-size: var(--fs-s); font-weight: 800; }
.aw-erg.is-verloren { color: var(--schlecht-flaeche); }
.aw-erg.is-gehalten { color: var(--gut-plakette); }

/* Zähler ungesehener Angriffe am Arena-Knopf. Rot, weil es eine Zumutung ist,
   die auf Antwort wartet – nicht bloß eine Benachrichtigung. */
.nav-angriff { display: inline-block; min-width: 1.15rem; margin-left: .3rem; padding: 0 .25rem;
    border-radius: 999px; background: var(--schlecht-flaeche); color: #fff; font-size: var(--fs-xs); font-weight: 800;
    line-height: 1.15rem; text-align: center; vertical-align: top; }
/* position: relative steht bei .bottomnav .ic weiter oben (Anker für diesen Zähler). */
.bottomnav .nav-angriff { position: absolute; top: -4px; right: -10px; margin: 0; }

/* ===== Schwarzes Brett: Steckbriefe ===== */
/* Fahndungsplakat statt Tabellenzeile – ein Gesicht mit „GESUCHT" bleibt hängen,
   eine Zeile mit Zahlen liest man weg. */
/* Zwei Reihen: die drei größten Rechnungen groß, Platz 4 bis 9 klein darunter.
   Wird eine Fehde ausgeglichen, rückt der nächste nach oben. */
.wanted-grid { display: grid; gap: .8rem; margin-top: .7rem; }
/* FESTE Spaltenzahl statt auto-fit: Sonst zieht sich ein einzelnes Plakat über die
   volle Breite, und das Raster springt bei jeder beglichenen Fehde. Drei oben, sechs
   unten – auch wenn nur eines hängt. */
.wanted-grid--gross { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.wanted-grid--klein { grid-template-columns: repeat(6, minmax(0, 1fr)); gap: .5rem; }
@media (max-width: 560px) {
    .wanted-grid--gross { grid-template-columns: repeat(2, minmax(0, 1fr)); }
    .wanted-grid--klein { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
.wanted { display: flex; flex-direction: column; align-items: center; gap: .3rem;
    padding: .6rem .5rem .5rem; border-radius: 10px; text-align: center;
    /* Echtes Pergament statt weißer Fläche. NICHT bg_box_set.jpg: das ist ein
       778×110-Streifen für einzeilige Zeilen und würde auf einem hohen Plakat zehnfach
       hochgezogen und matschig. `pergament.jpg` ist ein sauberer Ausschnitt aus dem
       großen Hintergrund – ohne die Blätter, die dort in den Ecken sitzen. */
    background: var(--parch) url('../img/pergament.jpg') no-repeat left top / cover;
    box-shadow: inset 0 0 0 1px rgba(90, 60, 25, .5), 0 0 0 2px var(--wood), 0 2px 6px rgba(0,0,0,.25);
    /* HIER MÜSSEN LITERALE STEHEN, keine Variablen: Diese Zeile SETZT --ink zurück,
       weil der Bereich auf hellem Pergament liegt und nicht im dunklen Kasten.
       `--ink: var(--ink)` wäre ein Selbstbezug – in CSS ungültig, die Variable
       behielte den geerbten hellen Wert und die Schrift verschwände (passiert am
       18.08.2026 genau so: Kontrast 1,07 in der Set-Liste). */
    --ink: #241a0e; --ink-soft: #6b5236;
    color: var(--ink); }
.wanted-kopf { font-weight: 900; letter-spacing: .22em; text-transform: uppercase;
    font-size: var(--fs-m); color: var(--schlecht-hell); }
.wanted-img { width: 100%; aspect-ratio: 1 / 1; object-fit: cover; border-radius: 8px;
    box-shadow: 0 0 0 2px var(--wood); background: var(--charbg) no-repeat center / cover;
    filter: sepia(.25) contrast(1.05); }
/* Der Name ist die Hauptsache auf einem Fahndungsplakat – deshalb im selben Rot wie
   „Gesucht", nicht in der Fließtextfarbe. */
.wanted-name { font-weight: 900; font-size: var(--fs-l); line-height: 1.15; color: var(--schlecht-hell); }
.wanted-tat { font-size: var(--fs-s); color: var(--schlecht-hell); font-weight: 700; }
/* Kräftiger Kontrast: Diese beiden Zeilen sind der Inhalt des Plakats – sie standen
   in --ink-soft auf hellem Pergament und waren praktisch nicht lesbar. */
.wanted-offen { font-size: var(--fs-m); color: #3a2a12; font-weight: 700; }
.wanted-offen b { color: var(--schlecht-hell); font-size: var(--fs-l); }
.wanted-kraft { font-size: var(--fs-m); color: #3a2a12; line-height: 1.3; font-weight: 600; }
.wanted-kraft b { color: var(--schlecht-hell); }
.wanted-fuss { display: flex; align-items: center; gap: .5rem; margin-top: .25rem; width: 100%;
    justify-content: center; }
.wanted-wc { font-weight: 800; padding: .15rem .5rem; border-radius: 7px; font-size: var(--fs-m); }
.wanted-fuss .btn { margin: 0; padding: .3rem .8rem; }

/* Kleine Plakate (Platz 4 bis 9): gleicher Aufbau, alles eine Stufe kompakter. */
.wanted-grid--klein .wanted { padding: .4rem .3rem .35rem; gap: .15rem; }
.wanted-grid--klein .wanted-kopf { font-size: var(--fs-2xs); letter-spacing: .12em; }
.wanted-grid--klein .wanted-name { font-size: var(--fs-m); }
.wanted-grid--klein .wanted-tat { font-size: var(--fs-xs); }
.wanted-grid--klein .wanted-offen { font-size: var(--fs-s); }
.wanted-grid--klein .wanted-offen b { font-size: var(--fs-m); }
.wanted-grid--klein .wanted-kraft { font-size: var(--fs-xs); }
.wanted-grid--klein .wanted-fuss { gap: .3rem; flex-wrap: wrap; }
.wanted-grid--klein .wanted-wc { font-size: var(--fs-s); padding: .1rem .35rem; }
.wanted-grid--klein .wanted-fuss .btn { padding: .2rem .5rem; font-size: var(--fs-s); }

/* ===== Quest-Reisekarte ===== */
/* Statt eines Countdowns eine Strecke: links dein Lager, rechts das Ziel, dazwischen
   wandert das Team. Alles CSS – der Fortschritt ist eine einzige Prozentzahl, die
   quests.js im Sekundentakt weiterschiebt. Kein Nachladen, keine Serveranfrage. */
.q-reise { display: flex; align-items: center; gap: .6rem; margin: .8rem 0 .4rem; }
/* Die Ortsbilder nutzen .dn-poster aus dem Dungeonbereich (Rahmen + schräges
   Namensband). Hier nur die Breite – alles andere kommt von dort. */
.q-ort { flex: 0 0 auto; width: clamp(110px, 24vw, 200px); }

/* Geschwungener Weg wie auf einer Schatzkarte. Die Kurve ist ein SVG-Pfad; das Team
   sitzt als normales Element darüber und bekommt seine Position von quests.js aus dem
   Pfad (getPointAtLength) – so läuft es exakt auf der Linie. */
.q-weg { position: relative; flex: 1 1 auto; height: clamp(70px, 12vw, 110px); }
.q-weg-svg { position: absolute; inset: 0; width: 100%; height: 100%; overflow: visible; }
/* Schatzkarten-Optik: kräftige, weit gesetzte Striche mit runden Enden. Ohne
   `non-scaling-stroke` würden die Striche mit dem Seitenverhältnis verzerrt, weil das
   SVG in der Breite anders skaliert als in der Höhe. */
.q-pfad, .q-pfad-fort { fill: none; stroke-width: 3.5; stroke-linecap: round;
    vector-effect: non-scaling-stroke; }
/* Der Weg VORAUS muss auf dem dunklen Brett sichtbar sein – vorher war er fast
   schwarz auf Braun und man sah nur die zurückgelegte Strecke. */
.q-pfad      { stroke: rgba(240, 226, 198, .75); stroke-dasharray: 1 11; }
.q-pfad-fort { stroke: var(--green); stroke-dasharray: 1 11; }

.q-team { position: absolute; transform: translate(-50%, -50%);
    width: clamp(34px, 6vw, 46px); height: clamp(34px, 6vw, 46px);
    border-radius: 8px; overflow: hidden; box-shadow: 0 0 0 2px var(--wood);
    background: var(--parch) url('../img/pergament.jpg') no-repeat center / cover;
    transition: left .6s linear, top .6s linear; }   /* nur der Kopf, wie im Paperdoll */

/* Energie je Minute auf der Angebotskarte – macht sichtbar, dass lange Quests
   sparsamer sind. */
.q-effizienz { font-size: var(--fs-xs); opacity: .8; margin-left: .2rem; }
.q-ziel-name b { font-size: var(--fs-xl); }

/* =====================================================================
   HANDY, HOCHKANT (Fehlerbilder vom Markt)
   Alles hier behebt Symptome kleiner Breiten – nichts davon greift am Desktop.
   ===================================================================== */
@media (max-width: 560px) {
    /* Nichts darf breiter sein als der Bildschirm. Ohne diese Bremse schiebt ein
       einziges zu breites Element die ganze Seite und erzeugt waagerechtes Scrollen
       – am deutlichsten im Querformat. */
    html, body { max-width: 100%; overflow-x: hidden; }

    /* Kopfzeile: Name und Level in EINER Zeile, langer Name wird gekürzt statt
       umgebrochen. Gold und Diamanten bleiben nebeneinander. */
    .topbar { gap: .5rem; padding-left: .6rem; padding-right: .6rem; font-size: var(--fs-l); }
    .topbar > div:first-child { display: flex; align-items: center; gap: .35rem;
        min-width: 0; overflow: hidden; }
    .topbar .badge { flex: 0 0 auto; }
    .topbar .res { gap: .6rem; flex: 0 0 auto; white-space: nowrap; }

    /* Die Söldnerleiste bleibt im Charakterbild statt darüber hinauszuragen. */
    .merc-row { left: 3px; right: 3px; bottom: 3px; }
    .merc-group { gap: 2px; }
}

/* Sterne in der PUPPE: Ihre Slots sind unterhalb von 1280 px nur 38 px breit – zwei
   Dreiergruppen à 11 px brauchen zusammen aber rund 40 px und schoben sich ineinander,
   sodass fünf statt zweimal drei zu sehen waren. Erst am Desktop, wo die Slots über
   70 px messen, ist die normale Größe wieder richtig. */
@media (max-width: 1279px) {
    .paperdoll .item-tile .stars { font-size: 7px; letter-spacing: -1.5px; }
    .paperdoll .item-tile .stars.l { left: 2px; }
    .paperdoll .item-tile .stars.r { right: 2px; }
    .paperdoll .item-tile .lvl { font-size: var(--fs-2xs); padding: 0 .18rem; }
}

/* Beute-Verteilung auf der Dungeon-Kachel: Farbrahmen mit Prozentzahl darunter.
   Die Zahl steht sichtbar dabei, weil es auf dem Handy kein Tooltip gibt und die
   Verteilung inzwischen die wichtigste Aussage einer Kachel ist. */
.dg-loot-x { display: inline-flex; flex-direction: column; align-items: center; gap: 1px; cursor: help; }
.dg-loot-x i { font-style: normal; font-size: var(--fs-2xs); min-height: .7em; line-height: 1; color: var(--ink-soft); }

/* GESCHÜTZTE E-MAIL (helpers.php: mail_schutz). Der Quelltext enthält die Adresse
   rückwärts; diese beiden Zeilen drehen sie für die Anzeige wieder um. Ohne
   `unicode-bidi: bidi-override` täte `direction` bei lateinischer Schrift nichts –
   die Schreibrichtung steht sonst am Zeichen selbst, nicht am Kasten. */
.mailschutz { unicode-bidi: bidi-override; direction: rtl; }
.mailschutz-fertig { unicode-bidi: normal; direction: ltr; }

/* PayPal-Prüfung im Adminbereich. Farbe nur am Rand, damit die Meldung sich vom
   Rest der Server-Karte abhebt, ohne wie eine Fehlerseite zu wirken. */
.pp-pruef { margin-top: .8rem; display: flex; gap: .7rem; align-items: center; flex-wrap: wrap; }
/* HINWEISKASTEN – EINE Regel für alle Meldungen mit farbigem Randbalken.
   Vorher gab es zwei: .pp-ergebnis für die PayPal-Prüfung und .dg-bremse für die
   XP-Tagesgrenze, angelegt im Abstand weniger Tage, ohne dass mir die Doppelung
   auffiel. Genau der Fall, den der Betreiber gemeint hat.

   Die Varianten sind Zustände, keine Orte: gut / schlecht / merken. Wer den
   nächsten Hinweis braucht, nimmt eine davon – und legt keine dritte Klasse an. */
.hinweis { margin: .6rem 0 0; padding: .55rem .7rem; border-left: 3px solid;
           font-size: var(--fs-m); }
.hinweis--gut      { border-color: var(--gut-plakette); color: var(--gut-plakette); font-weight: 700; }
.hinweis--schlecht { border-color: var(--schlecht); color: var(--schlecht); font-weight: 700; }
.hinweis--merk     { border-color: var(--gold); background: rgba(224,160,32,.10);
                     color: var(--ink); }

/* Spielerverwaltung im Adminbereich. */
.sp-suche { margin: .8rem 0; display: flex; gap: .6rem; align-items: center; flex-wrap: wrap; }
/* Name und Kleingedrucktes untereinander – gilt für jede Tabelle dieser Art,
   nicht nur für die Spielerliste. */
.sys-tbl th .small { display: block; font-weight: 400; }
/* Trennzeile in der Diamanten-Übersicht: zwischen Ausgaben und Zugängen sowie vor
   „Unterm Strich". Eine leere Zeile statt eines Abstands an der Nachbarzeile – so
   hängt sie nicht daran, welcher Zweck gerade zuletzt steht. */
.dia-luecke td { height: .7rem; padding: 0; border: 0; }
/* Anker für das schwebende Tooltip; die Zahl selbst bleibt Fließtext. */
.dia-zelle { cursor: help; }
.sp-tat { display: flex; gap: .5rem; align-items: center; flex-wrap: wrap; justify-content: flex-end; }
.sp-tat .pk-in { min-width: 12rem; }

/* Item ohne hinterlegte Grafik – sollte nicht vorkommen, ist aber besser als ein
   leeres Kästchen: der Name macht die Kachel weiter bedienbar. */
.art-fehlt { display: flex; align-items: center; justify-content: center; text-align: center;
             width: 100%; height: 100%; padding: .2rem; font-size: var(--fs-2xs); line-height: 1.15;
             color: var(--box-ink-soft); overflow: hidden; }

/* --- Startseite ------------------------------------------------------ */
/* Das Bild ist ein Querschnitt, kein Quadrat: Es soll Stimmung zeigen und den
   „Sofort spielen"-Knopf NICHT unter die Bildschirmkante schieben. Deshalb feste
   Höhe mit object-fit statt freier Seitenverhältnisse. */
.start-held { margin: .2rem 0 .8rem; border-radius: 10px; overflow: hidden;
              box-shadow: inset 0 0 0 2px var(--gold), inset 0 0 0 4px var(--dunkel); }
.start-bild { display: block; width: 100%; height: 150px; object-fit: cover; }
@media (min-width: 560px) { .start-bild { height: 190px; } }

/* FARBE: dunkle Tinte wie der übrige Text auf dem Pergament – NICHT --box-ink.
   Das ist die Schriftfarbe der dunklen Kästen; auf hellem Grund kommt sie auf
   1,38:1 und ist damit unsichtbar (gemessen 18.08.2026). Hier sind es 9,22:1. */
/* Anreißer-Zeile: eine Aussage, mittig, kräftig. Hieß .start-claim und galt damit
   nur für die Startseite – als .lead ist sie überall brauchbar, und die
   Anmeldeseite benutzte den Namen im Markup ohnehin bereits. */
.lead { margin: 0 0 .8rem; text-align: center; font-weight: 800;
               font-size: var(--fs-xl); color: var(--ink); }

.start-punkte { margin: 0 0 1rem; padding: 0; list-style: none;
                font-size: var(--fs-l); line-height: 1.5; }
.start-punkte li { padding-left: 1.1rem; position: relative; }
.start-punkte li::before { content: '›'; position: absolute; left: .2rem;
                           color: var(--gold); font-weight: 800; }

.start-los { margin: 0; }
/* Eigener, dunklerer Ton als das allgemeine .hint: Dieser Satz erklärt den
   wichtigsten Knopf der Seite und muss gelesen werden. .hint kommt auf dem
   Pergament nur auf 3,93:1, dieser Ton auf 5,45:1. */

/* Die Anmeldung ist zugeklappt, aber nicht versteckt – der Zusammenklapper trägt
   denselben Rahmen wie ein Knopf, damit er als anklickbar gelesen wird. */
/* DIE ZWEITE REIHE der Startseite: „Konto anlegen" und „Ich habe schon ein Konto".
   Beide sind Knöpfe, beide kleiner als „Sofort spielen" und in Holzbraun statt Grün –
   die Rangfolge steht damit in der Größe und in der Farbe, nicht in der Lesbarkeit.

   Vorher waren es graue Textzeilen. Der Kontrast war in Ordnung (gemessen 5,07:1 auf
   dem Pergament), sie wurden trotzdem übersehen: Was nicht wie ein Knopf aussieht,
   sucht man auch nicht als einen. */
.btn-klein { font-size: var(--fs-m); padding: .38rem .9rem; border-radius: 8px; }
.start-zweit { display: block; width: fit-content; margin: .7rem auto 0; }

.start-anmelden { border-top: 1px solid rgba(101,94,77,.5); padding-top: .9rem; margin-top: .9rem; }
.start-anmelden > summary { cursor: pointer; text-align: center; font-weight: 700;
                            list-style: none;
                            display: block; width: fit-content; margin: 0 auto;
                            font-size: var(--fs-m); padding: .38rem .9rem;
                            border-radius: 8px; color: #fff;
                            background: var(--holz-dunkel); box-shadow: 0 2px 0 #3a2814; }
.start-anmelden > summary::-webkit-details-marker { display: none; }
.start-anmelden > summary::after { content: ' ▾'; color: var(--gold); }
.start-anmelden[open] > summary::after { content: ' ▴'; }
.start-anmelden > form { margin-top: .6rem; }

/* Hinweis auf die XP-Tagesbremse. Bewusst KEIN Rot: Es ist kein Fehler und kein
   Verbot, sondern eine Auskunft – rot gelesen wäre es eine Strafe. */

/* Fläschchen als Link (eigene Puppe). Sieht unverändert aus – nur der Zeiger verrät,
   dass man klicken kann. Farbe und Unterstreichung müssen weg, sonst erbt das Bild
   die Linkfarbe und bekommt in manchen Browsern einen Rahmen. */
a.flask { color: inherit; text-decoration: none; cursor: pointer; }
a.flask img { border: 0; }

/* Passwort-Auge. Die Hülle erbt die Feldbreite, der Knopf sitzt rechts IM Feld –
   deshalb rechts Platz lassen, sonst läuft der Text unter das Auge. */
.pw-huelle { position: relative; display: block; }
.pw-huelle > input { width: 100%; padding-right: 2.4rem; }
.pw-auge {
    position: absolute; right: .35rem; top: 50%; transform: translateY(-50%);
    background: none; border: 0; padding: .2rem .3rem; cursor: pointer;
    font-size: var(--fs-xl); line-height: 1; opacity: .75;
}
.pw-auge:hover { opacity: 1; }
.pw-auge:focus-visible { outline: 2px solid var(--gold); border-radius: 4px; }

/* KAUFBARKEIT beim Händler (helpers.php: item_tile_html).
   Geometrie kommt aus der .item-lock-Regel weiter oben – dort ist der Sitz oben rechts
   bereits auf den Upgrade-Pfeil oben links abgestimmt (1px statt 2px von oben, weil
   Emoji-Zeichen anders im Kasten sitzen als das Pfeil-SVG). Eigene Werte hier hätten
   die Abstimmung ein zweites Mal gepflegt und wären auseinandergelaufen. */
.item-teuer { filter: grayscale(1); opacity: .85; }
/* Nur die GRAFIK ausgrauen, nicht den Rahmen: Die Seltenheitsfarbe ist die wichtigste
   Information der Kachel und muss auch dann lesbar bleiben, wenn man sich das Stück
   gerade nicht leisten kann. */
.item-tile.zu-teuer .art { opacity: .45; filter: grayscale(.7); }

/* Spielerzahlen im Adminbereich. Feste Spaltenzahl statt auto-fit: Bei acht Kacheln
   soll die Aufteilung vorhersehbar sein, sonst steht mal eine allein in der letzten
   Reihe (derselbe Fehler wie zuvor beim Diamantpaket-Raster). */
.sz-grid { display: grid; gap: .6rem; grid-template-columns: repeat(2, 1fr); margin: .8rem 0; }
@media (min-width: 560px)  { .sz-grid { grid-template-columns: repeat(4, 1fr); } }
.sz-kachel {
    background: rgba(0,0,0,.22); border-radius: 8px; padding: .55rem .5rem; text-align: center;
    box-shadow: inset 0 0 0 1px rgba(224,160,32,.28);
}
.sz-kachel b { display: block; font-size: var(--fs-2xl); line-height: 1.1; color: var(--box-ink); }
.sz-kachel span { display: block; margin-top: .15rem; font-size: var(--fs-s); color: var(--box-ink-soft); }
/* Die 24-Stunden-Zahl ist die, auf die man täglich schaut – sie bekommt den Goldrand. */
.sz-gross { box-shadow: inset 0 0 0 2px var(--gold); }
.sz-gross b { color: var(--gold-hell); }

/* ADMIN-SPRUNGLISTE (layout.php). Ersetzt auf dem Handy die Symbolreihe des
   Adminbereichs – fünfzehn gleiche Zahnräder nebeneinander waren nicht treffbar
   und nicht unterscheidbar.

   Die Liste füllt die ganze Leiste: Ein Ziel, das man mit dem Daumen erreichen
   muss, soll man nicht suchen müssen. */
.bottomnav--admin { display: flex; align-items: center; padding: .4rem .6rem; }
.adm-sprung { display: flex; align-items: center; gap: .5rem; width: 100%; }
.adm-sprung-ic { flex: 0 0 auto; font-size: var(--fs-xl); }
.adm-sprung select {
    flex: 1 1 auto; width: 100%; min-height: 2.6rem;
    background: rgba(0,0,0,.35); color: var(--box-ink);
    border: 1px solid rgba(224,160,32,.5); border-radius: 8px;
    padding: .4rem .6rem; font-size: var(--fs-l);
}
/* Die Liste selbst zeichnet das Betriebssystem – dort gilt seine eigene Farbwelt,
   nicht unsere. Deshalb die Optionen ausdrücklich dunkel setzen: Sonst steht auf
   manchen Geräten helle Schrift auf hellem Grund. */
.adm-sprung option { background: var(--dunkel); color: var(--box-ink); }

/* --- „Als App installieren"-Leiste (assets/app-install.js) --------------------
   Sitzt ÜBER der unteren Navigation, nicht darunter: Die ist auf dem Handy fest am
   Bildschirmrand (58px + Geräte-Sicherheitsrand), eine Leiste dahinter wäre halb
   verdeckt. Ab 1280px ist die Navigation ausgeblendet, dann rückt die Leiste nach
   unten durch. */
.app-banner {
    position: fixed; z-index: 35;
    left: 8px; right: 8px; margin: 0 auto; max-width: 460px;
    bottom: calc(58px + env(safe-area-inset-bottom) + 8px);
    display: flex; align-items: center; gap: .6rem;
    padding: .55rem .7rem; border-radius: 10px;
    background: var(--box-bg) url('../img/bg_braune_box.jpg') no-repeat center / cover;
    box-shadow: inset 0 0 0 2px var(--gold), 0 4px 14px rgba(0, 0, 0, .6);
    color: var(--box-ink); font-size: var(--fs-s); line-height: 1.3;
}
.app-banner-txt { flex: 1 1 auto; }
.app-banner-go  { flex: 0 0 auto; font-size: var(--fs-s); padding: .3rem .7rem; }
.app-banner-zu {
    flex: 0 0 auto; border: 0; background: none; cursor: pointer;
    color: var(--box-ink-soft, #c3b191); font: inherit; font-size: var(--fs-l);
    padding: 0 .2rem; line-height: 1;
}
.app-banner-zu:hover { color: var(--box-ink); }
@media (min-width: 1280px) { .app-banner { bottom: 12px; } }
