/* Chat Tower Companion -- pages/dashboard.css
 * P6-Kette Glied 6/8 (web-241). Reine Verschiebung aus web/public/style.css.
 * Scope: die Dashboard-HUELLE selbst (Tab-Leiste/Header/Konto-Pillen-
 * Kompaktierung, geteilt mit /u/:login ueber .profile-page-Co-Selektoren),
 * web-085 /dashboard-Mobile-Fallback, ui-318 Dashboard/Setup-Mobil-Layout
 * (inkl. der Karten-Tile-Mobil-Skalierung -- physisch hier, nicht in
 * collection.css, weil das Block-eigene Kommentarnetz [Sub-Nav/Header/Card-
 * Scaling] durchgehend zusammenhaengt, Kommentar-Fragmentierungs-Risiko
 * hoeher als der Ertrag einer Trennung, Muster web-240 Kern-Entscheidung),
 * ui-148 Setup-Walkthrough-Coachmark, sowie der generische
 * .embed-surface.ct-embed-mount-Basisadapter (ui-402, aus dem sonst
 * techtree-lastigen Setup-Fragment-Mount-Block herausgezogen, weil er
 * NICHT tab-spezifisch ist).
 *
 * KASKADEN-LOCK (kritisch, s. web-241-Ticket Cross-File-Sweep +
 * check-order2.mjs-Verifikation): MUSS NACH pages/{entry,collection,build,
 * highscore,achievements}.css UND VOR pages/{shared-mobile,glossar}.css
 * laden -- mehrere hier definierte Overrides (.cs-inv-grid/.cs-socket-grid
 * 2-Spalten-Fix, .achievement-claim-btn/.cs-sort-btn/.account-delete-btn
 * 44px-Touch-Fix, .cs-subtoggle-btn) MUESSEN spaetere Datei-Position als die
 * jeweilige Basisregel behalten (gleiche Spezifitaet, sonst kippt die
 * Kaskade und die urspruengliche Mobile-Fallback-Reparatur wird lautlos
 * inaktiv). */


/* ===== Dashboard ===== */

.dashboard { padding: 0.5em 0; }
/* .dash-header gap/padding-bottom: IST 1em=15px vs. --sp-l-max 16px —
   bewusste Skalen-Angleichung (ui-265) */
.dash-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-l);
  padding-bottom: var(--sp-l);
  border-bottom: 1px solid var(--line);
}
.dash-id { display: flex; flex-direction: column; }
/* ui-265: Header-Familie auf fluide Type-/Space-Skala (DL-5 Proof-Surface A) */
.dash-name { font-size: var(--fs-h2); font-weight: 600; color: var(--text); }
/* .dash-handle: IST 0.9em=13.5px vs. --fs-small-max 12.75px — bewusste Skalen-Angleichung (ui-265) */
.dash-handle { color: var(--muted); font-size: var(--fs-small); }

.dash-stats { display: flex; gap: var(--sp-m); flex-wrap: wrap; }
@media (prefers-reduced-motion: no-preference) {
  .tab-badge--sp:not(.tab-badge--hidden) {
    animation: tab-badge-pulse 1.4s ease-in-out infinite;
  }
  /* ui-381: identischer Puls-Rhythmus, orangene Welle (--cur-draw, ui-623 —
     vormals cyanfarben; eigene Keyframes, weil die box-shadow-Farbe im
     Keyframe hartkodiert ist). */
  .tab-badge--draw:not(.tab-badge--hidden) {
    animation: tab-badge-pulse-draw 1.4s ease-in-out infinite;
  }
  /* ui-378: identischer Puls-Rhythmus, goldene Welle (◈-Claimable-Badge). */
  .tab-badge--ach:not(.tab-badge--hidden) {
    animation: tab-badge-pulse-ach 1.4s ease-in-out infinite;
  }
  /* ui-585 (AK0): identischer Puls-Rhythmus, orangene Welle (🛒-Shop-Badge). */
  .tab-badge--shop:not(.tab-badge--hidden) {
    animation: tab-badge-pulse-shop 1.4s ease-in-out infinite;
  }
}
/* ui-214 Breakpoint-Kanon -- volle Doku in pages/shared-mobile.css. */
@media (max-width: 600px) {
  .dash-header { flex-direction: column; align-items: flex-start; }
}

.embed-surface.ct-embed-mount {
  min-height: 0;
  height: auto;
  flex: 1 1 auto;
  width: 100%;
  box-sizing: border-box;
  padding: 8px 8px 0;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  overflow-x: hidden;
}
/* =====================================================================
   web-085: /dashboard Mobile-Fallback
   Breakpoints: ≤600px (Phablets/kleine Tablets + Phones) und ≤480px (Phones).
   Bestehende ≤600/560/1100px-Blöcke oben werden NICHT umgeschrieben — nur
   Dashboard-spezifische Ergänzungen, die dort noch fehlen.
   Desktop (≥768px) byte-gleich — alle Regeln hier sind in Mobile-Blöcken.
   ===================================================================== */

/* --- ≤600px: Tab-Strip als horizontaler Scroll-Strip (Design-Lock DL-A) ---
   flex-wrap:wrap → nowrap hält alle 6 Tabs (ui-368: + Profil) in einer Reihe
   (kein 2-Zeilen-Wrap).
   overflow-x:auto + -webkit-overflow-scrolling:touch = nativer Momentum-Scroll
   auf iOS. Scrollbar dezent ausgeblendet (Chrome/Firefox/Safari jeweils).
   .ref-cluster ausblenden: passt nicht in die Scroll-Strip-Reihe (würde als letztes
   Item hinter allen Tabs verschwinden, nie erreichbar ohne weit zu scrollen). */
@media (max-width: 600px) {
  .dash-tabs {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;       /* Firefox */
    -ms-overflow-style: none;    /* IE/Edge */
    /* Genug Platz damit der aktive Tab den Bottom-Border zeigt */
    padding-bottom: 0;
  }
  .dash-tabs::-webkit-scrollbar { display: none; } /* Chrome/Safari */

  /* Touch-Target ≥44px (Design-Lock DL-C): Padding erhöhen.
     Horizontal-Padding leicht reduziert damit mehr Tabs sichtbar bleiben. */
  .dash-tab {
    padding: 0.9em 1em;
    min-height: 44px;
    flex-shrink: 0; /* Tabs dürfen nicht zusammengequetscht werden */
    white-space: nowrap;
  }

  /* =====================================================================
     ui-586 (B2 · Icon-only + Aktiv-Label, Fifty-Entscheid 2026-09-03):
     8 Tabs (Tower/Sockel/Tech/Erfolge/Rang/Einladen/Shop/Profil) brauchten
     bei 360px sichtbarer Strip-Breite ~885px Inhalt — Rang/Einladen/Shop/
     Profil lagen komplett jenseits der Kante, kein Scroll-Hinweis
     (ui-582-Review-Alt-Problem). Prototyp-Optionen
     .claude/sprint/prototypes/ui-586-mobile-tab-leiste.html (IST/A/B/B2/C/D),
     Fifty waehlte B2.
     Layout-Kern (DL-2): ALLE Tabs teilen sich den Strip gleichmaessig
     (`flex:1 1 0` + `min-width:0` erlaubt Schrumpfen unter den eigenen
     Content — der aktive Tab bekommt weiter unten `flex:0 0 auto` zurueck
     und waechst auf seine Content-Breite); inaktive Tabs zeigen NUR das
     Icon, das Label wird per Clip-Pattern versteckt (bleibt im DOM,
     Accessible Name bleibt fuer Screenreader). `position:relative`
     verankert die Eck-Marker-Badges (Sticky-Block weiter unten in dieser
     Datei) an ihrem EIGENEN Tab statt am (ebenfalls positionierten,
     `position:sticky`) `.dash-tabs`-Strip.
     DL-6: bewusst KEIN `transition` auf flex/padding/gap hier — die
     geerbte `.dash-tab`-Transition (components.css) deckt nur color/
     border-color ab; Flex-Uebergaenge ruckeln und sind reduced-motion-
     relevant, der Zustandswechsel beim Tab-Tap (dash-tabs.js
     `classList.toggle('active', …)`, unveraendert) soll instant sein.
     Scope (DL-1): NUR dieser Standalone-Sub-Nav-Scope — der Viewer-Setup-
     Strip (`#nav > .dash-tabs`, mobile.css) bleibt unangetastet (4-6 Tabs,
     bereits Icon-ueber-Label seit ui-412, kein Platzproblem).
     ===================================================================== */
  .dashboard .dash-tab,
  .profile-page .dash-tab {
    flex: 1 1 0;
    min-width: 0;
    /* DL-3-Feintuning: 0.9em 0.2em statt der 0.9em 1em oben — bei 8 Tabs im
       Icon-only-Layout bleibt sonst zu wenig Breite fuer die 40px-Vorgabe
       (Prototyp-Ausgangswert, unveraendert aus Variante B/B2). */
    padding: 0.9em 0.2em;
    justify-content: center;
    gap: 0;
    position: relative;
  }
  .dashboard .dash-tab .tab-icon,
  .profile-page .dash-tab .tab-icon {
    /* Prototyp-Wert (~20px @ 15px-Companion-Root — companion/base.css setzt
       html/body auf font-size 15px) — DL-3 nennt diesen Wert explizit als
       Teil des Feintunings, obwohl er ggue. dem Rohprototyp unveraendert
       bleibt (Kontext fuer die Breiten-Rechnung). */
    font-size: 1.35em;
  }
  /* Inaktives Label: aus dem sichtbaren Fluss, bleibt im DOM (Muster
     mobile.css `@media (max-width:640px)` `#nav > .dash-tabs .dash-tab
     .tab-label--mobile` — identisches Clip-Pattern, DL-2). Der Tab zeigt
     bei inaktiv NUR das Icon. */
  .dashboard .dash-tab .tab-label--mobile,
  .profile-page .dash-tab .tab-label--mobile {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
  }
  /* Aktiver Tab (DL-2/DL-3): waechst auf seinen Content zurueck (Icon +
     Kurzlabel) statt sich die Strip-Breite gleichmaessig zu teilen. Padding
     0.55em statt der 0.7em aus dem Rohprototyp (Feintuning DL-3 — haelt die
     7 uebrigen Tabs ueber der 40px-Schwelle @390px, s. Ticket ## Notes fuer
     die Messreihe). */
  .dashboard .dash-tab.active,
  .profile-page .dash-tab.active {
    flex: 0 0 auto;
    padding: 0.9em 0.55em;
    gap: 0.4em;
  }
  /* Aktiv-Label wieder sichtbar (DL-4: "darf nie ellipsiert werden" — durch
     `flex:0 0 auto` oben strukturell garantiert, der Tab waechst auf den
     Content statt ihn zu quetschen). */
  .dashboard .dash-tab.active .tab-label--mobile,
  .profile-page .dash-tab.active .tab-label--mobile {
    position: static;
    width: auto;
    height: auto;
    overflow: visible;
    clip: auto;
    font-size: 0.9em;
    white-space: nowrap;
  }

  /* Referenz-Cluster (Guide/Glossar-Links) im Scroll-Strip ausblenden.
     Auf Mobile via Hilfe-Seite oder separatem Link erreichbar. */
  .ref-cluster { display: none; }

  /* Inventar- + Sockel-Grid: beide auf 2 Spalten (ui-343, Fifty-Entscheid
     2026-07-16 — Lesbarkeit vor Dichte, kehrt ui-318s "Beide Grids 3 Karten
     breit"-Entscheid vom 2026-07-07 BEWUSST um, s. Ticket ## Design-Locks
     Lock 1: der 7,5–9px-Interna-Preis fuer 3 enge Spalten war nicht mehr
     akzeptabel). 2 Spalten = ~170px bei 390px + padding — Boden fuer die
     Internas-Schrift auf ≥11px (s. Shrink-Block weiter unten in diesem File)
     OHNE Info wegzulassen (Fifty-Entscheid "Option B", nicht "schrumpfen +
     ausblenden").
     ROOT-CAUSE (ui-318, unveraendert gueltig): .cs-socket-grid hatte HIER —
     anders als .cs-inv-grid — urspruenglich GAR KEINEN ≤600px-Override. Es
     blieb am unconditional `grid-template-columns: repeat(2, var(--col))`
     haengen; --col wird ueber eine 7-Spalten-Container-Query-Formel
     (urspruenglich fuer BEIDE Grids gemeinsam gedacht) berechnet, die bei
     schmaler iframe-Breite (~370px) auf ~30px Kartenbreite kollabiert (Icon
     54px in einer 30×40px aspect-ratio-Box) — exakt Fiftys "kaputte"
     Sockel-Kacheln. Der zusaetzlich vorhandene @media(max-width:1100px)-
     Fallback (setzt ebenfalls repeat(2,1fr) fuer Sockel) wird von der
     TEXTUELL SPAETER stehenden unconditional Deklaration uebersteuert
     (gleiche Spezifitaet, letzter Ursprung gewinnt) — der 1100px-Fix greift
     dadurch nie. Fix bleibt hier: beide Grids explizit + gemeinsam auf
     repeat(2,1fr) in diesem spaeter geladenen Mobile-Fallback-Block (gewinnt
     die Kaskade). */
  .cs-inv-grid,
  .cs-socket-grid {
    grid-template-columns: repeat(2, 1fr);
    gap: 8px;
  }

  /* cs-panel padding etwas knapper auf Phone damit die Karten-Grid atmen kann. */
  .cs-panel { padding: 14px; }

  /* Draw-Leiste: bei ≤600px Hint-Text ausgeblendet — spart Platz auf schmalem Viewport;
     Zähler + Button bleiben sichtbar. */
  .cs-draw-hint { display: none; }

  /* Build-Stepper: Schritt-Zeile — Wrap statt Quetschen (ui-390).
     Der fruehere display:grid-Override (28px|1fr|auto|28px, "Zahl|Select|
     Kosten|Del") beschrieb eine Row-Struktur, die es nicht mehr gibt: die
     heutige Row hat 5 Kinder (⋮⋮-Griff, Head=Badge+Select, "Level im Spiel"
     (game-158, vormals "Ziel-Level"), Kosten, Delete). Im 4-Spalten-Grid
     landete der Griff in der 28px-Zahl-Spalte und der Level-Block quetschte
     die 1fr-Head-Spalte auf 0px — das Select ueberdeckte den −-Button
     vollstaendig (24x13px Overlap @390px, Messbefund ui-390; Minus-Button
     war mobil nicht treffbar).
     Fix: Basis-Flex (flex-wrap:wrap existiert dort) wrappen lassen; head
     behaelt eine echte Mindestbreite statt flex-basis-0-Quetschung.
     Gleiche drei Regeln nochmal im (hover:none)/(pointer:coarse)-Block
     fuer Touch-Geraete BREITER 600px (Tablets). */
  .build-step-row { row-gap: 0.45em; }
  .build-step-head { min-width: min(100%, 220px); }
  .build-step-select { max-width: 100%; min-width: 0; }

  /* Build-EndState-Grid: 1 Spalte statt auto-fill(230px) */
  .build-es-grid {
    grid-template-columns: 1fr;
  }

  /* ui-475 (Design-Lock 8): Deeplink-Scroll-Ziel für "Template bearbeiten"
     (viewer.html, controls/template.js) — nur ≤600px relevant, weil NUR
     hier `.dash-tabs` sticky ist (top:0 im `.ct-embed-mount`-Setup-Screen
     des Viewers, s. dortiger `.dashboard .dash-tabs`-Kommentar weiter
     unten in dieser Datei). Formel 1:1 aus pages/techtree.css `.tt-tree-
     wrap` übernommen (~44px Tabs + Notch-Inset ≈ 52px). Desktop-Popin hat
     KEINE sticky Sub-Nav bei dieser Breite — dort ist der Wert irrelevant
     (`scrollIntoView` landet ohnehin frei). */
  .build-weapon-panel.is-active {
    scroll-margin-top: calc(env(safe-area-inset-top, 0px) + 52px);
  }

  /* Runs-Tab: Highscore-Tabelle kompakter */
  .hs-table { font-size: 0.88em; }
  .hs-table th, .hs-table td { padding: 0.42em 0.6em; }

  /* ui-214: Touch-Target-Audit — Controls auf ≥44×44px (WCAG 2.5.5 / Apple HIG).
     Sort-Buttons: Basis min-height 34px → 44px. */
  .cs-sort-btn {
    min-height: 44px;
    padding: 10px 14px;
  }

  /* Achievement-Claim-Button: Basis min-height 36px → 44px. */
  .achievement-claim-btn {
    min-height: 44px;
    padding: 0.55em 1em;
  }

  /* Account-Löschen-Button: kein min-height in Basis → 44px. */
  .account-delete-btn {
    min-height: 44px;
    padding: 10px 16px;
  }

  /* .weapon-card (Waffen-Equip-Button): bereits ≥44px — kein Override nötig.
     Ist ein <button> mit padding:0.8em top+bottom (≈25.6px @ 16px base) +
     Glyph (1.5em≈24px) + Label (~16px) + Status (0.75em≈12px) + 2×gap(0.3em≈9.6px)
     → Gesamt-Tap-Höhe ≈ 87px, sicher über dem 44px-Limit. */
}

/* --- ≤480px: Phone-Stufe (DL-E: kein h-Scroll) --- */
@media (max-width: 480px) {
  /* Play-Now-Entry (ui-263): .entry füllt die Breite.
     .hero-title font-size: 2em kommt schon aus dem ≤600px-Block in
     pages/entry.css — hier kein Duplikat nötig. Bei ≤374px senkt ui-401
     weiter auf 1.35em (dort ebenfalls, Block am Datei-Ende). */
  .entry { max-width: 100%; }

  /* ui-343 (vormals ui-318): Inventar- + Sockel-Grid bleiben bei ≤480px auf
     2 Spalten — der alte 3→2-Downgrade (galt nur fuer .cs-inv-grid) bleibt
     entfernt. Fifty-Entscheid "2 Spalten, Lesbarkeit vor Dichte" (ui-343,
     2026-07-16) gilt durchgaengig ueber den gesamten ≤600px-Mobile-Bereich,
     nicht nur bis 480px — kein zusaetzlicher Override in diesem Block noetig
     (beide Grids erben repeat(2,1fr) aus dem ≤600px-Block oben, s. dortiger
     Root-Cause-Kommentar bei .cs-socket-grid). Die "volle Karten-Info"-
     Absicht von ui-318 (Icon/Name/Bonus/Sublabel/Tier-Badge/Anzahl/
     Klick-Hint, nichts wird weggelassen) bleibt erfuellt — nur die
     Spaltenzahl-Praemisse (3 statt 2) faellt, s. Ticket ## Design-Locks
     Lock 1. */

  /* Draw-Leiste: bei sehr schmalem Viewport Stack-Layout statt Row.
     Zähler oben, Button + Result unten. ui-487: die vormalige Mobile-
     Ersparnis (min-height 44px statt der 180px-Reserve VOR dem ersten Draw)
     ist entfallen — .draw-result ist seit ui-487 ein FESTER Slot mit
     width/height (288x180px, collection.css), weil die gestapelten Karten
     seit dem Stapel-Feature absolut positioniert sind und dem Element sonst
     GAR KEINE eigene Groesse mehr liefern (position:absolute-Kinder tragen
     nicht zur Groesse eines auto-groessen Elternelements bei) — ohne eine
     hier erzwungene kleinere Hoehe wuerden 180px-hohe Karten aus einer
     44px-Box herausragen und den Panel-Inhalt darunter ueberlappen. Bleibt
     hier NUR die horizontale Zentrierung (margin-left:0/align-self:center,
     Basis nutzt margin-left:auto fuer die Row-Variante).
     ui-598 (20 statt 5 Karten): +overflow:hidden +Kanten-Maske. Anders als
     Desktop (collection.css .draw-result-Kommentar: dort darf der Stapel
     frei ueber den 288px-Slot hinaus in den Panel-Hintergrund auslaufen,
     reichlich Platz) hat ein ≤480px-Viewport KEINEN sicheren Ueberlauf-Raum
     -- ungeclippt liefe der Stapel bis an/ueber die linke Bildschirmkante
     (live per CDP-Screenshot bei 390px verifiziert, Ticket `## Notes`).
     overflow:hidden schneidet den Rest sauber am Slot-Rand ab; die
     linksseitige mask-image-Gradiente macht daraus ein WEICHES Verblassen
     statt eines harten Kanten-Cuts (Fifty-Vorgabe "weich im Hintergrund
     verschwinden" gilt fuers Prinzip, nicht nur fuer den Exit-Pfad,
     s. draw-stack.js/app.js). */
  .cs-draw-bar {
    flex-direction: column;
    align-items: stretch;
    gap: 12px;
    padding: 12px 14px;
  }
  .cs-draw-counter { justify-content: center; }
  .draw-btn { width: 100%; justify-content: center; }
  .draw-result {
    margin-left: 0;
    align-self: center;
    overflow: hidden;
    mask-image: linear-gradient(to right, transparent, black 32px);
    -webkit-mask-image: linear-gradient(to right, transparent, black 32px);
  }

  /* Header-Pills: wrappen und kleiner werden */
  .dash-stats { gap: 0.5em; }
  .stat-pill { min-width: 4em; padding: 0.4em 0.7em; }
  .stat-num { font-size: 1.15em; }

  /* Site-Header: Nav kompakter — bewusst site-weit (alle Seiten profitieren von
     kompaktem Header auf Phone; kein /dashboard-Scope nötig). */
  .site-header { padding: 0.6em 1em; }
  .site-title { font-size: 0.95em; }
  .nav-user { display: none; } /* Benutzername ausblenden — spart Platz, Login-Zustand klar */

  /* ui-215: .hs-table display:block-Hack entfernt — durch .hs-scroll-Wrapper ersetzt.
     .hexa-table bleibt (Dashboard-Surface, nicht dieses Ticket). */
  .hexa-table {
    display: block;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
  }

  /* Build-Stepper: noch kompakter */
  .build-stage { padding: 0.8em 0.7em; gap: 0.6em; }
  .build-dot { width: 24px; height: 24px; font-size: var(--fs-caption); } /* ui-505 (M3): 0.72em (~10.8px, 0.72em-Treppe) -> --fs-caption (K1) — traegt Schritt-Nummer/Hexa-Icon */
  .build-spine { flex: 0 0 24px; }
  .build-spine-line { top: 24px; }

  /* Build-EndState-Grid: vollbreit, enge Zellen */
  .build-es-cell { padding: 0.35em 0.5em; }
  .build-es-bar { flex: 0 0 40px; }
  .build-es-v { min-width: 48px; } /* Base 60px → 48px: spart Platz auf 390px-Viewport */

  /* ui-318 (Zahl seit ui-343 auf 2 aktualisiert): der alte Kommentar hier
     ("Sockel-Grid bleibt 2-spaltig — kein Override nötig") war stale/falsch —
     s. Root-Cause-Kommentar bei .cs-socket-grid im ≤600px-Block oben. Kein
     Override HIER nötig, weil beide Grids ihre 2-Spalten-Regel (ui-343)
     bereits aus dem ≤600px-Block erben, nicht weil das Sockel-Grid je
     "bereits passte". */

  /* ui-214: Touch-Target-Audit — Phone-spezifische Controls.
     .cs-card-remove (✕): visuell 18×18px → auf ≥44×44px via min-width/min-height.
     ui-244: .cs-socket-box.is-filled hat kein overflow:hidden → Button kann wachsen
     ohne zu klemmen. Opacity-Reveal via .cs-socket-box.is-filled:hover. */
  .cs-card-remove {
    min-width: 44px;
    min-height: 44px;
    bottom: 0;
    left: 0;
    border-radius: 0 0 0 10px; /* Ecke passend zum Karten-Box-Radius */
  }

  /* Nav-Logout: Basis ~20px hoch → 44px min-height auf Phone. */
  .nav-logout {
    min-height: 44px;
    display: inline-flex;
    align-items: center;
  }
}

/* =====================================================================
   ui-318: Dashboard/Setup responsives Mobil-Layout (≤600px)
   Fifty-Anlass: Setup-Popin auf dem Handy (das /dashboard?embed=1-iframe aus
   ui-314) zeigte ein kaputtes Sockel-Grid + einen endlosen Seiten-Scroll —
   das volle Desktop-Dashboard einfach in Handy-Breite gequetscht. Kern des
   ui-318-Umbaus damals: die dash-tabs wurden bei ≤600px zu einer fixed
   Bottom-Nav + kompakter Kontext-Pillen-Strip + skalierte Karten-Tiles.

   ui-389 (Rueckbau des Bottom-Nav-Teils): die App-weite Bottom-Position
   gehoert jetzt der globalen App-Bottom-Nav (.app-botnav, layout.js —
   4 App-Ziele Spielen/Hexa/Rang/Profil auf ALLEN Voll-Layout-Surfaces,
   eigener Block am Datei-Ende). Die dash-tabs (Waffe/Sockel/Tech-Tree/
   Achievements/Highscore/Profil, Quelle views/dash-nav.js) sind Dashboard-
   INHALT, keine App-Ziele (Architektur-Lock ui-389) — sie rendern seither
   als Sticky-TOP-Sub-Nav-Strip (Basis: der generische ≤600px-Scroll-Strip-
   Block weiter oben, web-085): position:sticky unter Notch-Inset, quer-
   scrollend, Kurzlabels + Badges bleiben (ui-318-Entscheid weitergefuehrt,
   Prototyp ui-389.html). Der fruehere ui-318-Regelsatz (Flex-`order`-
   Umsortierung, position:fixed;bottom:0, vertikales Icon-ueber-Label-
   Layout, ::before-Top-Marker, Badge-Ecken-Position, Content-padding-
   bottom-Reservierung) ist ersatzlos zurueckgebaut.

   Scope (unveraendert ui-318): responsiver Breakpoint auf der Dashboard-
   Seite selbst, kein zweiter embed-only-Struktur-Pfad — der embed-Setup-
   iframe (ui-314) nutzt denselben Breakpoint automatisch. Der Kontext-
   Pillen-Strip + die Karten-Tile-Skalierung (unten) bleiben unveraendert
   ui-318/ui-343-Stand.

   Header-Fixierung nur im Embed-Pfad (.embed-surface): dort gibt es keinen
   konkurrierenden site-header. Im Standalone-Mobile-Pfad (.site-main, mit
   site-header voran) bleibt .dash-header im normalen Fluss. Die Sub-Nav
   klebt im Embed-Pfad UNTER dem fixed Header (top-Offset, s. Regel unten) —
   ui-402: NUR NOCH, wenn ein .dash-header ueberhaupt im Markup steht
   (`:has()`-Gate an der Regel selbst). Die Setup-Surface im Viewer
   (.ct-embed-mount) unterdrueckt .dash-header komplett (DL-3, dashboard.js
   embed-Zweig) — dort gibt es nichts mehr zu unterklettern, der Offset
   waere sonst 84px verschenkte Feldhoehe.
   ===================================================================== */
@media (max-width: 600px) {
  /* --- Sub-Nav (ui-389): .dash-tabs als Sticky-Top-Strip ---
     Basis ist der generische ≤600px-Scroll-Strip-Block weiter oben (web-085:
     flex-wrap:nowrap, overflow-x:auto, Scrollbar aus, Touch-Targets ≥44px) —
     hier kommt nur das Sticky-Top-Verhalten + Full-Bleed dazu. Der fruehere
     ui-318-Bottom-Nav-Regelsatz (order/fixed/vertikales Layout/::before-
     Marker/Badge-Ecken/Content-Reservierung) ist zurueckgebaut (ui-389, s.
     Block-Kommentar oben) — Aktiv-Indikator ist damit wieder der Basis-
     border-bottom (2px cyan, .dash-tab.active), Badges sassen bis ui-586
     wieder inline neben dem Label (Basis-Regeln .tab-badge--*) — seit
     ui-586 (B2) gilt das nur noch fuer den AKTIVEN Tab, inaktive Tabs
     zeigen den Badge als Eck-Marker (s. eigener Regelblock weiter unten).
     SCOPE (Finisher-Lektion ui-318, unveraendert gueltig): .dash-tabs/
     .dash-tab werden AUCH vom styleguide.js-Demo-Block (kein .dashboard/
     .profile-page-Wrapper) verwendet — alle Regeln hier bleiben deshalb
     unter `.dashboard`/`.profile-page` gescoped (ui-368: die geteilte
     Leiste rendert auch auf /u/:login), sonst risse position:sticky den
     Styleguide-Demo-Strip aus dem Textfluss. `.dashboard` selbst ist NUR
     in dashboard.js verwendet — bleibt bewusst ungescoped als Wurzel-
     Selektor. (ui-371-Scope-Notiz zum Pillen-Strip s. Strip-Block unten.) */
  .dashboard .dash-tabs,
  .profile-page .dash-tabs {
    position: sticky;
    /* ui-388 (viewport-fit=cover): top:0 wuerde beim Kleben in die Notch-/
       Statusbar-Zone rutschen (sticky ankert am Viewport, ignoriert das
       Body-Inset-Padding) — Muster #pwa-install-bar (layout.js). Desktop/
       Nicht-Notch: Inset 0 → wie top:0. */
    top: env(safe-area-inset-top, 0px);
    z-index: 30;
    /* Full-Bleed ueber das .site-main-Padding (1em @≤600px, s. Breakpoint-
       Kanon-Block) — sonst liefe scrollender Content sichtbar an den offenen
       Flanken des klebenden Strips vorbei. margin-top 0 (Basis 1.5em): der
       Strip schliesst direkt an den Kontext-Pillen-Strip an (Prototyp
       ui-389.html .subnav). Embed-Gutter (18px) s. Override unten.
       ui-586 (DL-3, B2-Feintuning): padding-left/-right auf 0.5em VERKLEINERT
       (vormals 1em, deckungsgleich mit der oben stillgelegten .site-main-
       Kante) — bei 8 Tabs im Icon+Aktiv-Label-Layout gewinnt das die
       Millimeter, die die inaktiven Tabs @390px ueber die 40px-Design-Lock-
       Schwelle heben (s. Ticket ## Notes fuer die Messreihe). Die
       margin:-1em-Full-Bleed-Kante selbst bleibt unveraendert — der Strip
       bleedet jetzt 0.5em WEITER als sein eigener Voll-Bleed-Ausgleich,
       bewusst (Ticket-Entscheid, kein Bug). */
    margin: 0 -1em 1em;
    padding-left: 0.5em;
    padding-right: 0.5em;
    /* Deckender Hintergrund — Content scrollt sonst sichtbar durch den
       klebenden Strip (Prototyp ui-389.html: var(--bg)). */
    background: var(--bg);
  }
  /* Kurzlabel-Umschaltung (ui-318-Entscheid, in ui-389 fuer den Top-Strip
     WEITERGEFUEHRT — Prototyp ui-389.html zeigt die Kurzformen „Erfolge"/
     „Rang"; weniger Scroll-Distanz im Quer-Strip. s. Basis-Kommentar bei
     .tab-label--mobile weiter oben): jeder Tab zeigt die KURZE Form statt
     der vollen Desktop-Top-Strip-Beschriftung. */
  .dashboard .dash-tab .tab-label--full,
  .profile-page .dash-tab .tab-label--full { display: none; }
  .dashboard .dash-tab .tab-label--mobile,
  .profile-page .dash-tab .tab-label--mobile { display: inline; }

  /* ui-586 (DL-5): Badges bei inaktiven Tabs als Eck-Marker (kein Platz mehr
     neben dem geclippten Icon-only-Label, s. Layout-Kern-Block weiter oben
     in dieser Datei) — Muster #nav > .dash-tabs (mobile.css, Viewer-Setup-
     Strip), hier eigene top/right-Werte (DL-5: 4px/8%, nicht die dortigen
     4px/18% — die B2-Tabs sind mit Icon + ggf. Aktiv-Label breiter als die
     4-Spalten-Vertikal-Slots des Viewer-Strips, ein enger Eck-Abstand
     wirkte dort abgeschnitten). .tab-badge--shop steht mit in der
     Selektor-Liste (DL-5) — die Pill-Optik selbst (Hintergrundfarbe/
     Padding) fehlt im Companion-CSS noch komplett (ui-585 AK0, Prototyp-
     Nebenfund s. Ticket ## Notes); hier geht es NUR um die Positionierung. */
  .dashboard .dash-tab .tab-badge--sp,
  .dashboard .dash-tab .tab-badge--draw,
  .dashboard .dash-tab .tab-badge--ach,
  .dashboard .dash-tab .tab-badge--shop,
  .profile-page .dash-tab .tab-badge--sp,
  .profile-page .dash-tab .tab-badge--draw,
  .profile-page .dash-tab .tab-badge--ach,
  .profile-page .dash-tab .tab-badge--shop {
    position: absolute;
    top: 4px;
    right: 8%;
    margin-left: 0;
  }
  /* Aktiver Tab: Badge bleibt inline neben dem Label wie am Desktop (DL-5) —
     hoehere Spezifitaet (zusaetzliche .active-Klasse) gewinnt gegen die
     Eck-Marker-Regel oben unabhaengig von der Datei-Reihenfolge. */
  .dashboard .dash-tab.active .tab-badge--sp,
  .dashboard .dash-tab.active .tab-badge--draw,
  .dashboard .dash-tab.active .tab-badge--ach,
  .dashboard .dash-tab.active .tab-badge--shop,
  .profile-page .dash-tab.active .tab-badge--sp,
  .profile-page .dash-tab.active .tab-badge--draw,
  .profile-page .dash-tab.active .tab-badge--ach,
  .profile-page .dash-tab.active .tab-badge--shop {
    position: static;
    margin-left: 0.35em;
  }

  /* Embed-Pfad, MIT .dash-header (ui-389-Ursprungsfall — heute: keiner der
     beiden .embed-surface-Konsumenten mehr, s. :has()-Gate unten): der
     .dash-header ist dort fixed-top (s. Embed-Abschnitt unten) — die
     Sub-Nav rueckt per margin-top aus dem Fluss-Start unter den Header
     (uebernimmt die fruehere .dash-body-padding-top-Reservierung) und klebt
     beim Scrollen mit demselben Offset. Hoehe via --embed-fixed-header-h
     (EINE Stelle): 84px GEMESSEN @375px (ui-389-CDP-Smoke) — der Kompakt-
     Header ist bei ≤600px 2-zeilig (die ungescopte .dash-header-column-Regel
     im Basis-Block stapelt Name-Zeile ueber Pillen-Zeile; beide Zeilen sind
     nowrap + quer-scrollend, brechen also nicht weiter um). Die historische
     52px-Schaetzung (ui-318, „Name/Handle-Zeile + Pillen-Zeile ≈ 44px +
     Puffer") war schon damals zu knapp — als reine .dash-body-padding-
     Reservierung fiel das nicht auf, unter den interaktiven Tabs wuerde es
     16px Verdeckung heissen. Gutter: .embed-surface-Padding ist 18px
     (nicht 1em). Gleiche Spezifitaet (0,2,0) wie die Regel oben → gewinnt
     per Datei-Position.
     ui-402: `:has(.dash-header)`-Gate ergaenzt — die Setup-Surface im
     Viewer (.ct-embed-mount) rendert NIE einen .dash-header (DL-3), ohne
     das Gate würde die Sub-Nav dort 84px leeren Chrome-Offset verschenken,
     obwohl nichts mehr zu unterklettern ist. Fällt bei einem künftigen
     embed-Konsumenten MIT Header automatisch wieder korrekt an (Praesenz-
     Check statt Namens-Annahme). */
  .embed-surface:has(.dash-header) .dash-tabs {
    --embed-fixed-header-h: 84px;
    top: var(--embed-fixed-header-h);
    margin-top: var(--embed-fixed-header-h);
    margin-left: -18px;
    margin-right: -18px;
    padding-left: 18px;
    padding-right: 18px;
  }
  /* ui-402: OHNE .dash-header (die Setup-Surface im Viewer) bleibt die
     Sub-Nav trotzdem sticky (Muster oben, .dashboard .dash-tabs) — jetzt
     einfach ohne Offset, sie klebt an der Mount-Oberkante statt unter
     einem Header. .ct-embed-mount hat bereits 8px eigenes Padding
     (style.css .ct-embed-mount-Block) — kein zusätzlicher Full-Bleed-
     Ausbruch wie beim 18px-Gutter-Fall oben nötig. */
  .embed-surface.ct-embed-mount .dash-tabs {
    top: 0;
    margin-top: 0;
    margin-left: 0;
    margin-right: 0;
    padding-left: 0;
    padding-right: 0;
  }

  /* Guide/Glossar/Patch-Notes-Referenz-Cluster war schon vor ui-318 bei ≤600px
     ausgeblendet (s. Block oben) — bleibt so (kein Platz im Quer-Scroll-Strip;
     die Doku-Ziele traegt mobil das „⋯"-Header-Menue, ui-389). */

  /* --- Kontext-Pillen-Strip: .dash-header kompakt + quer-scrollend ---
     ui-371: die Konto-Pillen-Leiste ist jetzt geteilt (dash-account-bar.js) und
     rendert auch auf /u/:login (eingeloggt, Wrapper .pf-account-bar). Die
     Kompakt-Strip-Familie bekommt deshalb zusätzlich den .pf-account-bar-Scope
     (Komma-Selektor an den bestehenden Regeln, EINE Werte-Quelle — exakt das
     ui-368-Muster der Nav-Familie oben). NUR die Pillen-Regeln (.dash-header/
     .dash-stats/.stat-pill/.stat-num/.stat-label) — die .dash-id-Regeln bleiben
     Dashboard-only (das Profil rendert keinen Viewer-Namen, der Identitäts-
     Platz gehört dem Subjekt-Hero). „Optisch identisch zum Dashboard-Header"
     (AK ui-371) heißt auf Mobile: dieselbe Kompakt-Optik wie der Mobile-
     Dashboard-Strip (volle Pillen fräßen hier wie dort die halbe Höhe —
     s. Ursprungs-Kommentar bei .dashboard .dash-id unten). */
  .dashboard .dash-header,
  .profile-page .pf-account-bar {
    flex-wrap: nowrap;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    gap: 0.5em;
    padding: 0.5em 0.7em;
    border-bottom: 1px solid var(--line);
    background: rgba(6, 8, 15, 0.92);
    backdrop-filter: blur(6px);
  }
  .dashboard .dash-header::-webkit-scrollbar,
  .profile-page .pf-account-bar::-webkit-scrollbar { display: none; }
  /* ui-371: das row/flex-start-Override gegen die ungescopte Column-Regel
     (≤600px-Basis-Block) + die flex-end-Desktop-Basis lebt BEIM Profil-Block
     (eigener ≤600px-Query direkt nach der Basis-Regel) — dort gewinnt es die
     Kaskade sicher (gleiche Spezifität, spätere Datei-Position; hier im
     ui-318-Block stünde es VOR der flex-end-Basis und verlöre). */
  /* Name/Handle-Block auf eine Zeile zusammenziehen — der volle Header
     (Name + Handle + 4 Stat-Pillen) frisst sonst zu viel Hoehe (Ticket-
     Anlass: „der volle Header frisst am Handy die halbe Hoehe"). */
  .dashboard .dash-id {
    flex: 0 0 auto;
    flex-direction: row;
    align-items: baseline;
    gap: 0.35em;
    white-space: nowrap;
  }
  .dashboard .dash-name { font-size: 0.95em; }
  .dashboard .dash-handle { font-size: 0.75em; }
  .dashboard .dash-stats,
  .profile-page .pf-account-bar .dash-stats {
    flex-wrap: nowrap;
    gap: 0.4em;
  }
  .dashboard .stat-pill,
  .profile-page .pf-account-bar .stat-pill {
    flex: 0 0 auto;
    flex-direction: row;
    gap: 0.3em;
    min-width: 0;
    padding: 0.3em 0.55em;
  }
  /* ui-371: .stat-num/.stat-label bewusst über den .pf-account-bar-WRAPPER
     gescoped (nicht .profile-page .stat-num) — die Hero-Stats des Subjekts
     (.heroes .stat-pill) behalten ihre volle Optik inkl. Label. */
  .dashboard .stat-num,
  .profile-page .pf-account-bar .stat-num { font-size: 0.85em; }
  .dashboard .stat-label,
  .profile-page .pf-account-bar .stat-label { display: none; } /* Icon + Zahl reichen im Kompakt-Strip; Label per glyph-tip weiterhin erreichbar */

  /* --- Embed-Pfad (Setup-Popin-iframe, ui-314): Header zusaetzlich fixed-top.
     Kein konkurrierender site-header hier (embed rendert ohne Nav-Chrome,
     layout.js). Standalone-Pfad (.site-main) behaelt den Header im normalen
     Fluss (site-header davor ist ebenfalls nicht fixed — kein Overlap-Risiko,
     aber auch kein zweiter fixed-Layer noetig).
     ui-389: die fruehere .embed-surface .dash-body{padding-top:52px}-
     Reservierung fuer diesen fixed Header ist entfallen — sie haengt jetzt
     als margin-top an der Sticky-Sub-Nav (.embed-surface .dash-tabs, s.
     Sub-Nav-Block oben), die im Fluss VOR dem Body steht.
     ui-416: Ausnahme direkt darunter — die Konto-Pillen-Leiste (.pf-account-bar)
     traegt zwar ebenfalls .dash-header (Optik-Wiederverwendung, ui-371), bekommt
     aber gezielt KEINE Fixierung (Gegenregel, s.u.). */
  .embed-surface .dash-header {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    z-index: 50;
  }

  /* ui-416: die Konto-Pillen-Leiste rendert seit diesem Fix auch im Embed-Pfad
     (eigenes Gate showAccountBar = hasNav, s. views/profile.js profileBody())
     — sie traegt zwar dieselbe .dash-header-Klasse wie oben (Optik-
     Wiederverwendung, ui-371), darf die Fixierung aber NICHT erben: die ist
     fuer den "echten" Dashboard-/Highscore-Kopf gedacht (kein konkurrierender
     site-header dort, s. Kommentar oben; seit ui-402 DL-3 rendert dort
     ohnehin kein aktueller .embed-surface-Konsument mehr einen .dash-header —
     dashboard.js/highscore.js unterdruecken ihn im Embed-Zweig komplett).
     Fixed-top wuerde die Leiste mobil an die Viewport-Oberkante kleben und
     GENAU den ui-407-DL-10-Fehler reproduzieren (App-Nav "Spielen"/"Hexa"
     verdeckt) — #viewer-app-nav (Mobile-Komposition, web/src/views/viewer-shell.js)
     ist selbst ein normaler Flex-Child im <body>-Fluss, kein positionierter Container, position:fixed
     haette also den ECHTEN Viewport als containing block, nicht nur die
     Flaeche (s. Ticket ui-416 "Die Falle"). Drei-Klassen-Selektor
     (Spezifitaet 0,3,0) schlaegt die Zwei-Klassen-Regel oben unabhaengig von
     der Datei-Reihenfolge (Muster .embed-surface.ct-embed-mount weiter oben
     in dieser Datei — Spezifitaet statt Reihenfolge). top/left/right/z-index
     sind bei position:static wirkungslos (CSS-Spec) — trotzdem explizit
     zurueckgesetzt, falls hier spaeter mal auf relative/sticky gewechselt
     wird (Absicherung, kein totes Offset-Erbe). */
  .embed-surface .dash-header.pf-account-bar {
    position: static;
    top: auto;
    left: auto;
    right: auto;
    z-index: auto;
  }

  /* --- Karten-Tile mobil-skaliert (Ticket-Punkt 5, ui-318): echtes
     .cs-card*-CSS aus style.css, NUR die Groessen fuer die urspruenglich
     3-Spalten-Kachel (~110px @ 375px, s. Prototyp-Rechnung im ui-318-Ticket)
     runterskaliert — VOLLE Info bleibt erhalten (Icon/Name/Bonus/Effekt/
     Sublabel/Tier-Badge/Anzahl/Klick-Hint), nichts wird weggelassen (Fifty-
     Lock „keine Vereinfachung"). Faktor ≈0.72 auf die visuell dominanten
     Werte (Icon 54→39px), sonst auf sinnvoll lesbare Werte gerundet statt
     stur multipliziert. Trifft NUR die generischen .cs-card-*-Basisklassen —
     is-group/is-weapon-exclusive/is-bosslure/is-hunt/is-pact haben eigene
     Farb-Overrides auf denselben Basisklassen (skalieren automatisch mit)
     bzw. eigene .cs-pact-*, .cs-bl-*, .cs-ht-*-Elemente mit eigenen fixen
     px-Werten.
     ui-343 (Fifty 2026-07-16, Lesbarkeit vor Dichte): die Kachel ist jetzt
     2 Spalten breit (~170px @ 390px statt ~110px, s. Root-Cause-Kommentar
     bei .cs-inv-grid/.cs-socket-grid oben) — Icon/Padding bleiben auf den
     ui-318-Werten (genug Breite auch ohne Anpassung, empirisch verifiziert:
     kein horizontaler Overflow bei 430/390/360px, s. Ticket ## Notes), aber
     die Text-Boeden der Internas werden zentral ueber zwei Custom
     Properties auf der Basis-`.cs-card`-Regel (Lock 2 — EINE Stelle statt
     15 Einzel-px-Overrides; gilt via Vererbung auch fuer is-pact/is-bosslure/
     is-hunt, die alle die `cs-card`-Klasse mittragen) von 6,5–10,5px auf
     ≥11px angehoben:
     `--card-min-fs` (allgemeiner Boden, 11px) und `--card-name-fs`
     (Kartenname/Effekt, etwas groesser, 12px). NICHT erfasst (bewusst
     out-of-scope, s. Ticket ## Design-Locks Tabelle): .cs-sub-info,
     .cs-click-hint, .cs-upgrade-cta sowie .cs-pact-name, cs-bl-Klassen,
     cs-ht-Klassen (Pakt-/Köder-/Jagd-Card-Namen + -Sub-Header) — nur die
     in der Tabelle gelisteten Klassen (Name/Effekt/Sublabel/Tier-Badge/
     Anzahl/Upgrade-Vorschau/Pakt-Mini-Labels) wurden angehoben.
     ui-503 (M1, 2026-08-21): die o.g. Ausnahmen NACHGEZOGEN — .cs-click-hint/
     .cs-upgrade-cta/.cs-bl-runglobal auf var(--card-min-fs), .cs-pact-name/
     .cs-bl-name/.cs-bl-effect/.cs-pact-blk-gly auf var(--card-name-fs) — deckt
     sich mit deren Desktop-12px-Geschwistern in components.css. Einzige
     verbleibende Ausnahme: .cs-sub-info — reines Deko-Element (K2-Whitelist,
     10px-Mikro-Boden statt 11px, s. Regel unten). cs-ht-Klassen waren bereits
     bei 11px (kein Handlungsbedarf, s. .cs-ht-name unten). ui-511: .cs-pact-name/
     .cs-pact-blk-gly existieren seither nicht mehr (Pakt-Front auf Cover-Layout
     umgebaut, components.css .cs-pact-cover-name/-kpi) — dieser Absatz bleibt
     als Verlauf stehen, pinnt aber nicht mehr den IST-Stand der Pakt-Klassen. */
  .cs-card {
    --card-min-fs: 11px;
    --card-name-fs: 12px;
  }
  .cs-card-surface {
    padding: 9px 7px 7px;
  }
  .cs-card-icon {
    font-size: 39px;
  }
  .cs-card-body {
    padding-right: 22px; /* Sublabel-Freiraum proportional zur kleineren Badge */
  }
  .cs-card-name {
    font-size: var(--card-name-fs);
  }
  .cs-card-bonus {
    font-size: 13px;
  }
  .cs-card-effect {
    font-size: var(--card-name-fs);
  }
  .cs-card-sub {
    font-size: var(--card-min-fs);
  }
  /* ui-503 (M1): mobiler Shrink (9x9px/6.5px) entfernt — die Basis-Regel
     (components.css .cs-sub-info) traegt seit ui-503 bereits den K2-Mikro-
     Boden (11x11px-Box/10px-Font); ein weiteres Herunterskalieren wuerde
     unter den 10px-Floor rutschen. Kontext (Drop-Chancen-Link) liegt nicht
     in der Kachel-Breitenklemme, kein Platzgrund fuer einen Mobile-Override. */
  .cs-card-tier-badge {
    font-size: var(--card-min-fs);
    bottom: 5px;
    right: 5px;
    padding: 3px 6px;
  }
  .cs-card-count {
    min-width: 20px;
    height: 20px;
    font-size: var(--card-min-fs);
    top: 4px;
    right: 4px;
  }
  .cs-upgrade-chevron {
    font-size: 17px;
    top: 2px;
    left: 5px;
  }
  .cs-click-hint {
    /* ui-503 (M1): 6.5px -> 11px-Boden (K1), ueber das bestehende lokale
       --card-min-fs-Token (nicht tokens.css — s. Ticket-Scope-Grenze). */
    font-size: var(--card-min-fs);
  }
  .cs-upgrade-cta {
    /* ui-503 (M1): 7.5px -> 11px-Boden (K1), ueber var(--card-min-fs) —
       Live-CDP-Fit-Check s. Ticket ## Notes. */
    font-size: var(--card-min-fs);
    margin-right: 26px;
  }
  .cs-card-touch-preview {
    font-size: var(--card-min-fs);
    padding: 4px 5px;
  }

  /* Köder-/Jagd-Karten (.is-bosslure/.is-hunt): eigene interne .cs-bl-*,
     .cs-ht-*-Header-Struktur (Icon links + Name daneben), NICHT Teil der
     generischen .cs-card-*-Basisklassen — braucht eigene Skalierung. FUND
     beim Headless-Verify (s. ui-318-Ticket ## Notes — historischer Befund an
     einer damaligen Pakt-Karte, deren Header-Struktur zu dem Zeitpunkt noch
     identisch war; ui-511 hat die Pakt-Front seither auf ein Cover-Layout
     umgebaut, s. Notiz am Blockende): Karten-Name ("Ansturm") lief bei der
     Basis-Icon-Groesse (28px) + Basis-Gap/Padding in der damaligen ~98px-3-
     Spalten-Kachel auf ~22px nutzbare Namen-Breite zusammen — TATSAECHLICH
     truncated (scrollWidth 37px > clientWidth 13px, per Live-Verify gemessen).
     Icon + Gap/Padding runterskaliert gab dem Namen wieder ausreichend
     Breite; diese Icon-/Gap-Werte bleiben unangetastet (ui-343 aendert nur
     Grid-Spalten + die in der Design-Locks-Tabelle gelisteten Text-Boeden,
     s. Kommentar oben bei .cs-card). Die Kachel ist seit ui-343 ohnehin
     breiter (~170px statt ~98px) — der urspruengliche Truncation-Risikofall
     besteht also mit noch mehr Puffer als damals gemessen. (Kommentar-
     Historie zu einem in dieser Zeile zwischenzeitlich vorhanden gewesenen
     Kommentar-Parser-Stolperstein s. ui-318-Ticket ## Notes „Kommentar-Bug"
     — bewusst NICHT hier ausformuliert, um den Stolperstein nicht per Text-
     Beschreibung erneut auszuloesen.)
     ui-511: die frühere `.cs-pact-*`-Mobile-Override-Gruppe (Header/Blocks/
     Reward-Split) ist mit dem Front-Redesign komplett entfernt — das neue
     Cover (components.css `.cs-pact-cover-*`/`.cs-pact-kpi`) nutzt feste
     px-Werte bzw. `var(--fs-caption)` statt der lokalen --card-min-fs/
     --card-name-fs-Tokens und braucht deshalb KEINEN eigenen @<=600-Block
     (62px-Aura/44px-Glyph passen in der ~170px-Mobile-Kachel, aspect-ratio
     3/4, live verifiziert — s. Ticket ## Notes). */

  .cs-bl-hdr {
    gap: 5px;
    padding: 6px 6px 0;
  }
  .cs-bl-icon {
    width: 20px;
    height: 20px;
  }
  .cs-bl-icon svg {
    width: 18px;
    height: 18px;
  }
  .cs-bl-name {
    /* ui-503 (M1): 9.5px -> var(--card-name-fs)=12px (K1) — deckt sich mit dem
       Desktop-Wert (components.css .cs-bl-name: 12px). */
    font-size: var(--card-name-fs);
  }
  .cs-bl-accent {
    margin: 5px 6px 0;
  }
  .cs-bl-body {
    gap: 5px;
    padding: 6px;
  }
  .cs-bl-effect {
    /* ui-503 (M1): 9.5px -> var(--card-name-fs)=12px (K1) — deckt sich mit dem
       Desktop-Wert (components.css .cs-bl-effect: 12px). */
    font-size: var(--card-name-fs);
  }
  .cs-bl-runglobal {
    /* ui-503 (M1): 7.5px -> var(--card-min-fs)=11px (K1) — deckt sich mit dem
       Desktop-Fix (components.css .cs-bl-runglobal: 11px). */
    font-size: var(--card-min-fs);
  }

  .cs-ht-hdr {
    gap: 6px;
  }
  .cs-ht-icon {
    width: 26px;
    height: 26px;
    flex: 0 0 26px;
  }
  .cs-ht-icon svg {
    width: 26px;
    height: 26px;
  }
  .cs-ht-name {
    font-size: 11px;
  }

  /* --- Sub-Umschalter "◇ Sockel ⇄ ◆ Inventar" (Ticket-Punkt 6) ---
     Pill-Toggle-Optik (Muster gespiegelt vom Prototyp ui-318.html
     .cs-subtoggle). Aktiv = var(--cyan) gefuellter Hintergrund (Kontrast
     genug fuer A11y — kein reiner Farbwechsel auf Text, sondern volle
     Flaechenfuellung + Textfarben-Invertierung).
     ui-628: die Markup-Stelle (views/dashboard.js) traegt daneben jetzt
     zusaetzlich `subnav subnav-seg`/`subnav-seg-btn` (Lock 1 "ein Muster") —
     die hoehere Spezifitaet der geteilten Regeln (components.css, (0,2,0))
     ueberschreibt Display/Rand/Aktiv-Fuellung HIER unten auf die
     Segment-Optik (Teal `#1b9aa8` statt var(--cyan)), OHNE dass diese
     Basisregeln selbst angefasst werden muessen (Grid-Aufteilung/Radius/
     Innenabstand bleiben hier trotzdem als Fallback stehen, falls die
     `.subnav*`-Klassen je fehlen sollten). */
  .cs-subtoggle {
    display: flex;
    background: var(--bg-2, rgba(13, 19, 34, 0.6));
    border: 1px solid var(--line-2);
    border-radius: 999px;
    padding: 3px;
    margin-bottom: 12px;
  }
  .cs-subtoggle-btn {
    flex: 1 1 0;
    min-height: 40px;
    border: none;
    background: none;
    color: var(--muted);
    font-family: inherit;
    font-size: 13px;
    font-weight: 600;
    border-radius: 999px;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 6px;
    transition: background-color 0.15s, color 0.15s;
  }
  .cs-subtoggle-btn.active {
    background: var(--cyan);
    color: var(--bg, #06080f);
  }

  /* Show/Hide der beiden .cs-layout-Sections ueber das stabile #cs-root[data-
     mobile-sub]-Attribut (NICHT ueber eine Klasse auf den Panels selbst — die
     werden bei jeder Sockel-Mutation per swapRegion/outerHTML ersetzt und
     wuerden eine eigene Sichtbarkeits-Klasse dabei verlieren; das Attribut auf
     #cs-root bleibt stabil ueber jeden Panel-Swap hinweg). Beide Sections
     bleiben immer im DOM (reine CSS-Anzeige-Schicht, kein Server-Re-Render
     noetig beim Umschalten). */
  #cs-root[data-mobile-sub="sockel"] .cs-inv-panel,
  #cs-root[data-mobile-sub="inventar"] .cs-socket-panel {
    display: none;
  }
  /* .cs-layout ist bei ≤600px bereits einspaltig (geerbt aus dem
     @media(max-width:1100px)-Block — 600<1100, beide Regeln matchen; kein
     zusaetzlicher Override hier noetig). Die Show/Hide-Regel oben sorgt
     dafuer, dass in dieser einen Spalte trotzdem immer nur EINE Section
     sichtbar ist. */
}

/* =====================================================================
 * Setup-Walkthrough-Layer (ui-148) — Dashboard-Coachmark (ui-539: unconditional,
 * vormals embed-only — läuft seither auch auf der standalone Mobile-Home-Seite).
 * Coachmark-Familie: Cyan-Box + Spotlight, wie ui-146/147 (ob-*-Prefix).
 * Nicht-blockierend: pointer-events:none auf Layer, ob-card ist auto.
 * ===================================================================== */
.ob-wt {
  position: fixed;
  inset: 0;
  z-index: 300;
  pointer-events: none;
}
.ob-wt.ob-wt--hidden { display: none; }

/* Spotlight: pulsierender Cyan-Glow auf dem Anker-Element */
.ob-spot {
  position: absolute;
  border-radius: 10px;
  box-shadow: 0 0 0 3px var(--cyan), 0 0 22px 4px rgba(56,225,255,.30);
  transition: all .2s ease;
  pointer-events: none;
  animation: ob-spot-pulse 1.6s ease-in-out infinite;
}
@keyframes ob-spot-pulse {
  0%,100% { box-shadow: 0 0 0 3px var(--cyan), 0 0 18px 2px rgba(56,225,255,.30); }
  50%      { box-shadow: 0 0 0 3px var(--cyan), 0 0 28px 8px rgba(56,225,255,.30); }
}

/* Coachmark-Karte */
.ob-card {
  position: absolute;
  width: min(320px, 86vw);
  pointer-events: auto;
  background: var(--bg2, #121826);
  border: 1px solid var(--cyan, #38e1ff);
  border-radius: var(--r-cont, 10px);
  box-shadow: 0 8px 30px rgba(0,0,0,.5);
  padding: 14px 16px;
  animation: ob-card-pop .22s ease-out;
}
@keyframes ob-card-pop {
  from { opacity: 0; transform: translateY(6px) scale(.98); }
  to   { opacity: 1; transform: none; }
}
.ob-kick {
  color: var(--cyan, #38e1ff);
  font-size: 11px;
  letter-spacing: .12em;
  text-transform: uppercase;
  font-weight: 700;
  display: block;
}
.ob-step {
  float: right;
  color: var(--muted, #6b7a96);
  font-size: 11px;
}
.ob-card .ob-title {
  margin: 6px 0 4px;
  font-size: 15px;
}
.ob-card .ob-body {
  margin: 6px 0 0;
  font-size: 13px;
  line-height: 1.5;
  color: var(--text, #e8eef7);
}
.ob-card .ob-body b    { color: var(--cyan, #38e1ff); }
.ob-card .ob-body .ob-m { color: var(--magenta, #ff5cf0); }
/* ui-535 (Befund 2, Box-Overflow-Fix): `flex-wrap: wrap` statt des
   impliziten `nowrap` — Back (bei Schritt 1 `visibility:hidden`, behält
   aber sein Layout-Feld) + CTA (Label-Länge variiert je Schritt, z.B. die
   vormalige lange "Zu den Achievements ›"-Sonder-CTA) + Skip (das
   deutsche "Überspringen" ist EIN zusammenhängendes Wort ohne
   Umbruchstelle, kann also nicht wie ein mehrteiliges Label intern
   schrumpfen) passten in der Summe ihrer Mindestbreiten nicht immer in die
   Karte (`width: min(320px, 86vw)` abzüglich Padding) — `.ob-card` selbst
   setzt kein `overflow`, der Rest lief sichtbar über den Kartenrand hinaus
   (Fifty-Screenshot: Überspringen halb außerhalb, neben der CTA). Mit
   `wrap` fällt `.ob-skip` (dank `margin-left:auto`, das auch auf der
   eigenen Zeile weiter rechtsbündig bleibt) bei Platzmangel in eine
   ZWEITE Zeile statt über den Rand zu laufen — bleibt in JEDEM Fall
   vollständig innerhalb der Box, in beiden Host-Kontexten (Popin/
   Standalone) und bei jedem Schritt (mit/ohne CTA — Schritte ohne CTA,
   `s.noCta` in dash-onboarding.js, blenden `#ob-wt-cta` komplett aus
   [`style.display='none'`, Muster `spot.style.display` im selben File],
   macht die Zeile für Back+Skip nur noch schmaler). */
.ob-row {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  margin-top: 13px;
}
.ob-cta {
  background: var(--cyan, #38e1ff);
  color: #04222b;
  border: 0;
  border-radius: var(--r-box, 8px);
  padding: 8px 14px;
  font-weight: 700;
  cursor: pointer;
  min-height: 32px;
  font-size: 13px;
}
/* ui-484 — Root Cause: companion/base.css definiert global
   `button:hover:not(:disabled) { color: var(--cyan); … }` — Spezifitaet
   (0,2,1) schlaegt das schlichte `.ob-cta:hover` (0,2,0), das bisher GAR
   KEIN eigenes `color` setzte. Der globale Hover-Text (var(--cyan)) landet
   damit exakt auf `.ob-cta`s eigener Hintergrundfarbe (background:
   var(--cyan)) → Text komplett unsichtbar (Fifty-Screenshot Tour-Schritt
   5/5, "Fertig ✓"-Pille rein cyan ohne lesbaren Inhalt; betraf technisch
   JEDEN Schritt, s. dash-onboarding.js — Schritt 5 war nur der zufaellig
   gemeldete). `:not(:disabled)` hier NICHT fuer echte Disabled-Logik (der
   Button wird nie disabled) — einzig um die Spezifitaet auf (0,3,0) zu
   heben und den globalen Reset zuverlaessig zu schlagen. `color`
   reetabliert den bereits eine Zeile darueber stehenden `.ob-cta`-Wert
   #04222b unveraendert (kein neuer Hex-Wert; identisches Literal wie im
   Schwester-Pattern `#ob-bridge .ob-cta-primary` in desktop.css, dort
   bereits durch die #ob-bridge-ID-Spezifitaet vor genau diesem Bug
   geschuetzt) — Kontrast auf var(--cyan) #38e1ff ≈ 10,5:1 (weit über der
   4,5:1-Mindestanforderung). focus-visible/aktiv OHNE Hover sind bereits
   lesbar (keine konkurrierende globale Regel dort) und brauchen keinen
   eigenen Fix. */
.ob-cta:hover:not(:disabled) {
  color: #04222b;
  box-shadow: 0 0 16px rgba(56,225,255,.4);
}
.ob-back {
  background: none;
  border: 0;
  color: var(--muted, #6b7a96);
  font-size: 12px;
  cursor: pointer;
  padding: 6px;
  min-height: 28px;
}
.ob-skip {
  margin-left: auto;
  background: none;
  border: 0;
  color: var(--muted, #6b7a96);
  font-size: 11px;
  cursor: pointer;
  text-decoration: underline;
  padding: 6px;
  min-height: 28px;
}

