/* ══════════════════════════════════════════════════════════════════════════
   ChillBot — Panel-Designsystem  „Eisgeist II"
   ─────────────────────────────────────────────────────────────────────────
   Das hier ist die GEMEINSAME Grundlage des kompletten Zuschauer-Panels:
   Shell (Navigation), Komponenten, Bewegung, Rangfolge. Seiten-CSS
   (cases.css, crew.css, social.css …) bleibt bestehen und baut darauf auf.

   Drei Leitsätze, die jede Entscheidung hier erklären:

   1. RUHE ALS GRUNDZUSTAND, DRAMA ALS AUSNAHME.
      Navigation, Listen und Formulare sind still und sauber. Glow, Partikel
      und große Bewegung sind für Momente reserviert, die etwas BEDEUTEN:
      Legendary-Drop, Phasenwechsel, World Boss besiegt. Wenn alles leuchtet,
      leuchtet nichts.

   2. NUR transform UND opacity BEWEGEN.
      Jede dauerhafte Animation läuft auf GPU-freundlichen Eigenschaften.
      width/height/top/left werden nicht animiert — das kostet Layout und
      damit die 60 fps, die im Kampf-Bildschirm zählen.

   3. BEWEGUNG IST ABSCHALTBAR.
      `prefers-reduced-motion` UND ein eigener Schalter im Panel
      (`<html data-motion="full|reduced|off">`). Beides greift GLOBAL über
      dieselben Tokens — es gibt keine Animation, die sich davon ausnimmt.
   ══════════════════════════════════════════════════════════════════════════ */

@import url("/static/css/tokens.css");

:root{
  /* ── Shell-Maße ── */
  --rail-w:236px;          /* Desktop-Seitenleiste offen */
  --rail-w-mini:76px;      /* … eingeklappt */
  --topbar-h:60px;
  --tabbar-h:64px;         /* Handy: Daumenleiste */
  --shell-max:1520px;

  /* ── Bewegung: EIN Satz Regler für das ganze Panel ──
     Die Dauern sind die aus der Vorgabe: Navigation 150–300 ms,
     Hover 100–200 ms, Modals 200–300 ms. `--m` ist der Globalfaktor,
     den der Bewegungs-Schalter umlegt (1 = voll, .5 = reduziert, 0 = aus). */
  --m:1;
  --d-hover:calc(140ms * var(--m));
  --d-press:calc(90ms  * var(--m));
  --d-nav:calc(220ms * var(--m));
  --d-modal:calc(260ms * var(--m));
  --d-card:calc(320ms * var(--m));
  --d-event:calc(700ms * var(--m));   /* die großen Momente dürfen Zeit haben */
  --ease:cubic-bezier(.22,.61,.36,1);
  --ease-out:cubic-bezier(.16,1,.3,1);
  --ease-spring:cubic-bezier(.34,1.56,.64,1);
  /* Symmetrisch rein und raus. Für Dauerschleifen wie den Glanzstrich auf
     dem Fortschrittsbalken: die läuft endlos, da darf kein Ende betont sein. */
  --ease-io:cubic-bezier(.65,0,.35,1);

  /* Dieselben Dauern noch einmal nach GESCHWINDIGKEIT benannt statt nach
     Bauteil. Wer eine neue Komponente baut, fragt sich „wie schnell", nicht
     „ist das ein Modal" — und greift dann hierhin. Gleiche Werte, keine
     zweite Quelle: --d-fast IST --d-hover, nur unter dem passenden Namen. */
  --d-instant:calc(100ms * var(--m));  /* Tastendruck, Umschalter */
  --d-fast:calc(170ms * var(--m));     /* Hover, Tooltip, Icon */
  --d-normal:calc(240ms * var(--m));   /* Dropdown, Tab, Navigation */
  --d-large:calc(380ms * var(--m));    /* Modal, großer Layoutwechsel */

  /* Die Kurve des Gleiters. Ganz leichtes Überschwingen (1.22 statt 1.0 am
     Ende) — genug, dass die Bewegung lebendig wirkt, zu wenig, dass sie
     zappelt. Zwei Nachkommastellen weniger und es fühlt sich tot an. */
  --ease-glide:cubic-bezier(.34,1.22,.5,1);

  /* ── Z-Index-Ordnung (eine Quelle, keine Zufallszahlen) ──
     `--z-bg` ist NEGATIV, und das ist kein Versehen: die Hintergrundebene
     liegt hinter Inhalt, der selbst gar nicht positioniert ist. Ein Wert
     von 0 würde sie über jeden gewöhnlichen Textblock heben — nur eine
     negative Stufe legt sie zuverlässig darunter (siehe Abschnitt 0). */
  --z-bg:-1; --z-content:1; --z-sticky:20; --z-rail:40; --z-topbar:45;
  --z-tabbar:50; --z-banner:60; --z-drawer:70; --z-modal:80; --z-toast:90;
  --z-overlay:100;

  /* ── Seltenheiten: Farbe + Glow + Rahmen ──
     Seltenheit darf NIE nur über Farbe laufen (Barrierefreiheit). Jede Stufe
     hat zusätzlich Rahmenstärke, Glow-Intensität und ab Legendary eine
     eigene Textur. Siehe .rar-* weiter unten. */
  --rar-common:#9aa4b2;    --rar-common-glow:rgba(154,164,178,.25);
  --rar-uncommon:#3bd16f;  --rar-uncommon-glow:rgba(59,209,111,.3);
  --rar-rare:#2e9bff;      --rar-rare-glow:rgba(46,155,255,.35);
  --rar-epic:#a64dff;      --rar-epic-glow:rgba(166,77,255,.4);
  --rar-legendary:#ffb300; --rar-legendary-glow:rgba(255,179,0,.45);
  --rar-mythic:#ff2e63;    --rar-mythic-glow:rgba(255,46,99,.5);
}

/* Bewegungsstufen. Der Schalter im Panel setzt data-motion auf <html>;
   die Systemeinstellung greift zusätzlich und immer. */
html[data-motion="reduced"]{--m:.45}
html[data-motion="off"]{--m:0}
@media (prefers-reduced-motion:reduce){
  html:not([data-motion="full"]){--m:0}
}
/* `--m:0` macht jede TRANSITION sofort — Endlos-Animationen (Puls, Schweben,
   Glanz) laufen aber unabhängig davon weiter. Die werden hier getrennt
   abgeschaltet. Zwei Regelblöcke, weil eine Selektorliste keinen @media-Block
   in der Mitte haben darf. */
html[data-motion="off"] *,
html[data-motion="off"] *::before,
html[data-motion="off"] *::after{
  animation-duration:0s!important; animation-iteration-count:1!important;
  transition-duration:0s!important;
}
@media (prefers-reduced-motion:reduce){
  html:not([data-motion="full"]) *,
  html:not([data-motion="full"]) *::before,
  html:not([data-motion="full"]) *::after{
    animation-duration:0s!important; animation-iteration-count:1!important;
    transition-duration:0s!important;
  }
}

/* `hidden` MUSS ausblenden — auch bei eigenem display.
   Das `display:none` fuer `[hidden]` kommt aus dem Browser-Standardblatt, und
   das verliert gegen JEDE Autoren-Regel. Ein `.pEventBar{display:flex}` hebelt
   `n.hidden = true` also aus: der Streifen blieb sichtbar, leer und nicht
   wegklickbar, und die Token-Pille zeigte dauerhaft „0". Dieselbe Regel steht
   aus demselben Grund schon in social.css und stueckweise in crew.css/
   cases.css — hier gilt sie fuer das gesamte Panel, damit die naechste
   Komponente mit eigenem display nicht wieder darauf hereinfaellt. */
[hidden]{display:none!important}

/* ══════════════════════════════════════════════════════════════════════════
   0 · HINTERGRUNDEBENE
   Die feste Ebene hinter der ganzen Oberfläche: Grundverlauf, drei driftende
   Farbwolken, Schneefall (Canvas, gezeichnet von panel.js) und ein feines
   Punktraster. Markup: `_partials/backdrop.html` — dort steht auch, warum
   sie EINMAL existiert statt fünfzehnmal kopiert.

   WARUM DIE WOLKEN OHNE `filter: blur()` AUSKOMMEN
   Der Entwurf zeichnet harte Kreise und weicht sie mit `blur(90–110px)` auf.
   Das stand hier schon einmal und kostete nachweislich die halbe Bildrate
   (8 fps auf der Rangliste, Messung und Begründung in community.css): eine
   Fläche dieser Größe muss bei JEDER Bildänderung neu gerastert werden.
   Weichheit kann der Verlauf selbst — mehrere Stufen, die nach außen
   auslaufen. Der wird einmal gezeichnet und danach nur noch verschoben.
   Die Flächen sind dafür etwas größer als im Entwurf, weil der Blur den
   sichtbaren Bereich über die Elementkante hinausgezogen hat.

   Die Bewegungsnamen des Entwurfs (`aur1/aur2/aur3`) heißen hier `pAur1–3` —
   panel.css stellt jedem eigenen Namen ein `p` voran, damit Seiten-CSS keine
   Keyframes überschreibt, die es nicht kennt.
   ══════════════════════════════════════════════════════════════════════════ */

/* Der <body> darf über der Ebene keine eigene Fläche malen, sonst deckt er
   sie zu — genau daran ist der alte Hintergrund gescheitert: er lag korrekt
   auf einer negativen Stufe, und der positionierte <body> malte seinen
   undurchsichtigen Grund danach darüber. Die Grundfarbe steht am <html>
   (head_css.html), den Verlauf bringt die Ebene selbst mit.

   Die Klasse setzt backdrop.html — so bleibt das Owner-Dashboard, das
   dieselbe Grundlage lädt, unberührt.

   `html` davor ist kein Zierrat: `body.pShell` weiter unten setzt wieder
   eine Fläche und hat dieselbe Spezifität, stünde aber später in der Datei
   und würde gewinnen. Die Klasse ist heute unbenutzt — die Ebene soll aber
   nicht in dem Moment verschwinden, in dem jemand sie benutzt. */
html body.pBackdrop-on{background:transparent}

.pBackdrop{
  position:fixed; inset:0; z-index:var(--z-bg);
  overflow:hidden; pointer-events:none;
  background:radial-gradient(120% 90% at 50% -10%,#0e1224 0%,#0a0c15 45%,#07080e 100%);
}

.pBackdrop-aur{
  position:absolute; border-radius:50%;
  will-change:transform;                 /* nur transform + opacity bewegen */
}
.pBackdrop-aur.is-ice{
  width:76vw; height:76vw; top:-18%; left:-14%; opacity:.5;
  background:radial-gradient(circle,
    rgba(56,214,245,.38) 0%, rgba(56,214,245,.27) 20%, rgba(56,214,245,.155) 38%,
    rgba(56,214,245,.068) 56%, rgba(56,214,245,.02) 72%, rgba(56,214,245,0) 84%);
  animation:pAur1 26s var(--e-io) infinite;
}
.pBackdrop-aur.is-indigo{
  width:72vw; height:72vw; top:6%; right:-18%; opacity:.45;
  background:radial-gradient(circle,
    rgba(99,102,241,.42) 0%, rgba(99,102,241,.3) 20%, rgba(99,102,241,.17) 38%,
    rgba(99,102,241,.076) 56%, rgba(99,102,241,.023) 72%, rgba(99,102,241,0) 84%);
  animation:pAur2 32s var(--e-io) infinite;
}
.pBackdrop-aur.is-violet{
  width:80vw; height:80vw; bottom:-28%; left:18%; opacity:.34;
  background:radial-gradient(circle,
    rgba(168,85,247,.4) 0%, rgba(168,85,247,.28) 20%, rgba(168,85,247,.164) 38%,
    rgba(168,85,247,.072) 56%, rgba(168,85,247,.022) 72%, rgba(168,85,247,0) 84%);
  animation:pAur3 38s var(--e-io) infinite;
}
@keyframes pAur1{
  0%{transform:translate3d(-6%,-4%,0) scale(1)}
  50%{transform:translate3d(8%,6%,0) scale(1.18)}
  100%{transform:translate3d(-6%,-4%,0) scale(1)}
}
@keyframes pAur2{
  0%{transform:translate3d(4%,6%,0) scale(1.1)}
  50%{transform:translate3d(-8%,-6%,0) scale(.9)}
  100%{transform:translate3d(4%,6%,0) scale(1.1)}
}
@keyframes pAur3{
  0%{transform:translate3d(0,0,0) scale(1)}
  50%{transform:translate3d(-10%,8%,0) scale(1.25)}
  100%{transform:translate3d(0,0,0) scale(1)}
}

/* Schneefall. Die Größe setzt panel.js in Gerätepunkten (`width`/`height`),
   die Anzeigegröße kommt von hier — sonst wäre das Bild auf Retina-Geräten
   doppelt so groß wie das Fenster. */
.pBackdrop-snow{position:absolute; inset:0; width:100%; height:100%; opacity:.7}

/* Feines Raster über allem. `overlay` mischt nur INNERHALB der Ebene (die
   ein eigener Stapelkontext ist) — der Inhalt darüber bleibt unberührt.
   Nimmt den Verläufen das Digitale und deckt Banding auf großen Flächen zu. */
.pBackdrop-grid{
  position:absolute; inset:0; opacity:.16; mix-blend-mode:overlay;
  background-image:radial-gradient(rgba(255,255,255,.5) 1px,transparent 1px);
  background-size:3px 3px;
}

/* ══════════════════════════════════════════════════════════════════════════
   1 · SHELL
   Desktop: Seitenleiste links (Icon + Name + Zustand) und schlanke Topbar.
   Handy:   Topbar oben, Daumenleiste unten. Beides gleichzeitig im Markup —
            welche sichtbar ist, entscheidet allein die Breite.
   ══════════════════════════════════════════════════════════════════════════ */

body.pShell{
  min-height:100vh;
  background:var(--bg);
  color:var(--text);
  font-family:var(--f-ui);
}

.pShell-grid{
  display:grid;
  grid-template-columns:var(--rail-w) minmax(0,1fr);
  min-height:100vh;
}
body.pRail-mini .pShell-grid{grid-template-columns:var(--rail-w-mini) minmax(0,1fr)}

/* ── Seitenleiste ── */
.pRail{
  position:sticky; top:0; align-self:start;
  height:100vh; z-index:var(--z-rail);
  display:flex; flex-direction:column; gap:var(--sp-2);
  padding:var(--sp-4) var(--sp-3);
  background:linear-gradient(180deg,rgba(12,14,26,.96),rgba(10,12,21,.92));
  border-right:1px solid var(--stroke);
  backdrop-filter:var(--blur);
  transition:width var(--d-nav) var(--ease);
}
.pRail-brand{
  display:flex; align-items:center; gap:10px;
  padding:6px 8px 14px; text-decoration:none; color:var(--text);
  font-family:var(--f-display); font-weight:700; font-size:var(--fs-6);
  letter-spacing:var(--tracking-tight);
}
.pRail-brand .mark{font-size:22px; line-height:1}
body.pRail-mini .pRail-brand b, body.pRail-mini .pRail-lb,
body.pRail-mini .pRail-sect{display:none}

.pRail-sect{
  font-size:var(--fs-1); text-transform:uppercase; letter-spacing:var(--tracking-wide);
  color:var(--dim); padding:var(--sp-4) 10px var(--sp-1);
}
.pRail-nav{
  position:relative;                    /* Bezugspunkt für den Gleiter */
  display:flex; flex-direction:column; gap:2px; overflow-y:auto; scrollbar-width:thin;
}
.pRail-nav::-webkit-scrollbar{width:6px}
.pRail-nav::-webkit-scrollbar-thumb{background:var(--stroke-2); border-radius:99px}

.pNav-item{
  position:relative; z-index:1;         /* über dem Gleiter */
  display:flex; align-items:center; gap:12px;
  padding:10px 12px; border-radius:var(--r-nav);
  color:var(--muted); text-decoration:none; font-size:var(--fs-4); font-weight:600;
  transition:color var(--d-fast) var(--ease),
             background-color var(--d-fast) var(--ease),
             transform var(--d-press) var(--ease);
}
.pNav-item .pNav-ic{
  flex:0 0 22px; width:22px; height:22px; display:grid; place-items:center;
  font-size:17px; line-height:1;
  transition:transform var(--d-fast) var(--ease-spring), filter var(--d-fast) var(--ease);
}
/* Hover bewegt das Symbol einen Hauch nach rechts — dieselbe Richtung, in die
   der Klick führt. Das ist die einzige Icon-Bewegung in der Leiste. */
.pNav-item:hover{color:var(--text); background:var(--hover)}
.pNav-item:hover .pNav-ic{transform:translateX(2px)}
.pNav-item:active{transform:scale(.985)}
.pNav-item.on{color:var(--text); background:transparent}
.pNav-item.on .pNav-ic{
  transform:none;
  filter:drop-shadow(0 0 10px rgba(99,102,241,.55));
}
.pNav-item .pNav-dot{
  margin-left:auto; width:8px; height:8px; border-radius:99px; background:var(--red);
  box-shadow:0 0 0 3px rgba(251,113,133,.18);
  animation:pFxPulse 2s ease-in-out infinite;
}
.pNav-item .pNav-badge{
  margin-left:auto; min-width:20px; height:20px; padding:0 6px; border-radius:99px;
  display:grid; place-items:center; font-size:var(--fs-1); font-weight:800;
  background:var(--indigo-dim); color:#c7d2fe; border:1px solid rgba(99,102,241,.35);
}
.pNav-item.is-live .pNav-ic{color:var(--red)}
/* Reiter, den es nur gibt, WEIL gerade etwas läuft (Submarathon). Er sitzt
   direkt unter „Start" und darf sich deshalb vom Rest der Gruppe abheben —
   sonst liest man ihn als weiteren Dauerpunkt. Warm getönt statt eingefärbt:
   das Symbol ist ein Emoji und nimmt `color` nicht an, der Schein um das
   Symbol und die hellere Schrift wirken dagegen zuverlässig. Bewusst ohne
   Animation — die Leiste soll nicht flackern, während man etwas anderes tut. */
.pNav-item.is-live-tab{color:var(--text); background:rgba(255,176,32,.07)}
.pNav-item.is-live-tab .pNav-ic{filter:drop-shadow(0 0 9px rgba(255,176,32,.6))}
.pNav-item.is-live-tab:hover{background:rgba(255,176,32,.12)}
body.pRail-mini .pNav-item.is-live-tab{background:transparent}
.pNav-item.locked{opacity:.55}
.pNav-item.locked .pNav-ic::after{content:'🔒'; font-size:10px; position:absolute; margin:10px 0 0 10px}

.pRail-foot{margin-top:auto; display:flex; flex-direction:column; gap:var(--sp-2); padding-top:var(--sp-4)}
.pRail-toggle{
  display:flex; align-items:center; justify-content:center; gap:8px;
  padding:9px; border-radius:var(--r-nav); border:1px solid var(--stroke);
  background:transparent; color:var(--dim); cursor:pointer; font-size:var(--fs-2);
  transition:color var(--d-fast) var(--ease), border-color var(--d-fast) var(--ease),
             background-color var(--d-fast) var(--ease), transform var(--d-press) var(--ease);
}
.pRail-toggle:hover{color:var(--text); border-color:var(--stroke-2); background:var(--hover)}
.pRail-toggle:active{transform:scale(.97)}
.pRail-ic{display:inline-grid; place-items:center; line-height:1;
  transition:transform var(--d-normal) var(--ease-spring)}
/* Der Pfeil zeigt immer dorthin, wohin ein Klick die Leiste schiebt. Vorher
   zeigte er auch im eingeklappten Zustand nach links — also auf einen Weg,
   den es dort gar nicht mehr gab. */
body.pRail-mini .pRail-chev{transform:rotate(180deg)}

/* ── Topbar ── */
.pMain{min-width:0; display:flex; flex-direction:column}
.pTopbar{
  position:sticky; top:0; z-index:var(--z-topbar);
  height:var(--topbar-h); display:flex; align-items:center; gap:var(--sp-3);
  padding:0 var(--sp-5);
  background:rgba(10,12,21,.72); backdrop-filter:var(--blur);
  border-bottom:1px solid var(--stroke);
}
.pTopbar-title{font-family:var(--f-display); font-size:var(--fs-5); font-weight:700}
.pTopbar-title small{display:block; font-size:var(--fs-2); color:var(--dim); font-weight:500}
.pTopbar-spacer{flex:1}
.pTopbar-right{display:flex; align-items:center; gap:var(--sp-2)}

/* Währungs-Pillen */
.pWallet{display:flex; align-items:center; gap:8px}
.pCur{
  display:inline-flex; align-items:center; gap:6px;
  padding:6px 11px; border-radius:var(--r-pill);
  background:var(--card); border:1px solid var(--stroke);
  font-size:var(--fs-3); font-weight:700; font-variant-numeric:tabular-nums;
  transition:border-color var(--d-hover) var(--ease), transform var(--d-press) var(--ease);
}
.pCur .ic{font-size:13px; line-height:1}
.pCur.is-up{animation:pFxBump var(--d-card) var(--ease-spring)}
.pCur[data-cur="coins"]{color:var(--gold)}
.pCur[data-cur="wb_token"]{color:var(--violet)}
.pCur[data-cur="scraps"]{color:var(--green)}
.pCur[data-cur="event"]{color:var(--ice)}

.pIconBtn{
  position:relative; width:38px; height:38px; border-radius:var(--r-sm);
  display:grid; place-items:center; cursor:pointer;
  background:var(--card); border:1px solid var(--stroke); color:var(--muted);
  transition:color var(--d-hover) var(--ease), background-color var(--d-hover) var(--ease),
             transform var(--d-press) var(--ease);
}
.pIconBtn:hover{color:var(--text); background:var(--card-2)}
.pIconBtn:active{transform:scale(.94)}
.pIconBtn .pBadge{
  position:absolute; top:-4px; right:-4px; min-width:18px; height:18px; padding:0 5px;
  border-radius:99px; background:var(--red); color:#fff;
  font-size:10px; font-weight:800; display:grid; place-items:center;
  border:2px solid var(--bg);
}
.pAvatar{
  width:38px; height:38px; border-radius:50%; background:var(--grad);
  display:grid; place-items:center; font-weight:800; cursor:pointer;
  background-size:cover; background-position:center;
  border:2px solid transparent; transition:border-color var(--d-hover) var(--ease);
}
.pAvatar:hover{border-color:var(--stroke-glow)}

/* ── Inhaltsbereich ── */
.pContent{
  flex:1; width:100%; max-width:var(--shell-max);
  margin:0 auto; padding:var(--sp-5) var(--sp-5) calc(var(--sp-8) + 20px);
}
.pPage{animation:pFxPageIn var(--d-nav) var(--ease-out) both}
.pPage-head{margin-bottom:var(--sp-5)}
.pPage-head h1{
  font-family:var(--f-display); font-size:var(--fs-8); line-height:var(--lh-tight);
  letter-spacing:var(--tracking-tight); margin-bottom:6px;
}
.pPage-head p{color:var(--muted); font-size:var(--fs-4); max-width:70ch}

/* ── Handy-Navigation ── */
.pMobile{display:none}
.pTabbar{
  position:fixed; left:0; right:0; bottom:0; z-index:var(--z-tabbar);
  height:calc(var(--tabbar-h) + env(safe-area-inset-bottom));
  padding-bottom:env(safe-area-inset-bottom);
  display:grid; grid-template-columns:repeat(5,1fr);
  background:rgba(10,12,21,.94); backdrop-filter:var(--blur-strong);
  border-top:1px solid var(--stroke);
}
.pTab{
  display:flex; flex-direction:column; align-items:center; justify-content:center; gap:3px;
  color:var(--dim); text-decoration:none; font-size:10px; font-weight:700;
  background:none; border:0; cursor:pointer; position:relative; z-index:1;
  transition:color var(--d-fast) var(--ease), transform var(--d-press) var(--ease);
}
.pTab .pTab-ic{font-size:19px; line-height:1;
  transition:transform var(--d-fast) var(--ease-spring)}
.pTab:active{transform:scale(.93)}
.pTab.on{color:var(--text)}
.pTab.on .pTab-ic{transform:translateY(-2px) scale(1.1)}
.pTab .pNav-dot{position:absolute; top:8px; right:calc(50% - 16px)}
/* Mittlerer Aktions-Knopf (World Boss / Kampf) */
.pTab.pTab-hero .pTab-ic{
  width:44px; height:44px; border-radius:50%; display:grid; place-items:center;
  background:var(--grad); color:#08101d; font-size:21px; margin-top:-14px;
  box-shadow:var(--shadow-glow);
}
.pTab.pTab-hero.is-live .pTab-ic{animation:pFxBeacon 1.8s ease-in-out infinite}

@media (max-width:860px){
  .pShell-grid{grid-template-columns:minmax(0,1fr)}
  .pRail{display:none}
  .pMobile{display:block}
  .pTopbar{padding:0 var(--sp-4); height:56px}
  .pContent{padding:var(--sp-4) var(--sp-4) calc(var(--tabbar-h) + 40px)}
  .pPage-head h1{font-size:var(--fs-7)}
}

/* ══════════════════════════════════════════════════════════════════════════
   1a · SEITENWECHSEL — und warum hier NICHTS mehr steht
   ─────────────────────────────────────────────────────────────────────────
   Hier stand eine dokumentübergreifende View-Transition: der Browser
   fotografiert die alte Seite, blendet zur neuen über, und Seitenleiste,
   Kopfzeile und Daumenleiste bleiben dabei an ihrem Platz. Schön gedacht,
   und in Chromium auch nachweislich sauber — gemessen wurde beim Klick
   weder ein Aufhellen noch ein Aussetzer.

   Gemeldet wurde trotzdem: „Bildschirm wird komplett weiß, wenn ich
   zwischen den Punkten navigiere." Also genau die Interaktion, die diese
   Regeln steuern, in einem Browser, der hier nicht zur Verfügung steht.

   Zwei Wege wären möglich gewesen: die Regeln vorsichtiger schreiben und
   hoffen — oder sie entfernen. Es ist reine Dekoration ohne jede Funktion,
   und ein weißer Bildschirm bei jedem Klick wiegt sehr viel schwerer als
   eine hübsche Überblendung. Also raus. Eine Vermutung, die man nicht
   prüfen kann, gehört nicht in eine laufende Anwendung.

   Was den Seitenwechsel weiterhin angenehm macht:
     • `.pPage` blendet den Inhalt beim Ankommen ein (rein lokal, kein
       Browsermechanismus, in jedem Browser identisch).
     • Der Aktiv-Zeiger fährt schon beim Klick los, also BEVOR die neue
       Seite lädt (siehe wireGlideClicks in panel.js). Genau das war ohnehin
       der Teil, der die Navigation zusammenhängend wirken lässt — er
       funktionierte nie über die View-Transition.

   Falls das Weiß auch ohne diesen Block bleibt, liegt es nicht hier,
   sondern an der Zeit bis zum ersten gemalten Bild: der Browser malt gar
   nichts, solange ein render-blockierendes Stylesheet fehlt. Dagegen
   arbeiten die preloads in _partials/head_css.html und die Kompression in
   bot.py.
   ══════════════════════════════════════════════════════════════════════════ */

/* ══════════════════════════════════════════════════════════════════════════
   1b · DER GLEITER  —  EIN Aktiv-Zeiger für die ganze Anwendung
   ─────────────────────────────────────────────────────────────────────────
   Vorher hatte jede Tabgruppe ihre eigene Vorstellung davon, wie „ausgewählt"
   aussieht: die Rangliste füllte den Reiter mit dem Farbverlauf, die Quests
   nahmen Glasfläche mit Leuchtrand, die Seitenleiste einen Strich am Rand,
   die Daumenleiste einen Strich oben. Vier Sprachen für eine einzige Aussage.

   Jetzt gibt es GENAU EINEN Zeiger, und er ist ein eigenes Element, das
   zwischen den Einträgen HINDURCHFÄHRT. Das ist nicht nur hübscher — es ist
   verständlicher: das Auge folgt einer Bewegung und weiß danach, wo es
   herkam. Ein Hintergrund, der an einer Stelle aus- und an einer anderen
   angeht, erzählt diese Verbindung nicht.

   Warum transform UND width animiert werden:
     Der Zeiger liegt absolut, also AUSSERHALB des Flusses — seine Breite zu
     ändern verschiebt kein einziges Geschwisterelement, das Layout bleibt
     also auf dieses eine Element beschränkt. Der Weg über scaleX wäre zwar
     rein GPU-seitig, würde aber die Rundung und den Rahmen mitverzerren, und
     bei Reitern von „Alle" bis „Hall of Fame" heißt das Faktor 2,5 — sichtbar
     eiförmige Enden. Bewegung läuft daher über translate3d (GPU), die Größe
     über width/height auf diesem einen Element.

   Ansteuerung: `data-slider` auf den Behälter. Alles Weitere macht panel.js —
   auch bei Gruppen, die ihr Markup per innerHTML neu bauen (Quests,
   Rangliste, Crew): ein MutationObserver misst dann einfach neu.
   ══════════════════════════════════════════════════════════════════════════ */

/* Der Behälter muss ein Bezugspunkt sein, damit der Zeiger darin liegen kann.
   `:where()` macht die Regel spezifitätslos — sonst hätte sie das gleiche
   Gewicht wie `.pTabbar{position:fixed}` und, weil sie später steht, die
   Daumenleiste vom unteren Bildrand an den oberen geholt. Genau das ist
   beim ersten Anlauf passiert. So gewinnt jede echte Positionsangabe, und
   Behälter ohne eigene bekommen `relative` geschenkt. */
:where([data-slider]){position:relative}

.pGlide{
  position:absolute; left:0; top:0; z-index:0;
  pointer-events:none;
  border-radius:var(--r-pill);
  opacity:0;
  /* Ruhezustand vor der ersten Messung: unsichtbar an Position 0. Ohne das
     Startmaß führe der Zeiger beim Laden aus der Ecke ins Bild. */
  transform:translate3d(0,0,0);
  transition:
    transform var(--d-normal) var(--ease-glide),
    width     var(--d-normal) var(--ease-glide),
    height    var(--d-normal) var(--ease-glide),
    opacity   var(--d-fast)   var(--ease);
}
/* Vor der ersten Messung (und bei Größenänderung des Fensters) springt der
   Zeiger ohne Übergang an seinen Platz — sonst gleitet er beim Seitenaufruf
   sichtbar von links herein, und das sieht nach Ladefehler aus. */
.pGlide.is-instant{transition:none}
.pGlide.is-on{opacity:1}

/* Der Gleiter ist nur der RAHMEN — er trägt Maß und Position, sonst nichts.
   Das Sichtbare liegt in zwei Pseudoelementen:
       ::before  die Fläche
       ::after   die Akzentkante
   Der Umweg lohnt sich, weil Varianten die Fläche dadurch einrücken können
   (Daumenleiste), ohne dass die Messung im JS davon etwas wissen muss. */
.pGlide::before,
.pGlide::after{content:''; position:absolute; border-radius:inherit}

/* ── Gestalt „pill": die Grundform. Getönte Fläche, feiner Rand, ein Hauch
      Tiefe. Bewusst KEIN voller Farbverlauf — der schrie vorher aus jeder
      zweiten Tabgruppe und ließ die Seite nach Vorlage aussehen. ── */
.pGlide::before{
  inset:0;
  background:
    linear-gradient(180deg,rgba(255,255,255,.09),rgba(255,255,255,.03)),
    var(--glass);
  border:1px solid rgba(255,255,255,.13);
  box-shadow:
    0 8px 20px -10px rgba(0,0,0,.7),
    0 0 0 1px rgba(99,102,241,.16),
    0 1px 0 rgba(255,255,255,.09) inset;
  transition:box-shadow var(--d-normal) var(--ease);
}
/* Der Marken-Farbverlauf kommt als KANTE zurück, nicht als Füllung: ein
   feiner Strich an der Führungsseite. Er trägt die Farbe, ohne zu blenden. */
.pGlide::after{background:var(--grad); opacity:.95; border-radius:var(--r-pill)}
/* waagerecht (Reiter, Segmente): Kante unten */
[data-slider-axis="x"] .pGlide::after{
  left:22%; right:22%; bottom:-1px; height:2px;
  box-shadow:0 0 12px rgba(99,102,241,.7);
}
/* senkrecht (Seitenleiste): Kante links */
[data-slider-axis="y"] .pGlide::after{
  top:26%; bottom:26%; left:-1px; width:2px;
  box-shadow:0 0 12px rgba(99,102,241,.7);
}

/* ── Gestalt „line": nur der Strich, keine Fläche.
      Für Untermenüs und Stellen, an denen eine Fläche zu laut wäre. ── */
[data-slider~="line"] .pGlide::before{display:none}
[data-slider~="line"][data-slider-axis="x"] .pGlide::after{left:0; right:0}
[data-slider~="line"][data-slider-axis="y"] .pGlide::after{top:0; bottom:0}

/* ── Gestalt „rail": Seitenleisten-Eintrag. Eckiger als eine Pille, weil er
      neben rechteckigen Kacheln sitzt. ── */
[data-slider~="rail"] .pGlide{border-radius:var(--r-nav)}
[data-slider~="rail"] .pGlide::after{border-radius:0 3px 3px 0}

/* ── Gestalt „soft": getönte Fläche ohne Kante — für Segmentschalter, wo der
      Strich neben drei kurzen Wörtern unruhig wirkt. ── */
[data-slider~="soft"] .pGlide::after{display:none}
[data-slider~="soft"] .pGlide::before{
  background:linear-gradient(180deg,rgba(255,255,255,.11),rgba(255,255,255,.045));
  border-color:rgba(255,255,255,.16);
}

/* Solange der Zeiger fährt, darf er ein wenig Leuchten mitnehmen — das ist
   der Moment, der die Bewegung hochwertig macht. Danach ist wieder Ruhe. */
.pGlide.is-moving::before{
  box-shadow:
    0 10px 26px -10px rgba(0,0,0,.75),
    0 0 0 1px rgba(99,102,241,.3),
    0 0 26px -6px rgba(99,102,241,.5),
    0 1px 0 rgba(255,255,255,.12) inset;
}

/* ── Daumenleiste: die Fläche wird eingerückt, damit sie als Kachel unter dem
      Symbol sitzt statt die ganze Leistenhöhe zu füllen. ── */
.pTabbar .pGlide{border-radius:var(--r-nav)}
.pTabbar .pGlide::before{inset:9px 10px}
.pTabbar .pGlide::after{left:34%; right:34%; top:5px; bottom:auto; height:3px}

/* ── Segmentschalter (Alle | Aktiv | Abgeschlossen) ────────────────────────
   Eine Spur, drei Beschriftungen, ein Zeiger dazwischen. Die Spur ist eine
   Grube (dunkler als die Umgebung), damit klar ist: hier wird gewählt, nicht
   gedrückt. */
.pSeg{
  position:relative; display:inline-flex; align-items:center; gap:2px;
  padding:4px; border-radius:var(--r-pill);
  background:rgba(0,0,0,.28); border:1px solid var(--stroke);
  box-shadow:0 1px 0 rgba(255,255,255,.04) inset;
  max-width:100%;
}
.pSeg-i{
  position:relative; z-index:1; flex:1 1 auto; white-space:nowrap;
  display:inline-flex; align-items:center; justify-content:center; gap:7px;
  padding:8px 16px; border:0; background:none; cursor:pointer;
  border-radius:var(--r-pill); color:var(--muted);
  font:var(--ty-cap-w) var(--fs-3)/1 var(--f-ui); font-weight:700;
  transition:color var(--d-fast) var(--ease), transform var(--d-press) var(--ease);
}
.pSeg-i:hover{color:var(--text)}
.pSeg-i:active{transform:scale(.96)}
.pSeg-i.on{color:var(--text)}
.pSeg-i:focus-visible{outline:none; box-shadow:var(--ring)}

/* ── Reitergruppe (waagerecht, scrollbar) ────────────────────────────────── */
.pTabs{
  position:relative; display:flex; align-items:center; gap:4px;
  border-bottom:1px solid var(--stroke); margin-bottom:var(--sp-4);
}
.pTabs-i{
  position:relative; z-index:1; white-space:nowrap;
  display:inline-flex; align-items:center; gap:8px;
  padding:11px 16px; border:0; background:none; cursor:pointer;
  color:var(--muted); font-size:var(--fs-3); font-weight:700;
  border-radius:var(--r-nav) var(--r-nav) 0 0;
  transition:color var(--d-fast) var(--ease), background-color var(--d-fast) var(--ease);
}
.pTabs-i:hover{color:var(--text); background:var(--hover)}
.pTabs-i.on{color:var(--text)}
.pTabs-i:focus-visible{outline:none; box-shadow:var(--ring)}
@media (min-width:861px){.pTabbar{display:none}}

/* ── Überlagerungs-Modus ──────────────────────────────────────────────────
   Die bestehenden Seiten (Booster, Monster, Community, Inventar, Rangliste …)
   haben ihr eigenes Layout mit `.wrap`. Statt sie alle umzubauen — und dabei
   Risiko in acht Vorlagen zu verteilen — liegt die Hülle als FESTE Ebene
   darüber und schiebt den Inhalt per Body-Padding zur Seite. Das Ergebnis
   sieht identisch zum Grid-Modus aus, kostet aber keine Strukturänderung.
   Gesetzt wird die Klasse von _partials/nav.html, sofort beim Rendern. */
body.pShell-overlay{padding-left:var(--rail-w); padding-top:var(--topbar-h)}
body.pShell-overlay.pRail-mini{padding-left:var(--rail-w-mini)}
body.pShell-overlay .pRail{position:fixed; left:0; top:0; width:var(--rail-w)}
body.pShell-overlay.pRail-mini .pRail{width:var(--rail-w-mini); padding-left:10px; padding-right:10px}
body.pShell-overlay.pRail-mini .pNav-item{justify-content:center; padding:11px 0}
body.pShell-overlay .pTopbar{
  position:fixed; top:0; left:var(--rail-w); right:0; width:auto;
  transition:left var(--d-nav) var(--ease);
}
body.pShell-overlay.pRail-mini .pTopbar{left:var(--rail-w-mini)}
body.pShell-overlay [data-eventbar]{
  position:fixed; top:var(--topbar-h); left:var(--rail-w); right:0;
  z-index:var(--z-banner); transition:left var(--d-nav) var(--ease);
}
body.pShell-overlay.pRail-mini [data-eventbar]{left:var(--rail-w-mini)}
body.pShell-overlay .pEventBar{position:relative; top:auto}
/* Sobald der Streifen sichtbar ist, macht der Inhalt Platz — sonst läge er
   unter der Leiste. Die Klasse setzt panel.js beim Ein-/Ausblenden.
   `--eventbar-h` ist die gemessene Höhe: der Streifen bricht auf schmalen
   Schirmen um und ist dann höher als eine Zeile. */
body.pShell-overlay.pHasEventbar{
  padding-top:calc(var(--topbar-h) + var(--eventbar-h,56px));
}
/* Die alte, waagerechte Leiste ist ersetzt — falls eine Seite sie noch
   mitliefert, bleibt sie unsichtbar statt doppelt zu erscheinen. */
body.pShell-overlay > nav.nav{display:none}

@media (max-width:860px){
  body.pShell-overlay{padding-left:0; padding-top:56px}
  body.pShell-overlay .pTopbar,
  body.pShell-overlay [data-eventbar]{left:0}
  body.pShell-overlay .pTopbar{display:none}       /* Handy nutzt .m-topbar */
  body.pShell-overlay [data-eventbar]{top:56px}
  body.pShell-overlay.pHasEventbar{padding-top:calc(56px + var(--eventbar-h,64px))}
}

/* ══════════════════════════════════════════════════════════════════════════
   2 · GLOBALER EVENT-STREIFEN
   Ein laufendes World-Boss-Event muss man auf JEDER Seite bemerken, ohne die
   World-Boss-Seite zu öffnen. Der Streifen sitzt unter der Topbar, ist
   wegklickbar und kommt bei einem neuen Ereignis von selbst zurück.
   ══════════════════════════════════════════════════════════════════════════ */

.pEventBar{
  position:sticky; top:var(--topbar-h); z-index:var(--z-banner);
  display:flex; align-items:center; gap:var(--sp-4);
  padding:11px var(--sp-5);
  background:linear-gradient(100deg,rgba(168,85,247,.22),rgba(56,214,245,.14) 60%,transparent);
  border-bottom:1px solid rgba(168,85,247,.3);
  overflow:hidden;
  animation:pFxSlideDown var(--d-nav) var(--ease-out) both;
}
.pEventBar::before{
  content:''; position:absolute; inset:0;
  background:radial-gradient(120% 200% at 0% 50%,rgba(168,85,247,.28),transparent 60%);
  pointer-events:none;
}
.pEventBar.is-fight{
  background:linear-gradient(100deg,rgba(251,113,133,.24),rgba(255,179,0,.12) 60%,transparent);
  border-bottom-color:rgba(251,113,133,.35);
}
.pEventBar-glyph{
  position:relative; font-size:26px; line-height:1;
  filter:drop-shadow(0 0 12px rgba(168,85,247,.7));
  animation:pFxFloat 3.4s ease-in-out infinite;
}
.pEventBar-body{position:relative; min-width:0; flex:1}
.pEventBar-title{font-weight:800; font-size:var(--fs-4); display:flex; align-items:center; gap:8px}
.pEventBar-sub{color:var(--muted); font-size:var(--fs-2); display:flex; gap:12px; flex-wrap:wrap}
.pEventBar .pCountdown{font-variant-numeric:tabular-nums; font-weight:800; color:var(--text)}
.pEventBar-actions{position:relative; display:flex; align-items:center; gap:8px}
.pEventBar-x{
  background:none; border:0; color:var(--dim); cursor:pointer; font-size:18px;
  padding:4px 8px; border-radius:var(--r-xs);
}
.pEventBar-x:hover{color:var(--text); background:var(--card)}
@media (max-width:860px){
  .pEventBar{top:56px; padding:9px var(--sp-4); gap:var(--sp-3)}
  .pEventBar-sub{font-size:11px}
}

/* ══════════════════════════════════════════════════════════════════════════
   3 · KOMPONENTEN
   ══════════════════════════════════════════════════════════════════════════ */

/* ── Karte ── */
.pCard{
  position:relative; border-radius:var(--r-md);
  background:linear-gradient(160deg,var(--card-2),var(--card));
  border:1px solid var(--stroke); padding:var(--sp-4);
  box-shadow:var(--elev-1);
  transition:transform var(--d-hover) var(--ease),
             border-color var(--d-hover) var(--ease),
             box-shadow var(--d-hover) var(--ease);
}
.pCard.is-interactive{cursor:pointer; will-change:transform}
.pCard.is-interactive:hover{
  transform:translateY(-2px); border-color:var(--stroke-2); box-shadow:var(--elev-2);
}
.pCard.is-interactive:active{transform:translateY(-1px) scale(.995)}
.pCard-head{display:flex; align-items:center; gap:10px; margin-bottom:var(--sp-3)}
.pCard-title{font-family:var(--f-display); font-weight:700; font-size:var(--fs-5)}
.pCard-sub{color:var(--dim); font-size:var(--fs-2)}
.pCard.is-accent{border-color:rgba(99,102,241,.35); box-shadow:var(--glow-ice)}

.pGrid{display:grid; gap:var(--sp-4)}
.pGrid-2{grid-template-columns:repeat(auto-fit,minmax(300px,1fr))}
.pGrid-3{grid-template-columns:repeat(auto-fit,minmax(240px,1fr))}
.pGrid-4{grid-template-columns:repeat(auto-fit,minmax(190px,1fr))}
.pStack{display:flex; flex-direction:column; gap:var(--sp-4)}
.pRow{display:flex; align-items:center; gap:var(--sp-3); flex-wrap:wrap}

/* ── Knöpfe: EINE Hierarchie fürs ganze Panel ── */
.pBtn{
  display:inline-flex; align-items:center; justify-content:center; gap:8px;
  padding:11px 18px; border-radius:var(--r-sm); border:1px solid transparent;
  font-family:var(--f-ui); font-size:var(--fs-4); font-weight:700;
  cursor:pointer; text-decoration:none; color:var(--text);
  background:var(--card-2); border-color:var(--stroke);
  transition:transform var(--d-press) var(--ease),
             filter var(--d-hover) var(--ease),
             background-color var(--d-hover) var(--ease),
             border-color var(--d-hover) var(--ease),
             opacity var(--d-hover) var(--ease);
  will-change:transform;
}
.pBtn:hover{background:var(--glass); border-color:var(--stroke-2)}
.pBtn:active{transform:scale(.97)}
.pBtn:focus-visible{outline:none; box-shadow:var(--ring)}
/* Primär: die EINE Aktion, die die Seite von dir will. Höchstens eine je Block. */
.pBtn-primary{
  background:var(--grad); color:#07101c; border-color:transparent;
  box-shadow:var(--shadow-glow);
}
.pBtn-primary:hover{filter:brightness(1.08)}
.pBtn-danger{background:var(--red-dim); color:#ffd9df; border-color:rgba(251,113,133,.4)}
.pBtn-ghost{background:transparent; border-color:var(--stroke); color:var(--muted)}
.pBtn-ghost:hover{color:var(--text)}
.pBtn-sm{padding:7px 13px; font-size:var(--fs-2)}
.pBtn-lg{padding:15px 28px; font-size:var(--fs-5)}
.pBtn-block{width:100%}
.pBtn[disabled],.pBtn.is-disabled{opacity:.45; cursor:not-allowed; transform:none!important}
.pBtn.is-loading{color:transparent!important; pointer-events:none; position:relative}
.pBtn.is-loading::after{
  content:''; position:absolute; width:16px; height:16px; border-radius:50%;
  border:2px solid rgba(255,255,255,.35); border-top-color:#fff;
  animation:pFxSpin .7s linear infinite;
}
.pBtn.is-cooldown{position:relative; overflow:hidden}
.pBtn.is-cooldown::before{
  content:''; position:absolute; inset:0; background:rgba(0,0,0,.45);
  transform-origin:left; transform:scaleX(var(--cd,0));
  transition:transform .1s linear;
}
/* Erfolgs-Puls nach einer bestätigten Aktion (Claim, Beitritt). */
.pBtn.is-ok{animation:pFxOk var(--d-card) var(--ease-spring)}

.pChip{
  display:inline-flex; align-items:center; gap:6px;
  padding:4px 10px; border-radius:var(--r-pill);
  font-size:var(--fs-1); font-weight:700; letter-spacing:.02em;
  background:var(--card); border:1px solid var(--stroke); color:var(--muted);
}
.pChip.is-ice{background:var(--ice-dim); color:#a5eefd; border-color:rgba(56,214,245,.3)}
.pChip.is-gold{background:var(--gold-dim); color:#fde68a; border-color:rgba(251,191,36,.3)}
.pChip.is-green{background:var(--green-dim); color:#a7f3d0; border-color:rgba(52,211,153,.3)}
.pChip.is-red{background:var(--red-dim); color:#fecdd3; border-color:rgba(251,113,133,.3)}
.pChip.is-violet{background:var(--violet-dim); color:#e9d5ff; border-color:rgba(168,85,247,.3)}

/* ── Fortschritt ── */
.pBar{
  position:relative; height:10px; border-radius:99px; overflow:hidden;
  background:rgba(255,255,255,.07); border:1px solid var(--stroke);
}
.pBar-fill{
  position:absolute; inset:0; transform-origin:left;
  transform:scaleX(var(--v,0));
  background:var(--grad);
  transition:transform var(--d-card) var(--ease-out);
  will-change:transform;
}
.pBar-fill.is-green{background:linear-gradient(90deg,#34d399,#3bd16f)}
.pBar-fill.is-gold{background:linear-gradient(90deg,#f59e0b,#fbbf24)}
.pBar.is-lg{height:16px}
.pBar.is-sm{height:6px}
/* Glanzstrich, der nur bei aktivem Fortschritt läuft. */
.pBar.is-live .pBar-fill::after{
  content:''; position:absolute; inset:0;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.35),transparent);
  transform:translateX(-100%); animation:pFxSheen 2.2s var(--ease-io) infinite;
}
.pBar-label{
  display:flex; justify-content:space-between; font-size:var(--fs-2);
  color:var(--muted); margin-bottom:5px; font-variant-numeric:tabular-nums;
}

/* ── Countdown ── */
.pCountdown{font-variant-numeric:tabular-nums; letter-spacing:.02em}
.pCountdown.is-urgent{color:var(--red); animation:pFxPulse 1s ease-in-out infinite}

/* ── Statistik-Kachel ── */
.pStat{
  padding:var(--sp-3) var(--sp-4); border-radius:var(--r-sm);
  background:var(--card); border:1px solid var(--stroke);
}
.pStat-v{
  font-family:var(--f-display); font-size:var(--fs-7); font-weight:700;
  font-variant-numeric:tabular-nums; line-height:1.1;
}
.pStat-l{font-size:var(--fs-2); color:var(--dim); margin-top:3px}

/* ── Seltenheit ──
   Farbe ALLEIN reicht nicht: jede Stufe bekommt zusätzlich Rahmenstärke,
   Glow und ab Legendary eine bewegte Textur. So bleibt der Unterschied auch
   ohne Farbwahrnehmung erkennbar. */
.rar{--c:var(--rar-common); --g:var(--rar-common-glow)}
.rar-uncommon{--c:var(--rar-uncommon); --g:var(--rar-uncommon-glow)}
.rar-rare{--c:var(--rar-rare); --g:var(--rar-rare-glow)}
.rar-epic{--c:var(--rar-epic); --g:var(--rar-epic-glow)}
.rar-legendary{--c:var(--rar-legendary); --g:var(--rar-legendary-glow)}
.rar-mythic{--c:var(--rar-mythic); --g:var(--rar-mythic-glow)}

.pRarFrame{
  position:relative; border:1px solid var(--c); border-radius:var(--r-sm);
  box-shadow:0 0 0 1px rgba(0,0,0,.4) inset, 0 6px 22px -10px var(--g);
  background:linear-gradient(160deg,color-mix(in srgb,var(--c) 12%,transparent),var(--card));
}
.rar-epic.pRarFrame,.rar-legendary.pRarFrame,.rar-mythic.pRarFrame{border-width:2px}
.rar-legendary.pRarFrame::after,.rar-mythic.pRarFrame::after{
  content:''; position:absolute; inset:-1px; border-radius:inherit; pointer-events:none;
  background:linear-gradient(115deg,transparent 30%,color-mix(in srgb,var(--c) 45%,transparent) 50%,transparent 70%);
  background-size:250% 250%; opacity:.55;
  animation:pFxShimmer 3.6s linear infinite;
}
.pRarBadge{
  display:inline-flex; align-items:center; gap:5px; padding:3px 9px;
  border-radius:var(--r-pill); font-size:10px; font-weight:800;
  text-transform:uppercase; letter-spacing:.07em;
  color:var(--c); background:color-mix(in srgb,var(--c) 16%,transparent);
  border:1px solid color-mix(in srgb,var(--c) 45%,transparent);
}
/* Symbol zusätzlich zur Farbe — Barrierefreiheit. */
.pRarBadge::before{content:'◆'; font-size:8px}
.rar-legendary .pRarBadge::before{content:'★'}
.rar-mythic .pRarBadge::before{content:'✦'}

/* ── Skelette ──
   Sie müssen der späteren Komponente entsprechen, sonst springt das Layout
   beim Einblenden. Deshalb gibt es Skelett-Varianten mit denselben Maßen. */
.pSkel{
  position:relative; overflow:hidden; border-radius:var(--r-xs);
  background:rgba(255,255,255,.05); color:transparent!important;
}
.pSkel::after{
  content:''; position:absolute; inset:0;
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.08),transparent);
  transform:translateX(-100%); animation:pFxSheen 1.35s linear infinite;
}
.pSkel-line{height:12px; margin:7px 0}
.pSkel-line.w40{width:40%} .pSkel-line.w60{width:60%} .pSkel-line.w80{width:80%}
.pSkel-title{height:20px; width:55%; margin-bottom:12px}
.pSkel-card{height:150px; border-radius:var(--r-md)}
.pSkel-tile{height:78px; border-radius:var(--r-sm)}
.pSkel-avatar{width:44px; height:44px; border-radius:50%}

/* ── Leerzustände ── */
.pEmpty{
  text-align:center; padding:var(--sp-7) var(--sp-4); color:var(--muted);
  border:1px dashed var(--stroke); border-radius:var(--r-md);
}
.pEmpty-ic{font-size:34px; margin-bottom:10px; opacity:.7}
.pEmpty h3{font-family:var(--f-display); color:var(--text); margin-bottom:6px}

/* ── Gesperrte Inhalte ──
   Ein Schloss ist eine verpasste Gelegenheit. Gesperrtes zeigt deshalb IMMER
   das Ziel und den Fortschritt dorthin. */
.pLocked{
  position:relative; overflow:hidden; border-radius:var(--r-lg);
  border:1px solid var(--stroke); padding:var(--sp-6) var(--sp-5); text-align:center;
  background:
    radial-gradient(90% 120% at 50% 0%,rgba(168,85,247,.16),transparent 65%),
    linear-gradient(160deg,var(--card-2),var(--card));
}
.pLocked::before{
  content:''; position:absolute; inset:0; pointer-events:none;
  background:repeating-linear-gradient(-45deg,transparent 0 14px,rgba(255,255,255,.02) 14px 28px);
}
.pLocked-ic{font-size:44px; margin-bottom:var(--sp-3); filter:grayscale(.3)}
.pLocked h2{font-family:var(--f-display); font-size:var(--fs-7); margin-bottom:8px}
.pLocked p{color:var(--muted); max-width:52ch; margin:0 auto var(--sp-4)}
.pLocked .pBar{max-width:420px; margin:0 auto}

/* ── Modal ── */
.pModal{
  position:fixed; inset:0; z-index:var(--z-modal);
  display:grid; place-items:center; padding:var(--sp-4);
}
.pModal[hidden]{display:none}
.pModal-scrim{
  position:absolute; inset:0; background:rgba(4,6,12,.72);
  backdrop-filter:blur(6px); animation:pFxFade var(--d-modal) var(--ease) both;
}
.pModal-sheet{
  position:relative; width:min(560px,100%); max-height:86vh; overflow:auto;
  border-radius:var(--r-lg); border:1px solid var(--stroke-2);
  background:linear-gradient(160deg,#131725,#0d101b);
  box-shadow:var(--elev-3);
  animation:pFxModalIn var(--d-modal) var(--ease-out) both;
}
.pModal-sheet.is-wide{width:min(920px,100%)}
.pModal-head{
  display:flex; align-items:center; gap:10px; padding:var(--sp-4) var(--sp-5);
  border-bottom:1px solid var(--stroke); position:sticky; top:0;
  background:inherit; z-index:1;
}
.pModal-head h3{font-family:var(--f-display); font-size:var(--fs-6); flex:1}
.pModal-body{padding:var(--sp-5)}
.pModal-foot{
  padding:var(--sp-4) var(--sp-5); border-top:1px solid var(--stroke);
  display:flex; gap:var(--sp-2); justify-content:flex-end;
}
@media (max-width:640px){
  /* Handy: Blätter kommen von unten, nicht aus der Mitte. */
  .pModal{align-items:flex-end; padding:0}
  .pModal-sheet{width:100%; max-height:90vh; border-radius:var(--r-lg) var(--r-lg) 0 0;
                animation-name:pFxSheetIn}
}

/* ── Schublade (Benachrichtigungen, Einstellungen) ── */
.pDrawer{position:fixed; inset:0; z-index:var(--z-drawer)}
.pDrawer[hidden]{display:none}
.pDrawer-scrim{position:absolute; inset:0; background:rgba(4,6,12,.6);
  animation:pFxFade var(--d-modal) var(--ease) both}
.pDrawer-panel{
  position:absolute; top:0; right:0; height:100%; width:min(400px,92vw);
  background:linear-gradient(180deg,#101321,#0b0e18);
  border-left:1px solid var(--stroke); box-shadow:var(--elev-3);
  display:flex; flex-direction:column;
  animation:pFxDrawerIn var(--d-modal) var(--ease-out) both;
}
.pDrawer-head{display:flex; align-items:center; gap:10px;
  padding:var(--sp-4) var(--sp-5); border-bottom:1px solid var(--stroke)}
.pDrawer-head h3{font-family:var(--f-display); font-size:var(--fs-5); flex:1}
.pDrawer-body{flex:1; overflow:auto; padding:var(--sp-3) var(--sp-4)}
.pDrawer-foot{padding:var(--sp-3) var(--sp-4); border-top:1px solid var(--stroke)}
@media (max-width:640px){
  .pDrawer-panel{width:100%; top:auto; bottom:0; height:min(80vh,640px);
    border-left:0; border-top:1px solid var(--stroke);
    border-radius:var(--r-lg) var(--r-lg) 0 0; animation-name:pFxSheetIn}
}

/* ── Benachrichtigungs-Eintrag ── */
.pNote{
  display:flex; gap:12px; padding:12px; border-radius:var(--r-sm);
  border:1px solid transparent; transition:background-color var(--d-hover) var(--ease);
  text-decoration:none; color:inherit;
}
.pNote:hover{background:var(--card)}
.pNote.is-unread{background:var(--glass); border-color:rgba(99,102,241,.22)}
.pNote-ic{flex:0 0 34px; height:34px; border-radius:var(--r-xs);
  display:grid; place-items:center; font-size:17px; background:var(--card-2)}
.pNote-t{font-weight:700; font-size:var(--fs-3)}
.pNote-b{color:var(--muted); font-size:var(--fs-2); line-height:1.45}
.pNote-time{color:var(--dim); font-size:var(--fs-1); margin-top:3px}
.pNote.rar-legendary .pNote-ic,.pNote.rar-mythic .pNote-ic{
  background:color-mix(in srgb,var(--c) 22%,transparent);
  box-shadow:0 0 18px -4px var(--g);
}

/* ── Toasts ── */
.pToasts{
  position:fixed; z-index:var(--z-toast); right:var(--sp-4); bottom:var(--sp-4);
  display:flex; flex-direction:column; gap:10px; pointer-events:none;
  width:min(380px,calc(100vw - 32px));
}
@media (max-width:860px){.pToasts{bottom:calc(var(--tabbar-h) + 16px)}}
.pToast{
  pointer-events:auto; display:flex; gap:12px; align-items:flex-start;
  padding:13px 15px; border-radius:var(--r-sm);
  background:linear-gradient(150deg,#151a2b,#0f131f);
  border:1px solid var(--stroke-2); box-shadow:var(--elev-2);
  animation:pFxToastIn var(--d-card) var(--ease-spring) both;
}
.pToast.is-out{animation:pFxToastOut var(--d-nav) var(--ease) both}
.pToast-ic{font-size:19px; line-height:1.2}
.pToast-t{font-weight:800; font-size:var(--fs-3)}
.pToast-b{color:var(--muted); font-size:var(--fs-2); margin-top:2px}
.pToast.is-ok{border-color:rgba(52,211,153,.4)}
.pToast.is-err{border-color:rgba(251,113,133,.45)}
/* Legendäre Belohnungen dürfen deutlich mehr auftragen als eine Systemmeldung. */
.pToast.is-epic{border-color:var(--c,var(--rar-epic)); box-shadow:0 0 30px -8px var(--g)}
.pToast.is-epic::before{
  content:''; position:absolute; inset:0; border-radius:inherit; pointer-events:none;
  background:linear-gradient(115deg,transparent 35%,color-mix(in srgb,var(--c) 30%,transparent) 50%,transparent 65%);
  background-size:250% 250%; animation:pFxShimmer 2.4s linear infinite;
}
.pToast{position:relative; overflow:hidden}

/* ── Schnellsuche (Strg+K) ── */
.pPalette{position:fixed; inset:0; z-index:var(--z-overlay); padding-top:12vh}
.pPalette[hidden]{display:none}
.pPalette-scrim{position:absolute; inset:0; background:rgba(4,6,12,.7); backdrop-filter:blur(4px)}
.pPalette-box{
  position:relative; width:min(560px,92vw); margin:0 auto;
  border-radius:var(--r-md); border:1px solid var(--stroke-2);
  background:linear-gradient(160deg,#141828,#0d101b); box-shadow:var(--elev-3);
  overflow:hidden; animation:pFxModalIn var(--d-modal) var(--ease-out) both;
}
.pPalette-in{
  width:100%; padding:16px 18px; background:none; border:0; outline:none;
  color:var(--text); font-size:var(--fs-5); font-family:var(--f-ui);
  border-bottom:1px solid var(--stroke);
}
.pPalette-list{max-height:44vh; overflow:auto; padding:6px}
.pPalette-item{
  display:flex; align-items:center; gap:11px; padding:10px 12px;
  border-radius:var(--r-xs); cursor:pointer; color:var(--muted);
  text-decoration:none; font-size:var(--fs-4);
}
.pPalette-item.on,.pPalette-item:hover{background:var(--glass); color:var(--text)}
.pPalette-item .k{margin-left:auto; font-size:var(--fs-1); color:var(--dim)}
.pPalette-hint{padding:9px 14px; border-top:1px solid var(--stroke);
  color:var(--dim); font-size:var(--fs-1); display:flex; gap:14px}

/* ── Zahlen, die hochzählen ── */
.pNum{font-variant-numeric:tabular-nums}
.pNum.is-up{animation:pFxBump var(--d-card) var(--ease-spring)}

/* ══════════════════════════════════════════════════════════════════════════
   4 · BEWEGUNG
   Alle Keyframes an einem Ort. Ausschließlich transform/opacity/filter.
   ══════════════════════════════════════════════════════════════════════════ */

@keyframes pFxPageIn{from{opacity:0; transform:translateY(8px)} to{opacity:1; transform:none}}
@keyframes pFxFade{from{opacity:0} to{opacity:1}}
@keyframes pFxSlideDown{from{opacity:0; transform:translateY(-10px)} to{opacity:1; transform:none}}
@keyframes pFxModalIn{from{opacity:0; transform:translateY(12px) scale(.97)} to{opacity:1; transform:none}}
@keyframes pFxSheetIn{from{transform:translateY(100%)} to{transform:none}}
@keyframes pFxDrawerIn{from{transform:translateX(100%)} to{transform:none}}
@keyframes pFxToastIn{from{opacity:0; transform:translateX(24px) scale(.96)} to{opacity:1; transform:none}}
@keyframes pFxToastOut{to{opacity:0; transform:translateX(24px) scale(.96)}}
@keyframes pFxSpin{to{transform:rotate(360deg)}}
@keyframes pFxSheen{to{transform:translateX(100%)}}
@keyframes pFxShimmer{to{background-position:250% 0}}
@keyframes pFxBump{0%{transform:scale(1)} 40%{transform:scale(1.16)} to{transform:scale(1)}}
@keyframes pFxOk{0%{transform:scale(1)} 35%{transform:scale(1.07)} to{transform:scale(1)}}
@keyframes pFxPulse{0%,100%{opacity:1} 50%{opacity:.45}}
@keyframes pFxFloat{0%,100%{transform:translateY(0)} 50%{transform:translateY(-4px)}}
@keyframes pFxBeacon{
  0%,100%{box-shadow:0 0 0 0 rgba(251,113,133,.55),var(--shadow-glow)}
  50%{box-shadow:0 0 0 10px rgba(251,113,133,0),var(--shadow-glow)}
}
@keyframes pFxShake{
  0%,100%{transform:translateX(0)} 20%{transform:translateX(-6px)}
  40%{transform:translateX(5px)} 60%{transform:translateX(-3px)} 80%{transform:translateX(2px)}
}
@keyframes pFxRise{from{opacity:1; transform:translateY(0) scale(1)}
                   to{opacity:0; transform:translateY(-56px) scale(1.25)}}
@keyframes pFxPop{from{opacity:0; transform:scale(.6)} 60%{opacity:1; transform:scale(1.12)} to{transform:scale(1)}}
@keyframes pFxFlash{0%{opacity:.9} to{opacity:0}}

/* Wiederverwendbare Klassen — auf Elementen einsetzbar, ohne neue Keyframes. */
.pAnim-in{animation:pFxPageIn var(--d-card) var(--ease-out) both}
.pAnim-pop{animation:pFxPop var(--d-card) var(--ease-spring) both}
.pAnim-shake{animation:pFxShake .45s var(--ease) both}
.pAnim-stagger>*{animation:pFxPageIn var(--d-card) var(--ease-out) both}
.pAnim-stagger>*:nth-child(1){animation-delay:0ms}
.pAnim-stagger>*:nth-child(2){animation-delay:40ms}
.pAnim-stagger>*:nth-child(3){animation-delay:80ms}
.pAnim-stagger>*:nth-child(4){animation-delay:120ms}
.pAnim-stagger>*:nth-child(5){animation-delay:160ms}
.pAnim-stagger>*:nth-child(6){animation-delay:200ms}
.pAnim-stagger>*:nth-child(n+7){animation-delay:240ms}

/* Schwebende Schadenszahl (World Boss, Kampf). */
.pFloatNum{
  position:absolute; pointer-events:none; font-weight:900;
  font-variant-numeric:tabular-nums; text-shadow:0 2px 12px rgba(0,0,0,.7);
  animation:pFxRise 1.05s var(--ease-out) both; will-change:transform,opacity;
}
.pFloatNum.is-crit{color:var(--gold); font-size:1.6em}
.pFloatNum.is-ult{color:#ffb300; font-size:1.7em; filter:drop-shadow(0 0 12px rgba(255,179,0,.55))}
.pFloatNum.is-dot{font-size:.9em; opacity:.85; color:#fb7185}
.pFloatNum .lb{
  display:block; font-size:.42em; font-weight:800; letter-spacing:.08em;
  text-transform:uppercase; opacity:.9; line-height:1.1;
}

/* ══════════════════════════════════════════════════════════════════════════
   5 · HILFSKLASSEN
   ══════════════════════════════════════════════════════════════════════════ */
.pMuted{color:var(--muted)}
.pDim{color:var(--dim)}
.pSmall{font-size:var(--fs-2)}
.pMono{font-family:var(--f-mono)}
.pRight{margin-left:auto}
.pCenter{text-align:center}
.pHide{display:none!important}
.pSr{position:absolute; width:1px; height:1px; overflow:hidden; clip:rect(0 0 0 0); white-space:nowrap}
.pDivider{height:1px; background:var(--stroke); margin:var(--sp-4) 0}
.pScrollX{overflow-x:auto; scrollbar-width:thin; -webkit-overflow-scrolling:touch}
.pScrollX::-webkit-scrollbar{height:6px}
.pScrollX::-webkit-scrollbar-thumb{background:var(--stroke-2); border-radius:99px}

/* Fokus ist überall sichtbar — Tastaturbedienung ist kein Sonderfall. */
:where(a,button,input,select,textarea,[tabindex]):focus-visible{
  outline:none; box-shadow:var(--ring); border-radius:var(--r-xs);
}

/* ══════════════════════════════════════════════════════════════════════════
   6 · TYPOGRAFIE
   ─────────────────────────────────────────────────────────────────────────
   Größe allein macht keine Hierarchie. Eine Überschrift unterscheidet sich
   von Fließtext durch Schriftschnitt, Gewicht, Laufweite UND Zeilenhöhe —
   wer nur die Größe kopiert, bekommt nicht dieselbe Stimme. Deshalb steht
   hier jede Stufe als eine Klasse, statt in jeder Seite neu zusammengesetzt
   zu werden.
   ══════════════════════════════════════════════════════════════════════════ */
.tDisplay,.tH1,.tH2,.tH3{
  font-family:var(--f-display); line-height:var(--lh-tight);
  letter-spacing:var(--tracking-tight); color:var(--text-primary);
}
.tDisplay{font-size:var(--ty-display-s); font-weight:var(--ty-display-w)}
.tH1{font-size:var(--ty-h1-s); font-weight:var(--ty-h1-w)}
.tH2{font-size:var(--ty-h2-s); font-weight:var(--ty-h2-w)}
.tH3{font-size:var(--ty-h3-s); font-weight:var(--ty-h3-w)}
/* Abschnittsmarke: die kleine versale Zeile über einer Gruppe. Sie ordnet,
   ohne wie eine Überschrift um Aufmerksamkeit zu bitten. */
.tSection{
  font-size:var(--ty-sect-s); font-weight:var(--ty-sect-w);
  text-transform:uppercase; letter-spacing:var(--tracking-wide);
  color:var(--text-muted);
}
.tBody{font-size:var(--ty-body-s); line-height:var(--lh-normal); color:var(--text-secondary)}
.tSmall{font-size:var(--ty-small-s); line-height:1.5; color:var(--text-secondary)}
.tCaption{font-size:var(--ty-cap-s); color:var(--text-muted)}
.tLabel{
  font-size:var(--ty-label-s); font-weight:var(--ty-label-w);
  text-transform:uppercase; letter-spacing:var(--tracking-wide);
  color:var(--text-muted);
}
/* Fließtext bleibt lesbar breit. 70 Zeichen ist die Grenze, ab der das Auge
   die nächste Zeile nicht mehr sicher findet. */
.tProse{max-width:70ch; line-height:var(--lh-normal); color:var(--text-secondary)}
.tNum{font-variant-numeric:tabular-nums; font-family:var(--f-mono)}

/* ══════════════════════════════════════════════════════════════════════════
   7 · KNÖPFE — die restlichen Stufen
   ─────────────────────────────────────────────────────────────────────────
   Es gab Primär, Gefahr und Geist. Was fehlte, waren die Stufen dazwischen,
   und deshalb hat sich jede Seite ihre eigenen gebaut (.btn.grad, .qbtn,
   .openBtn, .linkBtn …). Hier stehen sie jetzt einmal.
   ══════════════════════════════════════════════════════════════════════════ */

/* Hover hebt an, Druck senkt ab. Eine Bewegung, überall dieselbe — das ist
   der Unterschied zwischen „reagiert" und „lebt". */
.pBtn{transition:transform var(--d-press) var(--ease),
                 filter var(--d-fast) var(--ease),
                 background-color var(--d-fast) var(--ease),
                 border-color var(--d-fast) var(--ease),
                 box-shadow var(--d-fast) var(--ease),
                 opacity var(--d-fast) var(--ease)}
.pBtn:hover{transform:translateY(-1px); box-shadow:var(--sh-subtle)}
.pBtn:active{transform:translateY(0) scale(.975)}

.pBtn-secondary{
  background:var(--indigo-dim); color:#dbe0ff;
  border-color:rgba(99,102,241,.38);
}
.pBtn-secondary:hover{background:rgba(99,102,241,.24); border-color:rgba(99,102,241,.55)}
.pBtn-outline{background:transparent; border-color:var(--border-strong); color:var(--text)}
.pBtn-outline:hover{background:var(--hover); border-color:var(--border-accent)}
.pBtn-success{background:var(--green-dim); color:#b8f5dd; border-color:rgba(52,211,153,.42)}
.pBtn-success:hover{background:rgba(52,211,153,.24)}
.pBtn-danger:hover{background:rgba(251,113,133,.24)}
.pBtn-link{
  background:none; border-color:transparent; color:var(--accent);
  padding-left:4px; padding-right:4px;
}
.pBtn-link:hover{color:#8fe7fb; box-shadow:none; text-decoration:underline}

/* Nur-Symbol. Quadratisch, damit die Fläche fürs Antippen reicht (44 px auf
   dem Handy sind das Minimum, unter dem man daneben greift). */
.pBtn-icon{padding:0; width:38px; height:38px; flex:0 0 38px}
.pBtn-icon.pBtn-sm{width:32px; height:32px; flex-basis:32px}
.pBtn-icon.pBtn-lg{width:46px; height:46px; flex-basis:46px}
@media (pointer:coarse){
  .pBtn-icon{width:44px; height:44px; flex-basis:44px}
  /* Der kleine Knopf war 31 px hoch — am Rechner trifft eine Maus das
     genau, ein Daumen nicht. Auf Fingergeräten bekommt er die Mindesthöhe,
     ohne dass sich seine Schriftgröße oder sein Gewicht ändern. */
  .pBtn-sm{min-height:40px; padding-top:9px; padding-bottom:9px}
  .pBtn{min-height:42px}
}

/* Symbole im Knopf bewegen sich beim Überfahren minimal mit — 2 px, nicht
   mehr. Es soll auffallen, wenn man darauf achtet, und sonst nicht. */
.pBtn .pBtn-ic{
  display:inline-grid; place-items:center; line-height:1;
  transition:transform var(--d-fast) var(--ease-spring);
}
.pBtn:hover .pBtn-ic{transform:translateX(2px)}
.pBtn-icon:hover .pBtn-ic{transform:scale(1.12)}
/* Ein Symbol, das nach vorn zeigt, wandert nach rechts; eines, das zurück
   zeigt, nach links. Alles andere wäre gegen die Leserichtung. */
.pBtn .pBtn-ic[data-dir="back"]{}
.pBtn:hover .pBtn-ic[data-dir="back"]{transform:translateX(-2px)}
.pBtn:hover .pBtn-ic[data-dir="up"]{transform:translateY(-2px)}
.pBtn:hover .pBtn-ic[data-dir="none"]{transform:none}

/* Die Ladeanzeige lag bisher immer auf Weiß — auf hellen Knöpfen war sie
   damit unsichtbar. Sie erbt jetzt die Textfarbe des Knopfes. */
.pBtn.is-loading::after{
  border-color:currentColor transparent transparent currentColor;
  opacity:.75; color:inherit;
}
.pBtn.is-loading{color:transparent!important}
.pBtn-primary.is-loading::after{color:var(--text-inverse)}

/* Fehlgeschlagene Aktion: kurzes Schütteln statt einer Meldung, die man
   wegklicken muss. Ergänzt den Toast, ersetzt ihn nicht. */
.pBtn.is-err{animation:pFxShake .4s var(--ease) both}

/* ══════════════════════════════════════════════════════════════════════════
   8 · KURZHINWEISE (Tooltips)
   ─────────────────────────────────────────────────────────────────────────
   Nur-Symbol-Knöpfe sind schnell zu treffen und schwer zu verstehen. Bisher
   hing an ihnen `title`, und das hat drei Nachteile: es erscheint erst nach
   gut einer Sekunde, es sieht auf jedem Betriebssystem anders aus, und auf
   dem Handy erscheint es nie.

   `data-tip` löst das rein über CSS — kein Skript, kein zusätzlicher Knoten
   im Markup. Der Hinweis ist bewusst leicht verzögert: wer nur über die
   Leiste fährt, soll nicht von aufpoppenden Kästen verfolgt werden.

   aria-label bleibt trotzdem Pflicht. `data-tip` ist ein Bild, kein Text
   für den Screenreader.
   ══════════════════════════════════════════════════════════════════════════ */
[data-tip]{position:relative}
[data-tip]::after,[data-tip]::before{
  position:absolute; z-index:var(--z-toast); pointer-events:none;
  opacity:0; transform:translate(-50%,4px) scale(.96);
  transition:opacity var(--d-fast) var(--ease), transform var(--d-fast) var(--ease);
  transition-delay:0s;
}
[data-tip]::after{
  content:attr(data-tip);
  left:50%; top:calc(100% + 9px);
  padding:6px 10px; border-radius:var(--r-tile); white-space:nowrap;
  background:#12141f; border:1px solid var(--stroke-2); color:var(--text);
  font-size:var(--fs-2); font-weight:600; line-height:1.3;
  box-shadow:var(--sh-floating);
}
/* Das Dreieck: ein gedrehtes Quadrat, das zur Hälfte unter dem Kasten liegt. */
[data-tip]::before{
  content:''; left:50%; top:calc(100% + 4px);
  width:9px; height:9px; background:#12141f;
  border-left:1px solid var(--stroke-2); border-top:1px solid var(--stroke-2);
  transform:translate(-50%,4px) rotate(45deg);
}
[data-tip]:hover::after,[data-tip]:hover::before,
[data-tip]:focus-visible::after,[data-tip]:focus-visible::before{
  opacity:1; transition-delay:.42s;
}
[data-tip]:hover::after,[data-tip]:focus-visible::after{transform:translate(-50%,0) scale(1)}
[data-tip]:hover::before,[data-tip]:focus-visible::before{transform:translate(-50%,0) rotate(45deg)}

/* Am rechten Rand (Kopfzeile) würde ein mittig zentrierter Kasten aus dem
   Bild laufen — dort hängt er rechtsbündig. */
[data-tip][data-tip-align="right"]::after{left:auto; right:0; transform:translate(0,4px) scale(.96)}
[data-tip][data-tip-align="right"]:hover::after,
[data-tip][data-tip-align="right"]:focus-visible::after{transform:translate(0,0) scale(1)}
/* In der Seitenleiste (eingeklappt) steht er daneben statt darunter. */
[data-tip][data-tip-side="right"]::after{
  left:calc(100% + 12px); top:50%; transform:translate(-4px,-50%) scale(.96);
}
[data-tip][data-tip-side="right"]::before{
  left:calc(100% + 7px); top:50%;
  transform:translate(-4px,-50%) rotate(-45deg);
  border-left:1px solid var(--stroke-2); border-top:none; border-bottom:1px solid var(--stroke-2);
}
[data-tip][data-tip-side="right"]:hover::after,
[data-tip][data-tip-side="right"]:focus-visible::after{transform:translate(0,-50%) scale(1)}
[data-tip][data-tip-side="right"]:hover::before,
[data-tip][data-tip-side="right"]:focus-visible::before{transform:translate(0,-50%) rotate(-45deg)}

/* Auf Fingergeräten gibt es kein Überfahren — dort wäre der Hinweis nur ein
   Kasten, der beim Antippen aufblitzt und stört. */
@media (pointer:coarse){
  [data-tip]::after,[data-tip]::before{display:none}
}

/* ══════════════════════════════════════════════════════════════════════════
   9 · FORMULARE
   ─────────────────────────────────────────────────────────────────────────
   Ein Eingabefeld muss vier Dinge zeigen können: was hineingehört, wo man
   gerade steht, was schiefging und dass es geklappt hat. Der Fehlerfall
   bekommt deshalb nicht nur einen roten Rahmen, sondern auch ein Symbol und
   einen Satz — Farbe allein hilft niemandem, der Rot nicht von Grau
   unterscheiden kann.
   ══════════════════════════════════════════════════════════════════════════ */
.pField{display:flex; flex-direction:column; gap:6px; min-width:0}
.pField-label{
  font-size:var(--fs-2); font-weight:700; color:var(--text-secondary);
  display:flex; align-items:center; gap:6px;
}
.pField-label .req{color:var(--danger); font-weight:800}
.pField-hint{font-size:var(--fs-2); color:var(--text-muted); line-height:1.45}

.pInput,.pTextarea,.pSelect{
  width:100%; padding:11px 13px; border-radius:var(--r-input);
  background:var(--surface-0); border:1px solid var(--border);
  color:var(--text); font-family:var(--f-ui); font-size:var(--fs-4);
  transition:border-color var(--d-fast) var(--ease),
             box-shadow var(--d-fast) var(--ease),
             background-color var(--d-fast) var(--ease);
}
.pTextarea{min-height:96px; resize:vertical; line-height:1.5}
.pSelect{appearance:none; cursor:pointer; padding-right:34px;
  background-image:linear-gradient(45deg,transparent 50%,var(--text-muted) 50%),
                   linear-gradient(135deg,var(--text-muted) 50%,transparent 50%);
  background-position:calc(100% - 17px) 51%, calc(100% - 12px) 51%;
  background-size:5px 5px, 5px 5px; background-repeat:no-repeat}
.pInput::placeholder,.pTextarea::placeholder{color:var(--text-muted)}
.pInput:hover,.pTextarea:hover,.pSelect:hover{border-color:var(--border-strong)}
.pInput:focus,.pTextarea:focus,.pSelect:focus{
  outline:none; border-color:var(--border-accent); box-shadow:var(--ring);
  background:rgba(0,0,0,.34);
}
.pInput[disabled],.pTextarea[disabled],.pSelect[disabled]{
  opacity:.5; cursor:not-allowed; background:var(--surface-2);
}

/* Zustände am Feldrahmen. Sie liegen auf `.pField`, damit Label, Feld und
   Meldung gemeinsam umschalten. */
.pField.is-err .pInput,.pField.is-err .pTextarea,.pField.is-err .pSelect{
  border-color:rgba(251,113,133,.6); background:rgba(251,113,133,.06);
}
.pField.is-err .pInput:focus{box-shadow:0 0 0 3px rgba(251,113,133,.3)}
.pField.is-ok .pInput,.pField.is-ok .pTextarea,.pField.is-ok .pSelect{
  border-color:rgba(52,211,153,.5);
}
.pField-msg{
  display:none; align-items:flex-start; gap:6px;
  font-size:var(--fs-2); line-height:1.45;
  animation:pFxSlideDown var(--d-fast) var(--ease-out) both;
}
.pField-msg::before{flex:none; font-size:12px; line-height:1.35}
.pField.is-err .pField-msg{display:flex; color:#fecdd3}
.pField.is-err .pField-msg::before{content:'⚠'}
.pField.is-ok .pField-msg{display:flex; color:#a7f3d0}
.pField.is-ok .pField-msg::before{content:'✓'}
/* Ein Feld mit Fehler bekommt genau einen Wink, keine Dauerbewegung. */
.pField.is-err .pInput{animation:pFxShake .38s var(--ease) both}

.pFieldRow{display:grid; gap:var(--sp-3); grid-template-columns:repeat(auto-fit,minmax(200px,1fr))}

/* Schalter. Ein `<input type=checkbox>` darunter bleibt der Wahrheitsträger —
   das Sichtbare ist nur die Hülle, Tastatur und Screenreader sehen das echte
   Feld. */
.pSwitch{display:inline-flex; align-items:center; gap:10px; cursor:pointer}
/* Trägt eine Zeile beide Klassen (`pRow pSwitch`), gewinnt die Zeile: sie soll
   die Breite füllen, damit Beschriftung links und Schalter rechts steht. */
.pRow.pSwitch{display:flex; width:100%}
.pSwitch input{position:absolute; opacity:0; width:0; height:0}
.pSwitch-track{
  position:relative; width:42px; height:24px; border-radius:99px; flex:none;
  background:var(--surface-0); border:1px solid var(--border);
  transition:background-color var(--d-fast) var(--ease), border-color var(--d-fast) var(--ease);
}
.pSwitch-track::after{
  content:''; position:absolute; left:3px; top:50%; width:16px; height:16px;
  border-radius:50%; background:var(--text-muted);
  transform:translateY(-50%);
  transition:transform var(--d-normal) var(--ease-spring), background-color var(--d-fast) var(--ease);
}
.pSwitch input:checked + .pSwitch-track{background:var(--indigo-dim); border-color:rgba(99,102,241,.55)}
.pSwitch input:checked + .pSwitch-track::after{transform:translate(18px,-50%); background:var(--accent)}
.pSwitch input:focus-visible + .pSwitch-track{box-shadow:var(--ring)}
.pSwitch input[disabled] + .pSwitch-track{opacity:.45; cursor:not-allowed}

/* ══════════════════════════════════════════════════════════════════════════
   10 · FEHLER- UND LEERZUSTÄNDE
   ─────────────────────────────────────────────────────────────────────────
   „Keine Daten vorhanden" ist eine Sackgasse. Ein guter Leerzustand sagt,
   WARUM nichts da ist und WAS man tun kann. Ein guter Fehlerzustand bietet
   einen Weg zurück, statt den Nutzer mit einer Zeile Technik allein zu
   lassen — die Einzelheiten stehen aufklappbar darunter, für die, die sie
   melden wollen.
   ══════════════════════════════════════════════════════════════════════════ */
.pEmpty{animation:pFxPageIn var(--d-card) var(--ease-out) both}
.pEmpty p{color:var(--text-secondary); max-width:44ch; margin:0 auto; line-height:1.5}
.pEmpty .pBtn{margin-top:var(--sp-4)}

.pError{
  text-align:center; padding:var(--sp-6) var(--sp-4);
  border-radius:var(--r-card); border:1px solid rgba(251,113,133,.28);
  background:linear-gradient(160deg,rgba(251,113,133,.07),transparent 60%),var(--surface-2);
  animation:pFxPageIn var(--d-card) var(--ease-out) both;
}
/* Steht das Fehlerbild allein in einer Karte, hat es schon einen Rahmen —
   ein zweiter drumherum sähe nach Kasten-im-Kasten aus. */
.panel>.pError:only-child,.pCard>.pError:only-child{border:0; background:none}
.pError-ic{font-size:32px; margin-bottom:10px; opacity:.85}
.pError h3{font-family:var(--f-display); font-size:var(--fs-6); color:var(--text); margin-bottom:6px}
.pError p{color:var(--text-secondary); max-width:46ch; margin:0 auto var(--sp-4); line-height:1.5}
.pError details{margin-top:var(--sp-4); text-align:left; max-width:52ch; margin-inline:auto}
.pError summary{
  cursor:pointer; font-size:var(--fs-2); color:var(--text-muted);
  list-style:none; display:inline-flex; align-items:center; gap:6px;
}
.pError summary::-webkit-details-marker{display:none}
.pError summary::before{content:'▸'; transition:transform var(--d-fast) var(--ease)}
.pError details[open] summary::before{transform:rotate(90deg)}
.pError pre{
  margin-top:8px; padding:10px 12px; border-radius:var(--r-tile);
  background:var(--surface-0); border:1px solid var(--border);
  font-family:var(--f-mono); font-size:var(--fs-1); color:var(--text-muted);
  white-space:pre-wrap; word-break:break-word; max-height:180px; overflow:auto;
}

/* ══════════════════════════════════════════════════════════════════════════
   11 · KARTEN — nach Aufgabe unterschieden
   ─────────────────────────────────────────────────────────────────────────
   Wenn jede Karte gleich aussieht, muss man jede lesen. Die Varianten hier
   unterscheiden sich in genau dem, was ihre Aufgabe verlangt — und NUR
   interaktive Karten reagieren auf Überfahren. Eine Karte, die sich hebt,
   ohne dass ein Klick etwas tut, ist ein gebrochenes Versprechen.
   ══════════════════════════════════════════════════════════════════════════ */
.pCard.is-interactive:hover{box-shadow:var(--sh-elevated)}
.pCard.is-flat{box-shadow:none; background:var(--surface-2)}

/* Kennzahl: eine große Zahl, ein kurzes Wort, sonst nichts. */
.pCard-stat{
  display:flex; flex-direction:column; gap:2px;
  padding:var(--sp-4); border-radius:var(--r-card);
  background:var(--surface-2); border:1px solid var(--border);
}
.pCard-stat .v{
  font-family:var(--f-display); font-size:var(--fs-7); font-weight:800;
  line-height:1.1; font-variant-numeric:tabular-nums;
}
.pCard-stat .l{font-size:var(--fs-2); color:var(--text-muted)}
.pCard-stat .d{font-size:var(--fs-2); font-weight:700; margin-top:4px}
.pCard-stat .d.up{color:var(--success)} .pCard-stat .d.down{color:var(--danger)}

/* Hinweis-/Warnkarte: farbige Kante links, sonst ruhig. Die Kante trägt die
   Bedeutung, ohne dass die ganze Fläche schreit. */
.pCard-note{
  position:relative; padding:var(--sp-4) var(--sp-4) var(--sp-4) calc(var(--sp-4) + 4px);
  border-radius:var(--r-card); background:var(--surface-2);
  border:1px solid var(--border); overflow:hidden;
}
.pCard-note::before{
  content:''; position:absolute; left:0; top:0; bottom:0; width:3px;
  background:var(--nc,var(--primary));
}
.pCard-note.is-warn{--nc:var(--warning); border-color:rgba(251,191,36,.26)}
.pCard-note.is-err{--nc:var(--danger); border-color:rgba(251,113,133,.28)}
.pCard-note.is-ok{--nc:var(--success); border-color:rgba(52,211,153,.26)}
.pCard-note.is-info{--nc:var(--info); border-color:rgba(59,155,255,.26)}
.pCard-note h4{font-weight:800; font-size:var(--fs-4); margin-bottom:3px}
.pCard-note p{color:var(--text-secondary); font-size:var(--fs-3); line-height:1.5}

/* ══════════════════════════════════════════════════════════════════════════
   12 · AUFKLAPPMENÜS
   ══════════════════════════════════════════════════════════════════════════ */
.pMenu{
  position:absolute; z-index:var(--z-drawer); min-width:196px;
  padding:6px; border-radius:var(--r-menu);
  background:#101320; border:1px solid var(--stroke-2);
  box-shadow:var(--sh-floating);
  transform-origin:top right;
  animation:pFxMenuIn var(--d-normal) var(--ease-out) both;
}
.pMenu[hidden]{display:none}
.pMenu-i{
  display:flex; align-items:center; gap:10px; width:100%;
  padding:9px 11px; border-radius:var(--r-tile); border:0; background:none;
  color:var(--text-secondary); font-size:var(--fs-3); font-weight:600;
  text-align:left; cursor:pointer; text-decoration:none;
  transition:color var(--d-fast) var(--ease), background-color var(--d-fast) var(--ease);
}
.pMenu-i:hover,.pMenu-i:focus-visible{color:var(--text); background:var(--hover); outline:none}
.pMenu-i.is-danger{color:#fecdd3}
.pMenu-i.is-danger:hover{background:var(--red-dim)}
.pMenu-sep{height:1px; background:var(--border); margin:5px 4px}
@keyframes pFxMenuIn{
  from{opacity:0; transform:translateY(-6px) scale(.97)}
  to{opacity:1; transform:none}
}

/* ══════════════════════════════════════════════════════════════════════════
   13 · SCHIEBEREGLER
   ─────────────────────────────────────────────────────────────────────────
   Die Lautstärkeregler im Einstellungsblatt waren die letzte Stelle, an der
   noch das Betriebssystem gezeichnet hat: systemblaue Balken mitten in einer
   Eis-Indigo-Oberfläche. Der zurückgelegte Teil der Spur wird über einen
   Farbverlauf mit hartem Stopp gefüllt, dessen Position `panel.js` als
   `--p` setzt — das kommt ohne zweites Element aus.
   ══════════════════════════════════════════════════════════════════════════ */
.pRange{
  -webkit-appearance:none; appearance:none;
  width:100%; height:22px; background:none; cursor:pointer;
}
.pRange::-webkit-slider-runnable-track{
  height:6px; border-radius:99px;
  background:linear-gradient(90deg,var(--accent) 0 calc(var(--p,50%)),
                             rgba(255,255,255,.11) calc(var(--p,50%)) 100%);
}
.pRange::-moz-range-track{
  height:6px; border-radius:99px;
  background:linear-gradient(90deg,var(--accent) 0 calc(var(--p,50%)),
                             rgba(255,255,255,.11) calc(var(--p,50%)) 100%);
}
.pRange::-webkit-slider-thumb{
  -webkit-appearance:none; appearance:none;
  width:16px; height:16px; margin-top:-5px; border-radius:50%;
  background:var(--text); border:2px solid var(--bg);
  box-shadow:0 2px 8px -2px rgba(0,0,0,.8), 0 0 0 1px rgba(255,255,255,.22);
  transition:transform var(--d-fast) var(--ease-spring),
             box-shadow var(--d-fast) var(--ease);
}
.pRange::-moz-range-thumb{
  width:16px; height:16px; border-radius:50%;
  background:var(--text); border:2px solid var(--bg);
  box-shadow:0 2px 8px -2px rgba(0,0,0,.8), 0 0 0 1px rgba(255,255,255,.22);
}
.pRange:hover::-webkit-slider-thumb{transform:scale(1.15)}
.pRange:active::-webkit-slider-thumb{transform:scale(1.05)}
.pRange:focus-visible{outline:none}
.pRange:focus-visible::-webkit-slider-thumb{box-shadow:var(--ring)}
.pRange:focus-visible::-moz-range-thumb{box-shadow:var(--ring)}
