/* Design ported from the Claude-Design project (Auswertung.dc.html / support.js).
   Parchment by default; grey when the switch in the header is thrown. Everything
   the two disagree about is a variable in one of the two blocks below, so there
   is nothing to keep in sync — a rule that spells a colour out is a rule that
   only works in one of them, which is why the danger, gold and warn shades moved
   up here too. Colours that sit on card artwork are the exception and stay
   fixed: the picture underneath is the same picture either way. */

:root {
  /* Leons Palette, warm. Die Namen sind seine, die Zuordnung ist die einzige
     Stelle, an der etwas dazukommt:

       --ch-ember  #C9420F  -> Akzent. 4,4:1 auf dem Grund, Weiss darauf 4,8:1.
       --ch-accent #E2600F  -> Fuellungen und Verlaeufe, nicht Text: auf Creme
                               kommt es auf 3,2:1 und ist als Schrift unlesbar.
       --ch-scale-line #8A3018 -> alles, was in Akzentfarbe *geschrieben* steht
                               (7,5:1). Sonst waere die halbe Beschriftung blass.
       --ch-highlight #F2A03D -> Warnton; 1,9:1, also nie auf hellem Grund als
                               Text, nur als Flaeche oder Marke. */
  --app-bg: linear-gradient(160deg,#faf2e5 0%,#f6ebd9 55%,#f1e3ce 100%);
  --header-bg: linear-gradient(180deg, rgba(255,253,248,.9), rgba(250,242,229,.66));
  --border-soft: #efe0c8; --border: #e3cfaf;
  --surface: linear-gradient(180deg,#fffdf8,#faf2e5); --surface-2: #f1e3ce;
  --text: #4a3327; --text-strong: #2a100b; --text-dim: #8a6a4f; --text-faint: #a4886c;
  --accent: #c9420f; --accent-glow: rgba(226,96,15,.45); --accent-soft: rgba(201,66,15,.10);
  --accent-border: #e3cfaf; --chip-a-text: #8a3018; --chip-text: #8a6a4f;
  --up: #17a06a; --down: #b0392e; --warn: #a8791f;
  --on-accent: #fffdf8;      /* text on a filled accent button */
  --overlay: rgba(42,16,11,.62);  /* scrims over card artwork / modals */
  /* Drei Familien, die vorher in vierzig Regeln buchstabiert standen und
     deshalb keinem Thema folgen konnten. */
  --danger-bg: #fbf0ea; --danger-bg-2: #f6ddd0;
  --danger-border: #e5c8b8; --danger-border-2: #c07050; --danger-text: #a8482c;
  --gold-bg: #f6e7c2; --gold-border: #d4b57e;
  --gold-text: #6d5623; --gold-text-2: #8a6a1c;
  --warn-bg: #fbeedc; --warn-border: #e9cfa6; --warn-text: #8a5518;
  /* Die oberste Kante des Seitenhintergrunds — das, was ein Registerreiter
     treffen muss, damit seine Unterkante in der Seite verschwindet. */
  --tab-bg: #faf2e5;
  /* Eine Hoehe fuer alles, was in einer Leiste nebeneinander steht: Knopf,
     Auswahlfeld, Menuepunkt. Gesagt statt gerechnet — ein `select` bringt auf
     macOS seine eigene mit. */
  --ctl-h: 31px;
  /* Acht Stuecke, acht Farben aus der warmen Familie — Ton *und* Helligkeit
     wechseln, damit zwei benachbarte sich nicht nur um eine Stufe unterscheiden.
     Acht, weil `TYPE_SECTIONS` acht Abschnitte kennt; die achte ist ein
     entsaettigtes Taupe, weil "Sonstiges" kein Typ ist, sondern der Rest. */
  --pie-1: #c9420f; --pie-2: #e2600f; --pie-3: #f2a03d; --pie-4: #8a3018;
  --pie-5: #b5703a; --pie-6: #6e1f14; --pie-7: #d9b98a; --pie-8: #a08d7e;
}

/* Grey, not a second colour scheme. The parchment is the look of this thing; at
   night the point is only that a screen full of card art should not sit in a
   lit-up page. So the greys are neutral and the mint stays mint, a shade
   brighter because the same green on dark reads as almost black.
   Chosen by hand on the root element, never from the system: the toggle is in
   the header, and a page that changed underneath because the Mac decided it was
   evening would be a surprise, not a service. */
:root[data-theme="dark"] {
  /* Leons Palette, kalt: Graphit. Der Dunkelmodus ist damit kein abgedunkeltes
     Warm mehr, sondern eine eigene Welt — das Auge im Logo ist hier Silber, und
     der Akzent #A6B1BC kommt auf 8,5:1 statt der 5,3:1, die das Orange hatte.

     Zwei Ausnahmen von der Palette, weil sie fuer Farben keine Werte hat und
     der Schirm sie braucht: Gewinn bleibt gruen, Verlust rot. Beide sind hier
     Aussagen ueber Geld und nicht ueber die Marke. */
  --app-bg: linear-gradient(160deg,#121316 0%,#16181b 55%,#101114 100%);
  --header-bg: linear-gradient(180deg, rgba(26,28,31,.92), rgba(18,19,22,.7));
  --border-soft: #26292d; --border: #33373c;
  --surface: linear-gradient(180deg,#1a1c1f,#161819); --surface-2: #212428;
  --text: #cbd4dc; --text-strong: #e8ebee; --text-dim: #8b929a; --text-faint: #6f757d;
  --accent: #a6b1bc; --accent-glow: rgba(166,177,188,.4); --accent-soft: rgba(166,177,188,.13);
  --accent-border: #5a6169; --chip-a-text: #cbd4dc; --chip-text: #8b929a;
  --up: #45c08a; --down: #e07a6a; --warn: #d9a441;
  --on-accent: #121316;
  --overlay: rgba(6,7,9,.7);
  --danger-bg: #2b1c1a; --danger-bg-2: #3a2220;
  --danger-border: #402724; --danger-border-2: #7a4038; --danger-text: #e0917f;
  --gold-bg: #2c2718; --gold-border: #4e4529;
  --gold-text: #d3c08c; --gold-text-2: #ddc98f;
  --warn-bg: #2b2618; --warn-border: #443c25; --warn-text: #d9a441;
  --tab-bg: #121316;
  /* Im Graphit gibt die Palette keine Toene her, also eine Helligkeitsleiter —
     aber mit weiten Schritten. Die vorige lief in Elferschritten von der
     Akzentfarbe zur Flaeche und war ab dem vierten Stueck nicht mehr vom
     Untergrund zu unterscheiden. */
  --pie-1: #e8ebee; --pie-2: #bfc8d1; --pie-3: #97a2ad; --pie-4: #717c88;
  --pie-5: #545c66; --pie-6: #3c424a; --pie-7: #2a2f35;
  /* Die achte faellt aus der Leiter heraus statt sie fortzusetzen: noch dunkler
     waere vom Untergrund nicht mehr zu trennen. Ein warmer Grauton — "Sonstiges"
     ist ohnehin kein Typ, sondern der Rest. */
  --pie-8: #8a8078;
}
* { box-sizing: border-box; }
/* Class rules that set `display` outrank the browser's own [hidden] handling —
   that is how a dialog can end up permanently on screen. Settle it once. */
[hidden] { display: none !important; }
html, body { margin: 0; height: 100%; }
body {
  font-family: 'Hanken Grotesk', system-ui, sans-serif; font-variant-numeric: tabular-nums;
  -webkit-font-smoothing: antialiased; color: var(--text); background: var(--app-bg);
  display: flex; flex-direction: column; height: 100vh;
}
a { color: inherit; text-decoration: none; }
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-thumb { background: rgba(128,128,128,.28); border-radius: 6px; border: 3px solid transparent; background-clip: padding-box; }
input, select, button { font-family: inherit; }

/* header + nav */
.header { display: flex; align-items: center; gap: 28px; padding: 0 22px; height: 58px;
  border-bottom: 1px solid var(--border-soft); background: var(--header-bg); flex: none; backdrop-filter: blur(6px); }
.brand { display: flex; align-items: center; gap: 10px; }
/* Leons Zeichen aus dem Design-Projekt: das freigestellte Drachenauge im
   Umriss (`static/logo.png` hell, `logo-dark.png` dunkel), ohne Rahmen und
   ohne Karton — seit 04.11.2026 auch hier, vorher stand an dieser Stelle das
   aeltere Schuppen-Auge als Kachel. */
.brand-mark { flex: none; width: 30px; height: auto; display: block; }
/* Dasselbe Mittel wie bei Sonne und Mond im Umschalter: beide im Markup, eine
   versteckt. Ein Tausch per Skript haette beim Laden einen Frame lang die
   falsche Fassung gezeigt. */
:root[data-theme="dark"] .light-only { display: none; }
:root:not([data-theme="dark"]) .dark-only { display: none; }
/* One word, two weights. The green square that used to sit beside it meant
   nothing on its own; the same colour inside the name means the second half. */
.brand-name { font-weight: 500; font-size: 15px; letter-spacing: .2px; color: var(--text-dim); }
.brand-hi { font-weight: 800; color: var(--accent); }
/* The current screen reads as a tab in a folder: it sits on the header's lower
   edge, its own bottom line is painted out, and the page continues underneath.
   The ringed pill it replaces looked like a thing you could press — which the
   one item you are already on is the one thing it is not. */
.nav { display: flex; gap: 2px; align-items: stretch; align-self: stretch; }
.nav a { display: flex; align-items: center; padding: 0 13px; margin-top: 9px;
  border: 1px solid transparent; border-bottom: none; border-radius: 9px 9px 0 0;
  font-size: 13px; font-weight: 500; color: var(--text-faint); }
.nav a:hover { color: var(--text-dim); }
.nav a.active { position: relative; font-weight: 600; color: var(--text-strong);
  background: var(--tab-bg); border-color: var(--border-soft); }
/* One pixel of page laid over the header's border, which is what makes the tab
   open into what is below instead of floating on top of it. */
.nav a.active::after { content: ''; position: absolute; left: 0; right: 0;
  bottom: -1px; height: 1px; background: var(--tab-bg); }
.spacer { flex: 1; }
/* Top right, the size of the other marks in this row. It carries one glyph and
   no label: what it does is visible the moment it is pressed, and a word next to
   it would be a caption on a light switch. */
.themebtn, .iconbtn { flex: none; display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 8px; cursor: pointer;
  border: 1px solid var(--border); background: var(--surface-2); color: var(--text-dim); }
.themebtn:hover, .iconbtn:hover { border-color: var(--accent-border); color: var(--text-strong); }
/* Wer angemeldet ist, und daneben die Tuer hinaus. Eigene Gruppe mit engem
   Abstand, sonst stuenden beide je 28 Pixel von ihren Nachbarn entfernt und die
   rechte Ecke fiele auseinander. */
.acct { display: flex; align-items: center; gap: 9px; }
.acct form { margin: 0; display: flex; }
/* Das Maennchen plus der Name. Auf schmalen Schirmen faellt der Name weg und
   das Symbol bleibt — es ist die Stelle, die man trifft, der Name die Auskunft,
   die man auf einem Telefon nicht braucht, weil dort ohnehin nur einer sitzt. */
.acct-name { display: inline-flex; align-items: center; gap: 6px;
  font-size: 12.5px; font-weight: 600; color: var(--text-dim); }
.acct-name:hover { color: var(--text-strong); }
@media (max-width: 720px) { .acct-handle { display: none; } }
.acct-name:hover { color: var(--text-strong); }
:root[data-theme="dark"] .themebtn .sun { display: none; }
:root:not([data-theme="dark"]) .themebtn .moon { display: none; }
.chips { display: flex; gap: 3px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 9px; padding: 3px; }
.chips a, .chips button { padding: 5px 11px; border-radius: 6px; font-size: 12px; font-weight: 600; cursor: pointer;
  border: none; background: transparent; color: var(--chip-text); }
.chips a.active, .chips button.active { background: var(--accent-soft); color: var(--chip-a-text); }
/* Ein Schalter, hinter dem nichts liegt, sagt das besser aus als eine leere
   Liste hinterher. */
.chips a.off { opacity: .38; cursor: default; }

/* Der Livescan-Reiter ist ein `.nav a` wie die uebrigen — nur der pulsierende
   Punkt bleibt ihm: er zeigt an, dass gerade etwas laeuft, was kein anderer
   Reiter je behauptet. Die fruehere gruene Pille drumherum ist weg. */
.nav a .live-dot { margin-right: 7px; flex: none; }
.live-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
  box-shadow: 0 0 0 0 var(--accent-glow); animation: livePulse 1.8s ease-out infinite; }
@keyframes livePulse {
  0%   { box-shadow: 0 0 0 0 var(--accent-glow); }
  70%  { box-shadow: 0 0 0 7px transparent; }
  100% { box-shadow: 0 0 0 0 transparent; }
}
/* Karte von Hand oben auflegen: Suchfeld + Trefferliste, in derselben Breite
   wie die Historie darunter. Die Treffer stehen als eigene Flaeche darunter,
   nicht als schwebendes Menue — ein Dropdown braucht Fokus-Regeln, die hier
   niemand vermisst. */
/* Nebeneinanderstehende Kaesten fangen oben gemeinsam an, statt sich auf die
   Hoehe des laengeren zu strecken. */
.grid { align-items: start; }
/* Frei waehlbares Fenster: zwei Datumsfelder, kein Absende-Knopf. */
.daterange { display: flex; align-items: center; gap: 8px; margin: 0;
  font-size: 12px; color: var(--text-faint); }
.daterange input { padding: 6px 9px; border-radius: 8px; font-size: 12.5px;
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
  font-family: inherit; }
.daterange input:focus { outline: none; border-color: var(--accent); }

.live-addbar { max-width: 880px; margin-bottom: 12px; }
.live-tools { display: flex; justify-content: flex-end; align-items: center;
  gap: 10px; margin-bottom: 8px; }
.live-addbar .print-list { margin-top: 8px; background: var(--surface);
  border: 1px solid var(--border); border-radius: 12px; padding: 8px;
  max-height: 320px; overflow-y: auto; }
/* Fehler haben eine Zeile; solange nichts schiefgeht, steht hier nichts. */
.live-msg { max-width: 880px; margin-bottom: 12px; padding: 9px 13px;
  border-radius: 10px; font-size: 13px; color: #8a5a1c;
  background: #fbf0e2; border: 1px solid #e7cfa8; }
/* The history is the scanner's own list (static/scanner.css), but it was drawn
   for a 400px sheet: on a dashboard the row stretches across the whole window
   with a stamp-sized thumbnail lost in the middle. These overrides give it the
   room the big screen actually has — the phone keeps its own sizes. */
.live-history { background: var(--surface); border: 1px solid var(--border);
  border-radius: 16px; overflow: hidden; max-width: 880px; }
.live-history .sheet-body { max-height: none; padding: 12px; gap: 10px; }
/* Auf dem grossen Schirm ist die Zeile ein Raster statt zweier gestapelter
   Reihen. Der Grund ist die senkrechte Ausrichtung: Griff, Platznummer, Prozent
   und das Kreuz gehoeren zur *ganzen* Karte, nicht zu ihrer oberen Haelfte — in
   der Kopfzeile zentriert sassen sie sichtbar zu hoch, weil die Zustands-Marken
   darunter noch mitzaehlen. Sie ueberspannen jetzt beide Zeilen (`grid-row:
   1/3`) und stehen damit wirklich mittig; Bild, Name und Marken ruecken dafuer
   in die Spalte daneben.

   `display: contents` an der Kopfzeile: die Elemente liegen im Markup in einem
   Zwischen-div (geteilt mit dem Handy), sollen hier aber unmittelbare
   Raster-Kinder sein. So bleibt das Handy-Layout unberuehrt. */
.live-history .result { border-radius: 12px; display: grid; padding: 12px 14px;
  grid-template-columns: auto auto auto minmax(0, 1fr) auto auto auto;
  grid-template-rows: auto auto; align-items: center; }
.live-history .result-head { display: contents; }
.live-history .result-grip,
.live-history .result-pos,
.live-history .result-thumb,
.live-history .result-flag,
.live-history .result-conf,
.live-history .result-del { grid-row: 1 / 3; }
.live-history .result-main { grid-column: 4; grid-row: 1; }
.live-history .result-tags { grid-column: 4; grid-row: 2; padding: 7px 0 0; gap: 7px; }
/* Abstaende als Rand statt als `gap`: das Ausrufezeichen ist meistens
   ausgeblendet, und eine Rasterluecke bliebe dann als Loch stehen. */
.live-history .result-grip { margin-right: 12px; }
/* Gefiltert wird nicht sortiert: der Griff bleibt als Platzhalter stehen, damit
   die Spalten nicht springen, laedt aber nicht mehr zum Ziehen ein. */
.live-history #results.nosort .result-grip { visibility: hidden; }
.live-history .result-pos { min-width: 24px; font-size: 13px; margin-right: 14px; }
.live-history .result-thumb { width: 88px; height: 64px; border-radius: 7px; margin-right: 16px; }
.live-history .result-name { font-size: 15px; }
.live-history .result-meta { font-size: 12.5px; margin-top: 3px; }
.live-history .result-flag { margin-left: 12px; }
.live-history .result-conf { font-size: 13px; margin-left: 14px; }
.live-history .result-del { margin-left: 12px; }
.live-history .empty { padding: 26px 14px; text-align: center; }

/* layout */
.scroll { flex: 1; overflow-y: auto; }
.container { max-width: 1180px; margin: 0 auto; padding: 26px 24px 60px; }
/* Bestellungen brauchen die ganze Breite: eine aufgeklappte Zeile traegt
   Anschrift, Versand und die Karten mit Platzangabe nebeneinander, und bei
   1180 px fing das an, sich zu stapeln. */
.container.wide { max-width: none; }
.page-head { display: flex; align-items: flex-end; justify-content: space-between; margin-bottom: 20px; gap: 16px; }
.page-title { font-size: 24px; font-weight: 800; color: var(--text-strong); letter-spacing: -.3px; }
.page-sub { font-size: 13.5px; color: var(--text-dim); margin-top: 5px; }

/* KPIs */
/* Von vier auf so viele, wie kommen: die fuenfte Kachel „Marge" erscheint erst,
   sobald ein Ankauf gebucht ist (`db/acquisitions.py`), und eine feste
   Vierer-Reihe haette sie umbrechen lassen — eine einzelne Kachel unter vier
   anderen sieht aus wie ein Fehler und nicht wie eine Zahl.

   `auto-fit` statt einer Zahl, weil die Anzahl von den Daten abhaengt und nicht
   vom Entwurf. 180px ist die Breite, unter der „€ 1.234,56" umbricht. */
.kpis { display: grid; gap: 16px;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
/* Gleiche Innenkante wie .panel (22px): Kacheln und Kaesten stehen auf jeder
   Seite untereinander, und ein Versatz von vier Pixeln sieht nicht nach
   Absicht aus, sondern nach Zufall. */
.kpi { background: var(--surface); border: 1px solid var(--border); border-radius: 15px; padding: 17px 22px; }
.kpis.two { grid-template-columns: repeat(2, 1fr); }
/* Ueberschrift eines Abschnitts. Die Startseite hatte eine Reihe Kacheln und
   drei Kaesten ohne Zwischenraum; jetzt sagt eine Zeile, welche Frage der
   Abschnitt darunter beantwortet. */
.sechead { display: flex; align-items: baseline; gap: 10px; margin: 34px 0 12px; }
.sechead.first { margin-top: 4px; }
.sechead h2 { margin: 0; font-size: 12px; font-weight: 700; letter-spacing: 1.1px;
  text-transform: uppercase; color: var(--text-dim); }
/* Was der Abschnitt zeigt, in einer Zeile neben der Ueberschrift statt als
   Untertitel in jedem Kasten darunter. */
.sechead-note { font-size: 12px; color: var(--text-faint); }
/* "und 10 weitere" unter einer gekuerzten Liste: die Zahl oben sagt, wieviel
   offen ist, diese Zeile sagt, dass die Liste nicht alles ist. */
.rows-more { padding: 10px 6px 2px; font-size: 11.5px; color: var(--text-faint); }
/* The two on the left are a switch — everything under them is computed over the
   one that is pressed. The two on the right are settled figures and deliberately
   not pressable: they are set back and quieter, so the row reads as two pairs
   rather than four of a kind. */
.kpi.pick { display: block; cursor: pointer; }
.kpi.pick:hover { border-color: var(--accent-border); }
.kpi.pick.on { border-color: var(--accent); background: var(--accent-soft); }
.kpi.pick.on .kpi-label { color: var(--chip-a-text); }
.kpi.done { background: none; border-style: dashed; }
.kpi.done .kpi-value { color: var(--text-dim); font-weight: 700; }
.kpi-label { font-size: 12px; color: var(--text-dim); font-weight: 600; margin-bottom: 12px; }
.kpi-value { font-size: 25px; font-weight: 800; color: var(--text-strong); letter-spacing: -.4px; }
.kpi-sub { font-size: 12px; margin-top: 6px; color: var(--text-faint); }
.kpi-sub.up { color: var(--up); font-weight: 700; }
.kpi-sub.down { color: var(--down); font-weight: 700; }
/* Wo die Zahl selbst die Nachricht ist (Marge), faerbt sie — nicht der
   Erklaertext darunter. Siehe `value_kind` in analytics.py. */
.kpi-value.up { color: var(--up); }
.kpi-value.down { color: var(--down); }

/* panels + grids */
.grid { display: grid; gap: 16px; margin-top: 16px; }
.grid.g-32 { grid-template-columns: 1.5fr 1fr; }
.grid.g-11 { grid-template-columns: 1fr 1fr; }
.panel { background: var(--surface); border: 1px solid var(--border); border-radius: 16px; padding: 20px 22px; }
.panel.tight { padding: 18px 20px; }
.panel-title { font-size: 14px; font-weight: 700; color: var(--text-strong); margin-bottom: 4px; }
.panel-cap { font-size: 12.5px; color: var(--text-faint); margin-bottom: 16px; }
.panel-head { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; margin-bottom: 14px; }
.badge-warn { font-size: 11px; font-weight: 700; color: var(--warn); padding: 2px 9px; border-radius: 20px;
  background: color-mix(in srgb, var(--warn) 14%, transparent);
  border: 1px solid color-mix(in srgb, var(--warn) 38%, transparent); }

/* bar chart */
/* Der Umsatzverlauf. Die Achse links macht die Hoehe eines Balkens ablesbar
   statt nur vergleichbar; die Stueckzahl steht als Zahl ueber dem Balken, weil
   sie als Punkt auf einer zweiten, unsichtbaren Skala lag und dort nichts
   bedeuten konnte. */
/* Auswertung: ein Rhythmus statt vier Zufaellen. Zwoelf Pixel zwischen Titel
   und Inhalt, in jedem Kasten — gemessen waren es 14, 4, 4 und 18. */
.reports .panel-title { margin-bottom: 12px; }
.reports .panel-head { margin-bottom: 0; }
.reports .panel-head .panel-title { margin-bottom: 12px; }
/* Der Schnitt: oben die Zahlen, unten die Karten. Eine Linie ueber die volle
   Breite und Luft darum — mehr braucht eine Grenze nicht. */
.reports .grid.cut { margin-top: 36px; padding-top: 28px;
  border-top: 1px solid var(--border-soft); }
.chartbox { display: flex; gap: 12px; }
.chart-axis { display: flex; flex-direction: column; justify-content: space-between;
  flex: none; height: 196px; padding-bottom: 19px; text-align: right;
  font-size: 10.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.bars { display: flex; align-items: flex-end; gap: 10px; height: 196px; flex: 1;
  border-left: 1px solid var(--border-soft); padding-left: 10px; }
.bar-col { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 7px; height: 100%; justify-content: flex-end; }
.bar-slot { position: relative; width: 100%; display: flex; justify-content: center; align-items: flex-end; flex: 1; }
.bar-fill { width: 60%; border-radius: 6px 6px 0 0;
  background: linear-gradient(180deg, var(--accent), color-mix(in srgb, var(--accent) 30%, transparent)); }
.bar-label { font-size: 10.5px; color: var(--text-faint); }
/* Der Balken sagt beim Ueberfahren, was er ist: Zeitraum, Betrag, Stueckzahl.
   Eine nackte Zahl darueber war keins davon — sie stand da, ohne zu sagen,
   wofuer sie steht. */
.bar-slot { cursor: default; }

/* segments */
.segments { display: flex; flex-direction: column; gap: 14px; }
.seg-head { display: flex; justify-content: space-between; margin-bottom: 6px; }
.seg-label { font-size: 12.5px; color: var(--text-dim); }
.seg-pct { font-size: 12.5px; font-weight: 700; color: var(--text-strong); }
.seg-pct.muted { color: var(--text-faint); font-weight: 500; }
.seg-track { height: 8px; border-radius: 5px; background: var(--surface-2); overflow: hidden; }
.seg-fill { height: 100%; border-radius: 5px; background: linear-gradient(90deg, var(--accent), color-mix(in srgb, var(--accent) 45%, transparent)); }
/* Woraus die Quote entstand — sonst ist "33 %" eine Zahl ohne Nenner. */
.seg-sub { font-size: 11px; color: var(--text-faint); margin-top: 5px; }

/* card list rows */
.rows { display: flex; flex-direction: column; gap: 2px; }
.row { display: flex; align-items: center; gap: 11px; padding: 9px 6px; border-bottom: 1px solid var(--border-soft); }
/* Die letzte Zeile zieht keinen Strich unter sich. Er trennte sie von nichts —
   und unter den blaetterbaren Listen von der Kante des Pfeilbalkens, also lagen
   dort zwei Haarlinien zehn Punkte auseinander. Wo eine Liste endet, sagt der
   Rand des Kastens.

   Zwei Regeln, weil `:last-child` das letzte Element im *Markup* trifft: in
   einer geblaetterten Liste ist das ein verstecktes, und die letzte sichtbare
   Zeile behielte ihren Strich. `pager.js` setzt dafuer `.last`. */
.rows .row:last-child,
.rows .row.last { border-bottom: none; }
.row:last-child { border-bottom: none; }
/* The card's own artwork, over the colour gradient it always had. The gradient
   stays underneath rather than being replaced: a printing with no crop on file
   then still shows its colours instead of an empty box. */
.art { position: relative; overflow: hidden; flex: none; width: 30px; height: 42px;
  border-radius: 5px;
  box-shadow: 0 3px 8px -3px rgba(0,0,0,.5), inset 0 0 0 1px rgba(0,0,0,.25); }
.art img { position: absolute; inset: 0; width: 100%; height: 100%;
  object-fit: cover; }
.row-main { flex: 1; min-width: 0; }
.row-name { font-size: 13px; font-weight: 600; color: var(--text-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.row-sub { font-size: 11.5px; color: var(--text-faint); margin-top: 2px; }
.row-end { flex: none; font-size: 13px; font-weight: 700; color: var(--text-strong); }
/* The pair on an order row: how far it has been picked, and whether Cardmarket
   is still waiting for the money. Both quiet — they are the state of a line you
   are scanning down, not the reason you stopped on it. */
.row-mid { flex: none; font-size: 12.5px; font-weight: 700; color: var(--text-dim);
  font-variant-numeric: tabular-nums; }
.row-flag { flex: none; padding: 2px 8px; border-radius: 999px; font-size: 10.5px;
  font-weight: 700; color: var(--warn-text); background: var(--warn-bg);
  border: 1px solid var(--warn-border); }
a.row:hover { background: var(--accent-soft); border-radius: 8px; }
.row-end.up { color: var(--up); } .row-end.down { color: var(--down); }
.row-end.warn { color: var(--warn); } .row-end.dim { color: var(--text-dim); }

.empty { color: var(--text-faint); font-size: 13px; padding: 8px 6px; }

/* ---------- Gesamtansicht (collection) ---------- */
@keyframes foilShift { 0%{background-position:0% 50%;} 50%{background-position:100% 50%;} 100%{background-position:0% 50%;} }

.col-layout { flex: 1; display: flex; min-height: 0; }

/* sidebar */
.sidebar { flex: none; width: 268px; border-right: 1px solid var(--border-soft);
  background: color-mix(in srgb, var(--surface-2) 60%, transparent); display: flex; flex-direction: column; min-height: 0; }
.side-head { display: flex; align-items: center; justify-content: space-between; padding: 16px 18px 10px; }
.side-title { font-size: 12px; font-weight: 700; letter-spacing: 1.2px; text-transform: uppercase; color: var(--text-dim); }
.reset { color: var(--text-faint); font-size: 12px; }
.side-body { padding: 0 18px 24px; overflow-y: auto; flex: 1; }
.search { position: relative; margin-bottom: 20px; }
.search-i { position: absolute; left: 10px; top: 50%; transform: translateY(-50%); color: var(--text-faint); font-size: 13px; }
.fgroup { margin-bottom: 20px; }
.fgroup.two { display: flex; gap: 10px; }
.fgroup.two > div { flex: 1; }
.flabel { font-size: 11px; color: var(--text-faint); text-transform: uppercase; letter-spacing: .8px; margin-bottom: 9px; }
.sidebar input, .sidebar select { background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px;
  color: var(--text); font-size: 12.5px; padding: 8px 9px; outline: none; width: 100%; }
.sidebar input:focus, .sidebar select:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
/* After `.sidebar input`, not before: same weight, so the last one wins — and
   the earlier order let the sidebar's 9px padding put the placeholder under the
   magnifier. */
.search input { padding: 9px 12px 9px 32px; }
.search-i { display: flex; pointer-events: none; }
/* Die Bereiche eines Decks im Kartendialog. Umbrechend, weil Star Wars fuenf
   davon hat und Riftbound fuenf — in einer Zeile stuenden sie sonst gedraengt
   oder liefen aus dem Dialog. */
/* Die Zahl am Freunde-Icon: rot und oben rechts am Knopf, weil sie eine
   Aufforderung ist und keine Statistik. `still` ist dieselbe Zahl im Menue,
   an ihrem Platz in der Zeile statt schwebend. */
/* Die Systemfarbe, nicht die Gefahrenfarbe: Eine Freundschaftsanfrage ist
   keine Warnung, sondern eine Einladung zu handeln — dasselbe Rotorange, das
   den aktiven Reiter und den Hauptknopf traegt. `--on-accent` fuer die Schrift,
   damit sie im hellen wie im dunklen Thema stimmt. */
.frnd-badge { position: absolute; top: -4px; right: -4px; min-width: 15px;
  height: 15px; padding: 0 4px; border-radius: 8px;
  background: var(--accent); color: var(--on-accent); font-size: 10px;
  font-weight: 700; line-height: 15px; text-align: center; }
.frnd-badge.still { position: static; }
#frndMenu { position: relative; }
.frow-copy { font-size: 11.5px; color: var(--text-dim); width: 100%; }
.frow-btns { display: inline-flex; gap: 6px; }
.frow-btns .viewbtn, .frow-btns .danger-btn { white-space: nowrap; }

/* Freunde: Zeilen mit Daten unter dem Namen (Set, Nummer, Kiste) und Links,
   die wie Zeilen aussehen. Kein `act-hint` — das ist fuer Erklaerzeilen
   reserviert, und hier stehen Fakten. */
.frow-sub { display: block; font-size: 11.5px; color: var(--text-faint); }
.frow-link { color: var(--accent); text-decoration: none; font-weight: 600; }
.frow-link:hover { text-decoration: underline; }

/* "Remove friend" at the foot of a friend's profile, "Withdraw" next to one's
   own request, "Revoke" next to an invitation link. Links and small forms that
   look like the danger button. The history of trades folds away. */
.frnd-foot { display: flex; justify-content: flex-end; margin-top: 24px; }
a.danger-btn { display: inline-block; text-decoration: none; }
.frnd-history > summary { cursor: pointer; list-style: none; }
.frnd-history > summary::-webkit-details-marker { display: none; }

/* Trades (migration 60): the two sides next to each other where there is
   room, one under the other on a phone. A card that cannot change hands says
   why in the danger colour — a fact to act on, not decoration. Closed trades
   are greyed in their folded history. */
.trade-panel + .trade-panel { margin-top: 12px; }
.trade-sides { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 22px; margin-top: 4px; }
.trade-side { min-width: 0; }
.trade-card { align-items: flex-start; margin-bottom: 8px; }
.trade-problem { display: block; font-size: 11.5px; font-weight: 600; color: var(--danger-text); }
.trade-money { margin: 6px 0 0; font-size: 13px; font-weight: 600; color: var(--text-strong); }
.trade-actions { margin-top: 12px; gap: 8px; }
.trade-confirm { margin-top: 12px; }
.trade-confirm .field-row { flex-wrap: wrap; }
.trade-history .trade-past { opacity: .62; }
/* A closed trade that can still put my cards back is not greyed: it asks. */
.trade-history .trade-past.trade-undo { opacity: 1; }
.trade-past .act-text { font-size: 12.5px; }
.trade-find { width: 100%; margin-top: 8px; }
.trade-amount { max-width: 120px; }
.trade-payer { flex: none; width: auto; }
.trade-held { opacity: .55; }
@media (max-width: 640px) { .trade-sides { grid-template-columns: 1fr; } }

/* Die Bereiche eines Decks im Kasten darueber. Rot nur, wo die Turniergrenze
   ueberschritten ist — und auch dann eine Auskunft, keine Sperre. */
.deck-zones { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 10px; }
.dz { font-size: 12px; color: var(--text-dim); background: var(--surface-2);
      border: 1px solid var(--border); border-radius: 7px; padding: 3px 9px; }
.dz b { color: var(--text-strong); font-weight: 700; margin-left: 3px; }
.dz.over, .dz.over b { color: var(--danger-text); }

.chips.zones { flex-wrap: wrap; }
.chips.zones button { white-space: nowrap; }

.chiprow { display: flex; flex-wrap: wrap; gap: 5px; }
.chip { padding: 5px 10px; border-radius: 7px; font-size: 12px; font-weight: 600; cursor: pointer;
  border: 1px solid var(--border); background: var(--surface-2); color: var(--text-dim); user-select: none; }
/* Gehalten wird die Markierung vom Kaestchen darin, nicht von einer Klasse,
   die der Server mitschickt. Seit das Filtern nur noch `#colmain` austauscht,
   bleibt die Seitenleiste stehen — und mit ihr die Klasse vom letzten Aufbau.
   Man kreuzte etwas an, das Raster aenderte sich, und der Filter sah aus wie
   vorher. `:has()` liest den tatsaechlichen Zustand, der ohnehin im Kaestchen
   steht, und kann deshalb gar nicht veralten. */
.chip.on, .chip:has(input:checked) { border-color: var(--accent);
  background: var(--accent-soft); color: var(--chip-a-text); }
.chip:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.colorrow { display: flex; gap: 6px; }
.colorbtn { width: 34px; height: 34px; border-radius: 9px; cursor: pointer; display: flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); background: var(--surface-2); }
.colorbtn.on, .colorbtn:has(input:checked) { border-color: var(--accent);
  background: var(--accent-soft); }
.colorbtn:has(input:focus-visible) { outline: 2px solid var(--accent); outline-offset: 2px; }
.cdot { width: 15px; height: 15px; border-radius: 50%; box-shadow: inset 0 0 0 1px rgba(0,0,0,.3); }
.pricerow input::-webkit-outer-spin-button,
.pricerow input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.pricerow input { -moz-appearance: textfield; appearance: textfield; }
.pricerow { display: flex; align-items: center; gap: 8px; }
.pricerow input { text-align: right; }
.pricerow span { color: var(--text-faint); }

/* main */
.col-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.mainbar { display: flex; align-items: center; gap: 14px; padding: 0 22px; height: 52px; border-bottom: 1px solid var(--border-soft); flex: none; }
.tabs { display: flex; gap: 4px; align-items: stretch; height: 100%; }
/* `nowrap`: In der Leiste steht seit „Umsortieren" ein Element mehr, und der
   erste Ausweg des Browsers ist, Beschriftungen umzubrechen — „Zum Verkauf" auf
   zwei Zeilen sieht aus wie ein Fehler und ist einer. Lieber schrumpft das
   Suchfeld, das dafuer eine Untergrenze hat. */
.tab { white-space: nowrap;
  display: flex; align-items: center; gap: 7px; padding: 0 4px; background: none; border: none;
  border-bottom: 2px solid transparent; color: var(--text-faint); font-size: 14px; font-weight: 500; cursor: pointer; }
.tab.active { border-bottom-color: var(--accent); color: var(--text-strong); font-weight: 700; }
.tab-count { font-size: 11px; font-weight: 700; padding: 1px 6px; border-radius: 20px; background: var(--surface-2); color: var(--text-faint); }
.tab.active .tab-count { background: var(--accent-soft); color: var(--chip-a-text); }
.sortbox { display: flex; align-items: center; gap: 8px; }
/* Dieselbe Schrift und dieselbe Polsterung wie `.ghost-btn` — sonst steht in
   einer Reihe ein Feld, das zwei Pixel niedriger ist als seine Nachbarn, und
   genau das sieht man. */
.sortbox select { height: var(--ctl-h); background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 8px; color: var(--text);
  font-size: 12.5px; font-weight: 600; padding: 0 8px 0 10px; outline: none; }
.active-badge { display: flex; align-items: baseline; gap: 9px; padding: 6px 14px; border: 1px solid var(--border); border-radius: 9px; background: var(--surface-2); }
.active-badge span { font-size: 11px; color: var(--text-faint); text-transform: uppercase; letter-spacing: .9px; }
.active-badge strong { font-size: 16px; color: var(--text-strong); }

/* tile grid */
.grid-scroll { flex: 1; overflow-y: auto; padding: 18px 22px 40px; }
.tilegrid { display: grid; grid-template-columns: repeat(auto-fill, minmax(158px, 1fr)); gap: 14px; }
.tile { position: relative; border-radius: 11px; overflow: hidden; background: var(--surface); border: 1px solid var(--border);
  cursor: pointer; box-shadow: 0 6px 18px -10px rgba(0,0,0,.7); transition: transform .18s ease, box-shadow .18s ease, border-color .18s ease;
  /* Was ausserhalb des Bildes liegt, wird gar nicht erst gesetzt, gezeichnet
     oder dekodiert. Bei zweihundert Kacheln ist das der groesste einzelne
     Gewinn, und man sieht nichts davon — `auto` merkt sich die tatsaechliche
     Hoehe, sobald eine Kachel einmal sichtbar war, damit der Bildlauf nicht
     springt. Der Startwert ist die Hoehe einer Kachel: Bild (Breite geteilt
     durch 1,32) plus Fuss. */
  content-visibility: auto; contain-intrinsic-size: auto 218px; }
.tile:hover, .tile:focus { transform: translateY(-3px); border-color: var(--accent-border); outline: none;
  box-shadow: 0 14px 30px -12px rgba(0,0,0,.85), 0 0 0 3px var(--accent-soft); }
.tile-art { position: relative; aspect-ratio: 1.32; overflow: hidden; }
.art-fill { position: absolute; inset: 0; }
.art-vignette { position: absolute; inset: 0; box-shadow: inset 0 -40px 44px -30px rgba(0,0,0,.9), inset 0 0 0 1px rgba(0,0,0,.25); }
.foil-sheen { position: absolute; inset: 0; pointer-events: none; opacity: .2; mix-blend-mode: color-dodge;
  background-image: linear-gradient(115deg, rgba(120,220,190,.55) 8%, rgba(150,140,230,.5) 26%, rgba(230,150,200,.5) 44%, rgba(220,210,120,.5) 62%, rgba(120,220,190,.55) 82%);
  background-size: 300% 300%; animation: foilShift 7s ease-in-out infinite; }
/* the browser clean-up page */
.reset-log { list-style: none; margin: 4px 0 0; padding: 0; display: flex;
  flex-direction: column; gap: 8px; }
.reset-line { font-size: 13px; color: var(--text); padding-left: 22px; position: relative; }
.reset-line::before { position: absolute; left: 0; font-weight: 700; }
.reset-line.ok::before { content: '\2713'; color: var(--up); }
.reset-line.bad::before { content: '\2715'; color: var(--down); }
.reset-line.note { color: var(--text-dim); }
.reset-line.note::before { content: '\2192'; color: var(--text-faint); }
.reset-actions { margin-top: 18px; }
.reset-btn { display: inline-block; padding: 9px 16px; border-radius: 9px;
  background: var(--accent); color: var(--on-accent); font-size: 13px; font-weight: 600; }

/* Das Namensfeld an einer Sitzung. Schmal, weil „Leons iPhone" hineinpasst und
   ein Satz nicht hineingehoert. */
.kname { width: 130px; padding: 5px 8px; border-radius: 7px; font-size: 12px;
  border: 1px solid var(--border); background: var(--bg); color: var(--text); }

/* Was ein Deck ueber sich sagt. Sitzt ueber dem Raster im Stapel, nicht auf
   einem eigenen Schirm — ein Deck *ist* ein Stapel. */
.deckpanel { margin-bottom: 16px; }
.deck-facts { font-size: 12px; color: var(--text-faint); }
/* Die Commander-Regeln zuerst: eine Karte ausserhalb der Farbidentitaet macht
   das Deck in *jedem* Format unspielbar, nicht nur in einem. */
.deck-problems { list-style: none; margin: 0 0 14px; padding: 0; display: flex;
  flex-direction: column; gap: 5px; font-size: 12.5px; color: var(--warn); }
.deck-formats { display: flex; flex-direction: column; gap: 2px; }
.deck-format { display: grid; grid-template-columns: 108px auto 1fr; gap: 10px;
  align-items: baseline; padding: 5px 2px; font-size: 12.5px; }
.deck-format + .deck-format { border-top: 1px solid var(--border-soft); }
.df-name { color: var(--text); font-weight: 600; }
.df-state { color: var(--text-faint); }
/* Legal ist die Ausnahme, nicht der Normalfall — deshalb traegt sie die Farbe
   und nicht das Gegenteil. */
.deck-format.ok .df-state { color: var(--up); font-weight: 700; }
.df-why { font-size: 11.5px; color: var(--text-faint); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.deck-bars { display: grid; gap: 20px; margin-top: 18px;
  grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
/* Die Gruppe ist das Raster, nicht die Zeile: nur so richtet sich die
   Beschriftungsspalte nach dem laengsten Wort *dieser* Gruppe aus. Mit einem
   Raster je Zeile stuenden „0" und „Verzauberungen" in verschieden breiten
   Spalten — und eine feste Breite waere entweder zu schmal fuer die Typen oder
   zu breit fuer die Manakurve. `display: contents` reicht die drei Felder der
   Zeile ans Raster der Gruppe durch. */
.deck-bargroup { display: grid; gap: 4px 8px; align-items: center;
  grid-template-columns: auto 1fr 28px; }
.deck-bargroup .flabel { grid-column: 1 / -1; }
.deck-bar { display: contents; }
.db-lab { text-align: right; white-space: nowrap; }
.db-track { height: 6px; border-radius: 999px; overflow: hidden;
  background: color-mix(in srgb, var(--text-faint) 18%, transparent); }
.db-fill { display: block; height: 100%; border-radius: 999px;
  background: var(--accent); }
.db-val { font-variant-numeric: tabular-nums; }

/* Was uebrig bleibt: Umsatz minus Abzuege. Drei Spalten, weil jede Zeile drei
   Dinge sagt — was es ist, wieviel, und woher man das weiss (oder eben nicht).
   Die Deckung steht *neben* dem Betrag und nicht darunter: eine Zahl mit einem
   Strich daneben ist eine Zahl, die man noch nicht hat, und das gehoert in
   dieselbe Zeile. */
.netrows { display: flex; flex-direction: column; }
.netrow { display: grid; grid-template-columns: 1fr auto 1fr; gap: 14px;
  align-items: baseline; padding: 8px 2px; font-size: 13px; }
.netrow + .netrow { border-top: 1px solid var(--border-soft); }
.net-lab { color: var(--text); }
.net-val { font-weight: 700; color: var(--text-strong);
  font-variant-numeric: tabular-nums; text-align: right; }
/* Ein Strich ist keine Null. Er sieht auch nicht so aus. */
.net-val.unknown { color: var(--text-faint); font-weight: 400; }
.net-note { font-size: 11.5px; color: var(--text-faint); }
.netrow.sum { border-top: 1px solid var(--border); margin-top: 4px; padding-top: 12px; }
.netrow.sum .net-lab, .netrow.sum .net-val { font-size: 15px; font-weight: 700; }

/* Die eine Zeile ueber dem Fehlerbericht: was durchging, und woher die Datei
   kam. Mehr steht dort nicht — der Kasten heisst „Nicht erkannt", darin stehen
   Zeilen, darunter ein Knopf. */
.importsum { font-size: 13px; color: var(--text-dim); margin-bottom: 10px; }
/* "3 wishes ticked off on Wants." with its Undo, one line (imports tick too);
   "Proxies would complete 2 wishes: …" with its "Tick off all" the same way. */
.importticks, .importasks { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; }

/* „Format wird erkannt" neben der Ueberschrift des Kartenfeldes. Eine
   Zusicherung, kein Hinweis: sie steht da, damit niemand nach dem Auswahlfeld
   sucht, das es absichtlich nicht gibt. */
.sec-hint { font-size: 11.5px; color: var(--text-faint); }

/* Import und Export. Keine eigene Formensprache — die Felder erben, was die
   Filterleiste schon hat, weil es dieselbe Sorte Eingabe ist. */
.qtext { width: 100%; padding: 8px 10px; border-radius: 8px; resize: vertical;
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px;
  line-height: 1.5; }
.tsel { padding: 8px 10px; border-radius: 8px; font-size: 13px; margin-right: 8px;
  border: 1px solid var(--border); background: var(--surface); color: var(--text); }
.tfile { font-size: 13px; color: var(--text-dim); }
/* Zwei Knoepfe nebeneinander: „erst ansehen" und „einbuchen" sind zwei
   Handlungen, keine Bestaetigung derselben. */
.tbar { margin-top: 16px; display: flex; flex-wrap: wrap; gap: 10px; }

/* Die Anmeldung. Ein Kasten, mittig, sonst nichts — von hier gibt es genau
   einen Weg weiter, und alles andere waere Ablenkung davon. */
.loginwrap { display: flex; justify-content: center; padding: 64px 16px; }
.loginbox { width: 100%; max-width: 320px; display: flex; flex-direction: column;
  gap: 14px; background: var(--surface); border: 1px solid var(--border);
  border-radius: 14px; padding: 24px; }
.login-title { font-size: 17px; font-weight: 700; color: var(--text-strong); }
.login-field { display: flex; flex-direction: column; gap: 5px; font-size: 12px;
  color: var(--text-faint); }
.login-field input { padding: 9px 11px; border-radius: 8px; font-size: 14px;
  border: 1px solid var(--border); background: var(--bg); color: var(--text); }
/* Ein Text fuer beide Faelle: „Konto gibt es nicht" von „Passwort falsch" zu
   unterscheiden verraet, welche Handles existieren. */
.login-err { font-size: 12.5px; color: var(--warn); line-height: 1.4; }
/* Der andere Weg, unter dem Knopf und leiser als er: wer hier steht, will
   meistens das, was der Knopf tut. */
.login-alt { font-size: 12px; color: var(--text-faint); text-align: center; }
.login-alt:hover { color: var(--text-dim); }

/* Der Kontoschirm. Er erbt Kaesten, Felder und Knoepfe — eigen ist nur, was es
   sonst nirgends gibt. */
.kmsg { margin-bottom: 14px; font-size: 12.5px; font-weight: 600; color: var(--chip-a-text); }
.kmsg.bad { color: var(--warn); }
.kform { margin: 0; max-width: 320px; }
/* Nur die Textfelder, nicht „jedes input": das Kaestchen darunter wurde davon
   320 Pixel breit und stand mit seiner Beschriftung an den Enden einer leeren
   Zeile. */
.kform .textin { width: 100%; }
.kform .fgroup { margin-bottom: 14px; }
/* Der Name, und daneben was er darf. Als Marke statt als zweite Zeile: bei
   einem Konto, das „admin" heisst, staende sonst zweimal dasselbe Wort
   untereinander. */
.krole { margin-left: 10px; vertical-align: middle; padding: 2px 8px;
  border-radius: 999px; font-size: 11px; font-weight: 700; letter-spacing: .3px;
  border: 1px solid var(--border); color: var(--text-dim); background: var(--surface-2); }
/* Formulare, die nur einen Knopf tragen: in einer Zeile stehen sie neben ihrem
   Text, und der Standardabstand eines <form> schoebe die Zeile auseinander. */
.kbar { margin: 0; display: flex; }
.kcheck { display: flex; align-items: center; gap: 8px; margin-bottom: 18px;
  font-size: 13px; font-weight: 600; color: var(--text); cursor: pointer; }
.kcheck input { accent-color: var(--accent); }
/* Die Warnung steht vor dem Haken und nicht als Fussnote darunter — hinter dem
   Haken passiert wirklich etwas, und danach ist es gelesen oder eben nicht. */
.kwarn { margin: 0 0 8px; font-size: 12px; color: var(--text-faint); line-height: 1.5; }

/* Das Ausdrucksfeld — die getippte Suche neben den Kaestchen.
   Schrift mit fester Breite, weil der Zeigefinger unter der Fehlermeldung sonst
   auf das falsche Zeichen zeigt: die Stelle wird in Zeichen gezaehlt, nicht in
   Pixeln, und in einer Proportionalschrift ist beides nicht dasselbe. */
.qbox input { width: 100%; padding: 8px 10px; border-radius: 8px;
  border: 1px solid var(--border); background: var(--surface); color: var(--text);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
.qbox.bad input { border-color: var(--warn); }
.qerr { margin-top: 6px; font-size: 11.5px; color: var(--warn); line-height: 1.4; }
.qmark { margin: 4px 0 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px; color: var(--text-faint); white-space: pre; overflow-x: auto; }

/* Die Ankuendigungsseite der Sammlerfassung. Keine eigene Formensprache — sie
   erbt Kasten, Titel und Knopf von allem anderen, weil sie eine Auskunft ist
   und keine Werbeflaeche. */
.soon-list { list-style: none; margin: 4px 0 0; padding: 0; display: flex;
  flex-direction: column; gap: 12px; }
.soon-list li { font-size: 13px; color: var(--text); line-height: 1.5;
  padding-left: 16px; position: relative; }
.soon-list li::before { content: '\2013'; position: absolute; left: 0;
  color: var(--text-faint); }
.soon-list strong { color: var(--text-strong); font-weight: 700; }
/* Nebeneinander, und beide gleich laut: es sind zwei Orte, nicht ein Ziel und
   eine Ausweiche. */
.soon-links { margin-top: 18px; display: flex; flex-wrap: wrap; gap: 10px; }

/* Type sections: a heading where one card type ends and the next begins, with
   real air around it — the sections are what makes a decklist readable. */
.typesec + .typesec { margin-top: 30px; }
.typesec-head { display: flex; align-items: center; gap: 8px; padding: 2px 2px 9px;
  font-size: 12px; font-weight: 700; letter-spacing: 1.1px; text-transform: uppercase;
  color: var(--text-dim); border-bottom: 1px solid var(--border-soft); margin-bottom: 14px; }
.typesec-dash { color: var(--text-faint); font-weight: 400; }
.typesec-n { font-size: 12px; font-weight: 700; color: var(--text-faint); }

/* Foil, spelled out under the condition — the sheen runs through the letters
   themselves, which is what a foil actually looks like. */
/* Same badge as the condition, but the lettering itself carries the sheen. The
   grey has to sit on a wrapper: background-clip:text applies to *every* layer of
   the element, so a background on the badge itself would be clipped away too. */
.tag-foil-badge { padding: 2px 7px; border-radius: 6px; background: rgba(12,13,15,.78); font-size: 10px; font-weight: 800; letter-spacing: 1px; }
.tag-foil-badge span { background-image: linear-gradient(100deg,#8fd6c4,#b7b0e0,#e0b7c8,#d6d68f,#8fd6c4);
  background-size: 300% 300%; animation: foilShift 6s ease-in-out infinite;
  -webkit-background-clip: text; background-clip: text; color: transparent; }

.tag-tl { position: absolute; top: 7px; left: 7px; display: flex; flex-direction: column;
  align-items: flex-start; gap: 4px; }
/* The condition and the commander mark are the same kind of label, so they are
   literally the same rule — styling them separately is how they drifted apart. */
.tag-cond, .tag-cmd { padding: 2px 7px; border-radius: 6px; background: rgba(12,13,15,.78); font-size: 10px; font-weight: 700; letter-spacing: .4px;
  color: #e8ebee; white-space: nowrap; }
.tag-tr { position: absolute; top: 7px; right: 7px; display: flex; gap: 5px; }
/* The two bottom corners, each a row of its own — colour pips and the slot in
   the pile on the left, quantity and language on the right. They used to be
   positioned individually and sat on top of each other in the stack view. */
.tag-bl { position: absolute; bottom: 7px; left: 7px; display: flex; align-items: center; gap: 6px; max-width: 62%; }
.tag-br { position: absolute; bottom: 7px; right: 7px; display: flex; align-items: center; gap: 5px; }
.tag-pips { display: flex; gap: 3px; }
.pip { width: 8px; height: 8px; border-radius: 50%; box-shadow: 0 0 0 1.5px rgba(12,13,15,.7), 0 0 4px rgba(0,0,0,.5); }
/* the pile header sits above the tabs; the grid below it is the collection grid */
.stackbar { display: flex; align-items: center; gap: 14px; padding: 0 22px; height: 50px;
  border-bottom: 1px solid var(--border-soft); flex: none; }
.stack-back { text-decoration: none; color: var(--text-dim); font-size: 17px; line-height: 1;
  padding: 4px 8px; border: 1px solid var(--border); border-radius: 8px; }
.stack-back:hover { color: var(--text); border-color: var(--accent); }
.stack-id { display: flex; align-items: center; gap: 8px; min-width: 0; }
.stack-name { font-weight: 700; font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Pile settings: name, note, kind and deleting live together behind the gear —
   they are all things a person decides about the pile, as opposed to what is in
   it. The kind used to sit alone in the header, which made a settled fact look
   like an open question and left the name with no way to change it at all. */
.gearbtn, .viewbtn, .plusbtn { flex: none; display: flex; align-items: center;
  justify-content: center; width: 30px; height: 30px; border-radius: 8px;
  cursor: pointer; border: 1px solid var(--border); background: var(--surface-2);
  color: var(--text-faint); }
.gearbtn:hover, .viewbtn:hover, .plusbtn:hover { border-color: var(--accent-border);
  color: var(--text-strong); }
/* Das Pluszeichen ist der einzige der drei, der etwas Neues macht statt etwas
   umzustellen — es traegt deshalb die Akzentfarbe, nicht das Grau der anderen. */
.plusbtn { color: var(--accent); }
.modal-frame.narrow { width: min(520px, 100%); padding: 0; }
/* the first block sits under the dialog heading and needs to clear it */
.modal-actions { display: flex; align-items: center; gap: 12px; padding: 14px 0 4px;
  border-top: 1px solid var(--border-soft); margin-top: 6px; }
.modal-right.wide { width: 100%; }
.textin { flex: 1; min-width: 0; padding: 8px 11px; border: 1px solid var(--border);
  border-radius: 9px; background: var(--surface); color: var(--text); font-size: 13px; }
.textin:focus { outline: none; border-color: var(--accent-border);
  box-shadow: 0 0 0 3px var(--accent-soft); }
.radiorow { display: flex; align-items: flex-start; gap: 11px; padding: 9px 10px;
  border: 1px solid transparent; border-radius: 10px; cursor: pointer; }
.radiorow:hover { background: var(--surface-2); }
.radiorow.on { background: var(--accent-soft); border-color: var(--accent-border); }
.radiorow input { margin-top: 3px; accent-color: var(--accent); flex: none; }
.primary-btn { padding: 9px 18px; border: none; border-radius: 9px; cursor: pointer;
  background: var(--accent); color: var(--on-accent); font-size: 13px; font-weight: 700; }
/* Ein Knopf, der nicht geht, muss auch so aussehen. Ohne diese Regel war er
   abgeschaltet und trotzdem leuchtend rot — man drueckt, es passiert nichts, und
   der Fehler sitzt scheinbar woanders. */
.primary-btn:disabled { cursor: not-allowed; background: var(--surface-2);
  color: var(--text-faint); box-shadow: inset 0 0 0 1px var(--border); }
.danger-btn { padding: 8px 14px; border: 1px solid var(--danger-border); border-radius: 9px;
  cursor: pointer; background: var(--danger-bg); color: var(--danger-text); font-size: 12.5px; font-weight: 600; }
.danger-btn:hover { background: var(--danger-bg-2); border-color: var(--danger-border-2); }

.stack-kind { padding: 2px 8px; border-radius: 999px; font-size: 11px; font-weight: 700;
  border: 1px solid var(--border); color: var(--text-dim); background: var(--surface-2); }
/* a deck is read differently — no order to it — so it is marked, not just named */
.stack-kind.is-deck { border-color: var(--gold-border); color: var(--gold-text); background: var(--gold-bg); }
.stack-note { font-size: 12.5px; color: var(--text-faint); white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; max-width: 30ch; }
/* where the copies sit in the pile; a deck never gets this */
.tag-pos { padding: 1px 6px; border-radius: 6px; background: rgba(12,13,15,.78); font-size: 11px; font-weight: 700; color: #dfe3e7;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.tag-count { padding: 1px 6px; border-radius: 6px; background: rgba(12,13,15,.78); font-size: 11px; font-weight: 700; color: #dfe3e7; }
/* Condition and language, on every tile (Leon, 05.11.2026: "it doesn't have
   to be intrusive"): one pill, a lighter scrim and softer type than the count,
   so a tile reads its name and price first. Not .tag-lang: the scanner history
   already uses that name, and the Livescan screen loads both stylesheets. */
.tile-copy { display: inline-flex; align-items: center; gap: 5px; padding: 1px 6px; border-radius: 6px;
  background: rgba(12,13,15,.5); font-size: 10px; font-weight: 600; letter-spacing: .4px;
  color: rgba(232,235,238,.8); white-space: nowrap; }
/* the condition moves the price more than the language: a shade brighter */
.tile-cond { color: rgba(238,240,242,.92); }
.tile-lang { text-transform: uppercase; }
.tile-foot { padding: 9px 10px 10px; }
.tile-name { font-size: 12.5px; font-weight: 600; color: var(--text-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; margin-bottom: 5px; }
.tile-price { font-size: 15px; font-weight: 700; color: var(--text-strong); }
.tile-price.noprice { font-size: 12.5px; font-weight: 600; color: var(--text-faint); font-style: italic; }
/* A proxy is a copy, not a value (migration 52): it wears the condition tag's
   badge with its own word, and its price is struck through wherever it shows.
   Signed, altered and misprint wear the same badge (a tile is copies that are
   exactly the same, 05.11.2026, so the tile says which they are). */
.tag-proxy, .tag-mark { letter-spacing: 1px; font-weight: 800; }
.tile-price.proxy, .cr-price.proxy, .sb-val.proxy { text-decoration: line-through;
  color: var(--text-faint); }
/* Sorted by "Paid": the price paid in the price's place, its word in front. */
.paid-word { font-size: 11px; font-weight: 600; letter-spacing: .2px;
  color: var(--text-dim); }
.cr-price.paid { width: auto; min-width: 86px; white-space: nowrap; }

/* The asking price leads on the sale screen; what the card is worth drops to a
   reference beside it. In a pile it is the other way round and there is only
   one number, so this row does not appear at all. */
/* The two figures the asking price is measured against, as marks rather than
   words: written out they competed with the price above them for the same line
   and for the same glance. */
.tile-refs { display: flex; align-items: center; gap: 10px; margin-top: 3px; }
.pref { display: inline-flex; align-items: center; gap: 3px; cursor: help;
  font-size: 10.5px; font-weight: 600; color: var(--text-faint);
  font-variant-numeric: tabular-nums; white-space: nowrap; }
.pref:hover { color: var(--text-dim); }
.pref-i { display: inline-flex; align-items: center; justify-content: center;
  width: 12px; font-size: 12px; line-height: 1; opacity: .8; }
/* two letters need more room than a glyph, and read as a label rather than a
   number when they are a shade smaller */
.pref-i.wide { width: auto; font-size: 9.5px; font-weight: 700; letter-spacing: .4px; }

/* An anchor nobody has refreshed in a fortnight still counts, it just should not
   look as solid as the figure beside it. */
.pref.anchor.stale { color: var(--warn); opacity: .85; }

/* ---------- Etiketten auf dem Kartenbild ----------
   Ohne `backdrop-filter`. Der Weichzeichner sass auf jedem Etikett, jedem
   Kaestchen und jedem Tooltip — bei zweihundert Kacheln rund 284 Elemente, von
   denen jedes eine eigene Kompositionsebene erzwingt und den Hintergrund
   fortlaufend neu weichzeichnet. Das war das Ruckeln beim Bildlauf.
   Sichtbar ist er ohnehin kaum: der Schleier darunter deckt zu 72 %, und was
   durch 28 % Restdurchlass weichgezeichnet wird, erkennt niemand. Die Deckung
   steigt dafuer minimal, damit die Schrift genauso gut steht wie vorher. */
/* ---------- the sale tick, top right over the artwork ----------
   Always in the same spot, whatever the card is called — under the footer it
   moved with the length of the name, and you are aiming at one small target per
   card while going through a pile. */
.tile-sale { position: relative; flex: none; display: inline-flex; padding: 0;
  cursor: pointer; border: 0; background: none; }
.tile-sale input { position: absolute; opacity: 0; width: 0; height: 0; }
.tile-box {
  width: 19px; height: 19px; border-radius: 6px; display: inline-block;
  position: relative; background: rgba(12,13,15,.78);
  border: 1.5px solid rgba(228,238,231,.5);
  transition: background .12s ease, border-color .12s ease;
}
.tile-sale:hover .tile-box { border-color: #fff; background: rgba(12,13,15,.85); }
/* The tick is drawn, not typed: a text glyph sits on the baseline and drifts by
   a pixel or two per font, which is very visible inside a 19px box. */
.tile-box::after {
  content: ''; position: absolute; left: 5.5px; top: 2px; width: 4px; height: 9px;
  border: solid #fff; border-width: 0 2px 2px 0; transform: rotate(42deg);
  opacity: 0;
}
.tile-sale input:checked + .tile-box,
.tile-sale.on .tile-box { background: var(--accent); border-color: var(--accent); }
.tile-sale input:checked + .tile-box::after,
.tile-sale.on .tile-box::after { opacity: 1; }
.tile-sale input:focus-visible + .tile-box,
.tile-sale.asks:focus-visible .tile-box { outline: 2px solid #fff; outline-offset: 2px; }
.tile-sale.locked { cursor: not-allowed; }
.tile-sale.locked .tile-box { opacity: .55; }

/* Says what a control is, without the browser's own second-long delay before its
   `title` appears. Drawn inside the tile on purpose: the tile clips its overflow,
   so a tooltip reaching past the edge would simply be cut in half — which is also
   why it goes downwards from the corner and upwards from the footer. */
.has-tip { position: relative; }
.has-tip::after {
  content: attr(data-tip); position: absolute; right: 0;
  width: max-content; max-width: 148px; padding: 5px 8px; border-radius: 7px;
  background: rgba(12,13,15,.94); color: #e8ebee;
  font-size: 10.5px; font-weight: 600; line-height: 1.35; text-align: right;
  white-space: normal; pointer-events: none; opacity: 0;
  transition: opacity .12s ease .2s; z-index: 4;
}
.has-tip:hover::after, .has-tip:focus-visible::after { opacity: 1;
  backdrop-filter: blur(3px); }
.tip-down::after { top: 25px; }
.tip-up::after { bottom: 20px; }
/* Fuer alles nahe am linken Rand: der Text haengt sonst an `right: 0` und laeuft
   aus dem Bild, wo ihn niemand mehr lesen kann. */
.tip-start::after { right: auto; left: 0; text-align: left; }

.capnote { padding: 20px 2px 6px; font-size: 12.5px; color: var(--warn); }
.pager { display: flex; align-items: center; justify-content: center; gap: 16px; padding: 26px 0 6px; }
.pgbtn { padding: 7px 14px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2); color: var(--text); font-size: 13px; }
.pginfo { font-size: 12px; color: var(--text-faint); }
.empty-state { display: flex; flex-direction: column; align-items: center; justify-content: center; height: 100%; text-align: center; color: var(--text-faint); gap: 12px; }
.empty-ico { width: 54px; height: 54px; border-radius: 14px; border: 1px dashed var(--border); display: flex; align-items: center; justify-content: center; font-size: 22px; }
.empty-title { font-size: 16px; font-weight: 600; color: var(--text-dim); }
.empty-reset { margin-top: 4px; padding: 8px 16px; background: var(--accent-soft); border: 1px solid var(--accent-border); border-radius: 8px; color: var(--chip-a-text); font-size: 13px; }

/* detail modal */
/* ---------- "wie viele?" ----------
   A small box, not the card dialog's frame: the question has four possible
   answers and they are the buttons. A number field plus a confirm button plus a
   paragraph explaining what would happen was three steps for one decision, and
   it stood in the way of the thing the tick exists for — getting through a box. */
.askbox.wide { width: min(400px, 100%); text-align: left; }
.askbox { position: relative; width: min(320px, 100%); padding: 18px 18px 16px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 14px;
  box-shadow: 0 40px 80px -20px rgba(0,0,0,.8); text-align: center; }
.ask-x { position: absolute; top: 10px; right: 10px; }
.ask-title { font-size: 14.5px; font-weight: 700; color: var(--text-strong); }
.ask-name { margin-top: 2px; font-size: 12px; color: var(--text-faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Slider and field are the same number twice. Dragging is quicker at four
   copies, typing at forty, and which pile you are holding is not something the
   dialog can know — so it offers both rather than choosing for you. */
/* one row per thing you can do, all the same shape */
.exp-row { display: flex; align-items: center; gap: 10px; margin-top: 14px; }
.exp-lab { flex: none; width: 96px; font-size: 12.5px; color: var(--text-dim); }
.exp-row.check { cursor: pointer; font-size: 12.5px; color: var(--text); }
/* above the list, so it is read before the rows rather than found under them */
.exp-row.check.top { margin-top: 12px; }
.exp-row.check input { width: 16px; height: 16px; accent-color: var(--accent); }
.exp-go { display: block; width: 100%; margin-top: 16px; text-align: center; text-decoration: none; }

/* The export dialog is a list, so it is wider than the count popup and its rows
   are the content. Scrolls inside itself: a hundred staged cards must not push
   the download button off the screen. */
.askbox.export { width: min(520px, 100%); text-align: left; }
.exp-head { display: flex; align-items: center; gap: 10px; margin: 14px 0 6px;
  padding-bottom: 6px; border-bottom: 1px solid var(--border-soft); }
.exp-all { display: flex; align-items: center; gap: 7px; cursor: pointer;
  font-size: 12px; font-weight: 600; color: var(--text-dim); }
.exp-all input { width: 15px; height: 15px; accent-color: var(--accent); }
.exp-sum { font-size: 12.5px; font-weight: 700; color: var(--text-strong);
  font-variant-numeric: tabular-nums; }
.exp-list { max-height: 46vh; overflow-y: auto; margin: 0 -4px; padding: 0 4px; }
.exp-item { display: flex; align-items: baseline; gap: 9px; padding: 5px 4px;
  border-radius: 7px; cursor: pointer; }
.exp-item:hover { background: var(--surface-2); }
.exp-item input { flex: none; width: 15px; height: 15px; accent-color: var(--accent);
  align-self: center; }
.exp-name { flex: 1; min-width: 0; font-size: 12.5px; font-weight: 600;
  color: var(--text-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.exp-meta { flex: none; font-size: 10.5px; color: var(--text-faint); white-space: nowrap; }
.exp-price { flex: none; font-size: 12.5px; font-weight: 700; color: var(--text-strong);
  font-variant-numeric: tabular-nums; }
/* an unticked row is still readable — it is a choice, not an error */
.exp-item:has(input:not(:checked)) .exp-name,
.exp-item:has(input:not(:checked)) .exp-price { color: var(--text-faint); font-weight: 600; }
.exp-empty { margin-top: 16px; font-size: 12.5px; color: var(--text-faint); }

.ask-pick { display: flex; align-items: center; gap: 10px; margin: 16px 0 14px; }
.ask-range { flex: 1; min-width: 0; height: 4px; -webkit-appearance: none; appearance: none;
  border-radius: 999px; background: var(--surface-2); border: 1px solid var(--border);
  cursor: pointer; }
.ask-range::-webkit-slider-thumb { -webkit-appearance: none; appearance: none;
  width: 18px; height: 18px; border-radius: 50%; background: var(--accent);
  border: 2px solid var(--surface); box-shadow: 0 1px 4px rgba(0,0,0,.4); cursor: grab; }
.ask-range::-moz-range-thumb { width: 16px; height: 16px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--surface); cursor: grab; }
.ask-range:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }
.ask-num { flex: none; width: 56px; padding: 6px 4px; border: 1px solid var(--border);
  border-radius: 8px; background: var(--surface); color: var(--text-strong);
  font-size: 14px; font-weight: 700; text-align: center; font-variant-numeric: tabular-nums; }
.ask-num:focus { outline: none; border-color: var(--accent-border); }
.ask-go { width: 100%; }

.modal-overlay { position: fixed; inset: 0; background: var(--overlay); backdrop-filter: blur(4px); display: flex; align-items: center; justify-content: center; z-index: 50; padding: 30px; }
/* The panel is the frame, not the card: it is wider by exactly the arrows' lane
   on each side, so the arrows sit inside the dialog rather than floating on the
   backdrop. The frame also does not scroll, which is what keeps them in place
   while the content moves. */
.modal-frame { position: relative; width: min(1020px,100%); display: flex;
  padding: 0 50px; background: var(--surface); border: 1px solid var(--border);
  border-radius: 16px; box-shadow: 0 40px 80px -20px rgba(0,0,0,.8); overflow: hidden; }
.modal { width: 100%; max-height: calc(100vh - 60px); overflow-y: auto;
  background: none; border: none; border-radius: 0; box-shadow: none;
  display: flex; position: relative; }
/* the gap clears the card's own drop shadow, which reaches ~24px below it;
   at 16px the shadow washed into the price box under it */
.modal-left { flex: none; width: 300px; padding: 22px 22px 22px 0;
  border-right: 1px solid var(--border-soft); display: flex; flex-direction: column;
  gap: 30px; }
.modal-art { position: relative; aspect-ratio: .716; border-radius: 12px; overflow: hidden; box-shadow: 0 20px 40px -16px rgba(0,0,0,.8); }
.foil-sheen.big { opacity: .34; }
.stat-box { text-align: center; padding: 10px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 9px; }
.sb-label { font-size: 10px; color: var(--text-faint); text-transform: uppercase; letter-spacing: .7px; margin-bottom: 3px; }
.sb-val { font-size: 17px; font-weight: 700; color: var(--text-strong); }
.modal-right { flex: 1; min-width: 0; padding: 22px 24px; }
.modal-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 12px; }
.modal-head h2 { margin: 0; font-size: 22px; font-weight: 700; color: var(--text-strong); line-height: 1.2; }
.modal-x { flex: none; width: 30px; height: 30px; border-radius: 8px; background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); cursor: pointer; font-size: 15px; }
.modal-sub { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 4px 0 18px; font-size: 13px; color: var(--text-dim); }
.badge-foil { font-size: 11px; font-weight: 700; padding: 2px 8px; border-radius: 6px; color: #0c0d0f; background: conic-gradient(from 0deg,#a7ddc9,#c4bfe6,#e6c4d2,#dcdc9a,#a7ddc9); }
.locbox { background: var(--accent-soft); border: 1px solid var(--accent-border); border-radius: 12px; padding: 14px 15px; margin-bottom: 18px; }
.loc-head { display: flex; align-items: center; gap: 8px; margin-bottom: 10px; }
.loc-title { font-size: 12px; font-weight: 700; letter-spacing: .6px; text-transform: uppercase; color: var(--chip-a-text); }
.loc-count { font-size: 12px; color: var(--text-faint); }
.loc-list { display: flex; flex-direction: column; gap: 6px; }
.loc { display: flex; align-items: center; gap: 12px; padding: 8px 11px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 9px; }
.loc-stack { font-size: 15px; font-weight: 700; color: var(--text-strong); min-width: 66px; }
.loc-pos { font-size: 13px; color: var(--text-dim); }
.loc .spacer { flex: 1; }
.loc-listed { font-size: 12px; font-weight: 700; color: var(--accent); }
/* changing the printing: the hash cannot tell reprints apart, so this is where a
   wrong set gets fixed. A dropdown, not set symbols — Scryfall's symbols differ
   in size and some printings have none, which made the list look broken. */
.printsel { width: 100%; padding: 7px 10px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface-2); color: var(--text); font-size: 12.5px; margin-bottom: 4px; }
/* the commander of a deck */
.loc-cmd { font-size: 11px; font-weight: 700; color: var(--gold-text); background: var(--gold-bg);
  border: 1px solid var(--gold-border); border-radius: 999px; padding: 2px 8px; }
.loc-cmd-btn { padding: 4px 9px; border: 1px solid var(--border); border-radius: 7px;
  background: transparent; color: var(--text-faint); font-size: 13px; cursor: pointer; }
.loc-cmd-btn.on { background: var(--gold-bg); color: var(--gold-text-2); border-color: var(--gold-border); }

/* removing a copy is destructive, so it stays quiet until you reach for it */
.loc-del { margin-left: 6px; padding: 5px 9px; border: 1px solid var(--border); border-radius: 7px;
  background: transparent; color: var(--text-faint); font-size: 12px; font-weight: 700; cursor: pointer; }
.loc-del:hover { border-color: var(--danger-border-2); color: var(--danger-text); background: var(--danger-bg); }
/* --- card detail dialog ------------------------------------------------ */
.sec { padding: 14px 0; border-top: 1px solid var(--border-soft); }
.sec:first-of-type { border-top: none; padding-top: 4px; }
/* Must stand after the rule above, not before: both weigh the same, so the last
   one wins — and the first version of this sat 100 lines earlier and did nothing
   at all. Clears the dialog heading in the pile settings. */
.sec.first { padding-top: 30px; }
.sec-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 10px; }
.sec-title { font-size: 12px; font-weight: 700; letter-spacing: .9px; text-transform: uppercase;
  color: var(--text-dim); }
.sec-note { font-size: 11.5px; color: var(--text-faint); }
.sec-foot { margin-top: 8px; font-size: 11.5px; color: var(--text-faint); }

.field-row { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; }
.field-label { flex: none; width: 92px; font-size: 12.5px; color: var(--text-faint); }
.field-row .printsel { margin-bottom: 0; }

/* stepping through copies sits next to the card name, because it decides what
   every per-copy row further down refers to */
.copypager { display: inline-flex; align-items: center; gap: 6px; margin-left: auto; }
.copynav { flex: none; width: 24px; height: 24px; border-radius: 6px; cursor: pointer;
  border: 1px solid var(--border); background: var(--surface-2); color: var(--text-dim);
  font-size: 14px; line-height: 22px; text-align: center; }
.copynav:hover { border-color: var(--accent-border); color: var(--text); }
.copynav.off { opacity: .35; pointer-events: none; }
.copy-of { font-size: 11.5px; font-weight: 700; color: var(--text-faint); }

/* Adding a card by hand: not everything gets scanned, and a deck is often typed
   in from a list. Sits in the pile header, where the pile's own actions belong. */
/* Sits in the toolbar row beside the tabs — two rows are enough for a screen
   that shows a list. */
/* `min-width`, seit „Umsortieren" mit in der Reihe steht: Ohne sie schrumpft das
   Suchfeld als Einziges — es ist das einzige `flex: 1` — und zeigte zuletzt zwei
   Buchstaben. Ein Feld, in das man einen Kartennamen tippt, muss einen
   Kartennamen zeigen koennen. */
.addbox { position: relative; flex: 1; min-width: 160px; max-width: 340px; }
.addbox-i { position: absolute; left: 11px; top: 50%; transform: translateY(-50%);
  display: flex; color: var(--text-faint); pointer-events: none; }
.addsearch { width: 100%; padding: 8px 12px 8px 32px; border: 1px solid var(--border);
  border-radius: 9px; background: var(--surface); color: var(--text); font-size: 13px; }
.addsearch:focus { outline: none; border-color: var(--accent-border);
  box-shadow: 0 0 0 3px var(--accent-soft); }
.addresults { position: absolute; top: calc(100% + 6px); left: 0; right: 0; z-index: 40; }
.addlist { border: 1px solid var(--border); border-radius: 10px; background: var(--surface);
  box-shadow: 0 18px 38px -18px rgba(0,0,0,.55); max-height: 320px; overflow-y: auto; }
.addopt { display: flex; align-items: baseline; gap: 10px; width: 100%; padding: 9px 12px;
  border: none; border-bottom: 1px solid var(--border-soft); background: transparent;
  cursor: pointer; text-align: left; font-size: 12.5px; color: var(--text); }
.addopt:last-child { border-bottom: none; }
.addopt:hover { background: var(--accent-soft); }
.ao-name { flex: 1; font-weight: 600; min-width: 0; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.ao-set { flex: none; font-size: 11.5px; font-weight: 700; color: var(--text-faint); }
/* how many other printings exist — the dialog is where you pick a different one */
.ao-more { font-weight: 600; opacity: .75; }
.ao-price { flex: none; font-weight: 600; min-width: 58px; text-align: right; }
.addnone, .addok { padding: 9px 12px; border: 1px solid var(--border); border-radius: 10px;
  background: var(--surface); font-size: 12.5px; color: var(--text-faint);
  box-shadow: 0 18px 38px -18px rgba(0,0,0,.55); }
.addok { color: var(--chip-a-text); border-color: var(--accent-border); font-weight: 600; }
/* Kein `margin-left` mehr: links davon stand die Stueckzahl, von der die Summen
   Abstand brauchten. Jetzt beginnt die Reihe selbst, und der Abstand kaeme von
   nirgendwo. */
.totals { display: inline-flex; align-items: baseline; gap: 14px; }
.tot { display: inline-flex; align-items: baseline; gap: 5px; font-size: 13px; font-weight: 700;
  color: var(--text-strong); font-variant-numeric: tabular-nums; white-space: nowrap; }
.tot-lab { font-size: 10px; font-weight: 700; letter-spacing: .4px; text-transform: uppercase;
  color: var(--text-faint); }
.moneyin.tiny .textin { width: 74px; padding: 3px 6px 3px 18px; font-size: 13px;
  font-weight: 700; text-align: right; }
.moneyin.tiny .moneyin-sym { left: 6px; font-size: 11px; }
/* Ebenso der Trennstrich: er trennte den Wert von der Stueckzahl. Ohne sie
   stuende er als Strich am linken Rand einer einzelnen Zahl. Die Groesse kam
   bisher von `.result-count` — jetzt steht sie hier, sonst faellt der Betrag auf
   die Schriftgroesse der Leiste zurueck und ist auf einmal groesser als die
   Summen auf dem Verkaufsschirm. */
.result-value { font-size: 13px; font-weight: 700; color: var(--text-strong);
  font-variant-numeric: tabular-nums; }

/* Stepping through the grid: chevrons at the card's own left and right edge —
   part of the dialog, not floating on the backdrop. No plate behind them; a
   button-looking thing there competes with the actual controls inside. */
.stepper { position: absolute; top: 50%; transform: translateY(-50%); z-index: 2;
  width: 40px; height: 76px; display: flex; align-items: center; justify-content: center;
  cursor: pointer; font-size: 34px; line-height: 1; color: var(--text-faint);
  background: none; border: none; user-select: none; transition: color .14s; }
.stepper:hover { color: var(--text-strong); }
.stepper.prev { left: 2px; }
.stepper.next { right: 2px; }
.stepper.off { opacity: .18; pointer-events: none; }

/* foil belongs to the price — that is what it changes */
.sb-foil { margin-top: 8px; font-size: 11px; font-weight: 800; letter-spacing: 1.2px; }
.sb-foil span { background-image: linear-gradient(100deg,#3f8f78,#6f68b0,#a86f8c,#8a8a3c,#3f8f78);
  background-size: 300% 300%; animation: foilShift 6s ease-in-out infinite;
  -webkit-background-clip: text; background-clip: text; color: transparent; }

/* deleting says what it is with a bin, not with a word */
.binbtn { flex: none; display: flex; align-items: center; justify-content: center;
  width: 32px; height: 32px; border-radius: 9px; cursor: pointer;
  border: 1px solid var(--danger-border); background: var(--danger-bg); color: var(--danger-text); }
.binbtn:hover { background: var(--danger-bg-2); border-color: var(--danger-border-2); }

/* Per-copy settings: a plain list of rows, each saying in words what it does.
   Label left with a hint under it, control right, always in the same place. */
.actrow { display: flex; align-items: center; gap: 16px; padding: 11px 0;
  border-bottom: 1px solid var(--border-soft); }
.actrow:last-of-type { border-bottom: none; }
/* a rule where the group-wide fields end and the per-copy ones begin */
.actrow.rule { border-top: 1px solid var(--border-soft); margin-top: 6px; padding-top: 14px; }
.actrow.quiet { opacity: .82; }
.actrow.quiet:hover { opacity: 1; }
.act-text { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.act-name { font-size: 13px; font-weight: 600; color: var(--text-strong); }
.act-hint { font-size: 11.5px; color: var(--text-faint); }

/* how many of this card the deck runs — a number you nudge, not a form you fill */
.counter { flex: none; display: inline-flex; align-items: center; gap: 2px;
  border: 1px solid var(--border); border-radius: 9px; background: var(--surface-2);
  overflow: hidden; }
.countbtn { width: 30px; height: 30px; border: none; background: transparent; cursor: pointer;
  color: var(--text-dim); font-size: 16px; font-weight: 700; line-height: 1; }
.countbtn:hover { background: var(--accent-soft); color: var(--chip-a-text); }
.countnum { min-width: 26px; text-align: center; font-size: 13px; font-weight: 700;
  color: var(--text-strong); }

.starbtn { flex: none; width: 32px; height: 32px; border-radius: 9px; cursor: pointer;
  border: 1px solid var(--border); background: var(--surface-2); color: var(--text-faint);
  font-size: 15px; line-height: 1; transition: background .14s, color .14s, border-color .14s; }
.starbtn:hover { border-color: var(--gold-border); color: var(--gold-text-2); }
.starbtn.on { background: var(--gold-bg); color: var(--gold-text-2); border-color: var(--gold-border);
  box-shadow: 0 0 0 3px rgba(201,180,130,.25); }
.linkbtn { flex: none; padding: 6px 10px; border: none; border-radius: 7px; cursor: pointer;
  background: transparent; font-size: 12.5px; font-weight: 600; color: var(--text-faint); }
.linkbtn.danger:hover { color: var(--danger-text); background: var(--danger-bg); }

/* Printing picker: a searchable list, because a native dropdown has no room for
   set, number and price side by side — and 150 Islands cannot be scrolled. */
/* the printing picker gets the full width under its heading */
.field-block { margin-bottom: 14px; }
.printpick { flex: 1; min-width: 0; }
.printsearch-box { position: relative; margin-bottom: 8px; }
.printsearch-i { position: absolute; left: 10px; top: 50%; transform: translateY(-50%);
  display: flex; color: var(--text-faint); pointer-events: none; }
.printsearch { width: 100%; padding: 8px 44px 8px 30px; border: 1px solid var(--border);
  border-radius: 9px; background: var(--surface); color: var(--text); font-size: 12.5px; }
.printsearch:focus { outline: none; border-color: var(--accent-border);
  box-shadow: 0 0 0 3px var(--accent-soft); }
.printsearch-n { position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
  font-size: 11px; font-weight: 700; color: var(--text-faint); pointer-events: none; }
.printlist { max-height: 176px; overflow-y: auto; border: 1px solid var(--border);
  border-radius: 10px; background: var(--surface); }
.printopt { display: flex; align-items: center; gap: 10px; width: 100%; padding: 8px 11px;
  border: none; border-bottom: 1px solid var(--border-soft); background: transparent;
  cursor: pointer; text-align: left; font-size: 12.5px; color: var(--text-dim); }
.printopt:last-child { border-bottom: none; }
.printopt:hover { background: var(--accent-soft); color: var(--text); }
.printopt.on { background: var(--accent-soft); color: var(--chip-a-text); cursor: default; }
.printopt.on .po-set::before { content: '✓ '; }
.po-set { flex: none; min-width: 62px; font-weight: 700; letter-spacing: .3px; }
.po-num { flex: 1; color: var(--text-faint); }
.printopt.on .po-num { color: var(--chip-a-text); }
.po-price { flex: none; font-weight: 600; }
.printnone { padding: 10px 2px; font-size: 12px; color: var(--text-faint); }

/* Label above value, not beside it. Side by side they share about 130px in the
   narrow left column, and a word like "uncommon" simply runs into "Seltenheit". */
.statgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 16px; margin-bottom: 18px; }
/* Stapel und Platz stehen nebeneinander — sie beantworten zusammen eine Frage
   ("wo greife ich hin"), und untereinander lasen sie sich wie zwei. Lange
   Kistennamen werden abgeschnitten statt umgebrochen, damit die Zeile nicht
   wandert. */
.statgrid .strow span:last-child { overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.strow { display: flex; flex-direction: column; gap: 1px; padding: 6px 0;
  border-bottom: 1px solid var(--border-soft); min-width: 0; }
.strow span:first-child { font-size: 10.5px; letter-spacing: .5px; text-transform: uppercase;
  color: var(--text-faint); }
.strow span:last-child { font-size: 12.5px; color: var(--text); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pricehist { margin-bottom: 18px; }
.ph-label { font-size: 12px; font-weight: 700; letter-spacing: .6px; text-transform: uppercase; color: var(--text-dim); margin-bottom: 8px; }
.ph-box { height: 120px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 10px; padding: 8px; }
.ph-box svg { width: 100%; height: 100%; }
/* The line with the numbers that make it readable: scale on the left, latest
   value on the right, the two dates underneath. */
/* `gap` grosszuegiger als noetig: die Wertstufen klebten an der Linie, und eine
   Zahl, die eine Achse beruehrt, liest sich als Teil der Kurve. */
.ph { display: flex; align-items: flex-start; gap: 14px; padding-right: 4px; }
.ph-scale { display: flex; flex-direction: column; justify-content: space-between;
  flex: none; padding: 1px 0; font-size: 10px; font-weight: 600; color: var(--text-faint);
  font-variant-numeric: tabular-nums; text-align: right; white-space: nowrap; }
/* Drawn inside the SVG on purpose: they mark the same fractions of the box the
   labels do, so they have to follow the same stretch. */
.ph-grid { stroke: var(--border-soft); stroke-width: 1; }
.ph-range { margin-left: auto; }
.ph-range a { cursor: pointer; }
.ph-plot { flex: 1; min-width: 0; }
.ph-dates { display: flex; justify-content: space-between; margin-top: 5px;
  font-size: 10px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.ph-empty { height: 90px; display: flex; align-items: center; justify-content: center; text-align: center; background: var(--surface-2); border: 1px dashed var(--border); border-radius: 10px; color: var(--text-faint); font-size: 13px; padding: 0 12px; }
.actions { padding-top: 4px; border-top: 1px solid var(--border-soft); }
.act-label { font-size: 10px; color: var(--text-faint); text-transform: uppercase; letter-spacing: .7px; margin: 12px 0 8px; }
.condrow { display: flex; flex-wrap: wrap; gap: 5px; }
/* A value with nothing to choose (a printing in one language). */
.field-text { font-size: 13px; color: var(--text-strong); }
/* Dasselbe Menue wie ueber dem Raster, nur im Dialogkopf: Der Knopf steht dort
   neben dem Schliessen-Kreuz, also braucht er dessen Groesse, und das Feld
   darunter darf nicht am Dialogrand kleben. */
.copymenu { margin-left: auto; }
.copymenu .viewbtn { width: 30px; height: 30px; display: flex; align-items: center;
  justify-content: center; border-radius: 8px; background: var(--surface-2);
  border: 1px solid var(--border); color: var(--text-dim); cursor: pointer; }
.copymenu .viewbtn:hover { border-color: var(--accent-border); color: var(--text-strong); }
.copymenu .viewpop { width: 290px; }
/* Etwas mehr Luft als im Ansichtsmenue: Dort stehen Ankreuzfelder untereinander,
   hier zwei Eingaben, und die brauchen ihren Rand. */
.copypop { padding: 8px 10px 10px; }
.copypop .field-row { margin-bottom: 8px; }
/* Keine feste Breite: „Verschieben" passte nicht in die 46 px, wurde
   abgeschnitten und lief unter das Auswahlfeld — die beiden standen ineinander.
   Das Wort bekommt, was es braucht, das Feld den Rest. */
.copypop .field-label { width: auto; flex: none; white-space: nowrap; }
.copypop .printsel { flex: 1; min-width: 0; width: auto; }
/* Das Feld in der Mitte des Zaehlers. Es sieht aus wie die Zahl, die es
   ersetzt — nur eingebbar: kein eigener Rahmen, kein eigener Untergrund, denn
   der Rahmen des Zaehlers ist schon da. */
.countin { width: 40px; border: none; background: transparent; color: var(--text-strong);
  font: inherit; font-size: 13px; font-weight: 700; text-align: center;
  appearance: textfield; -moz-appearance: textfield; }
.countin::-webkit-outer-spin-button, .countin::-webkit-inner-spin-button {
  appearance: none; margin: 0; }
.countin:focus { outline: none; background: var(--accent-soft); }
/* „von 40" steht neben dem Zaehler und nicht darunter: Es ist der Rand der Zahl
   im Feld, keine eigene Auskunft. */
.vp-of { flex: none; font-size: 11.5px; color: var(--text-faint); }
/* Das Loeschen unten, ueber die ganze Breite und hinter einem Strich — wie
   „Stapel loeschen" am Fuss der Stapeleinstellungen. */
.vp-foot { margin-top: 10px; padding-top: 11px; border-top: 1px solid var(--border-soft); }
/* Die Muelltonne mittig auf ganzer Breite: Die Groesse ist die Aussage — ein
   Knopf, der so breit ist wie das Menue, ist nicht aus Versehen zu treffen. */
.vp-foot .danger-btn { width: 100%; display: flex; align-items: center;
  justify-content: center; padding: 7px 14px; }
.condbtn { padding: 6px 12px; border: 1px solid var(--border); border-radius: 7px; background: var(--surface-2); color: var(--text-dim); font-size: 12.5px; font-weight: 600; cursor: pointer; }
.condbtn.on { border-color: var(--accent); background: var(--accent-soft); color: var(--chip-a-text); }
.toast { position: absolute; top: 12px; left: 50%; transform: translateX(-50%); z-index: 60; padding: 10px 18px; background: var(--surface-2); border: 1px solid var(--accent-border); border-radius: 11px; color: var(--chip-a-text); font-size: 13px; box-shadow: 0 16px 40px -12px rgba(0,0,0,.7); }
/* narrow screens: the arrows' lane shrinks rather than squeezing the card */
@media (max-width: 900px) {
  .modal-frame { padding: 0 32px; }
  .stepper { width: 28px; height: 56px; font-size: 26px; }
}
@media (max-width: 700px) { .modal { flex-direction: column; } .modal-left { width: auto; border-right: none; border-bottom: 1px solid var(--border-soft); flex-direction: row; } .modal-art { width: 120px; } }

/* real card images (overlay the gradient fallback) */
.art-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
.card-img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }

/* ---------- Stapel ---------- */
.page-hint { font-size: 12.5px; color: var(--text-faint); }
/* Pile overview: each pile wears the face of a card — its commander, or the card
   it starts with. A grid of identical panels told you nothing but the name. */
.warnbar { margin-bottom: 16px; padding: 11px 14px; border-radius: 10px;
  background: var(--danger-bg); border: 1px solid var(--danger-border); color: var(--danger-text); font-size: 13px; }
.pile-wrap { position: relative; }
/* deleting is irreversible, so it sits outside the link and has to be aimed at */
.pile-del { position: absolute; right: 9px; top: 9px; margin: 0; opacity: 0;
  transition: opacity .14s; z-index: 2; }
.pile-wrap:hover .pile-del, .pile-del:focus-within { opacity: 1; }
.pile-del button { display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; border-radius: 8px; cursor: pointer;
  border: 1px solid rgba(255,255,255,.28); background: rgba(24,20,12,.62);
  color: #f0c6b8; backdrop-filter: blur(4px); }
.pile-del button:hover:not(:disabled) { background: rgba(150,52,28,.85);
  border-color: rgba(255,255,255,.4); color: #fff; }
/* Dead while the pile holds something that must not vanish with it: a sale on
   record, or a card publicly on offer. Still drawn, because a bin that is simply
   absent looks like a screen that forgot it. */
.pile-del button:disabled { opacity: .4; cursor: not-allowed; }
/* ---------- one block per kind of pile, foldable ----------
   The heading matches the type headings over the card grid, because it is the
   same idea one level up: a deck and a bulk box are different objects, and
   looking for one you are never looking for the other. */
.pilesec + .pilesec { margin-top: 30px; }
.pilesec-head { display: flex; align-items: center; gap: 8px; padding: 2px 2px 9px;
  font-size: 12px; font-weight: 700; letter-spacing: 1.1px; text-transform: uppercase;
  color: var(--text-dim); border-bottom: 1px solid var(--border-soft);
  margin-bottom: 14px; cursor: pointer; list-style: none; user-select: none; }
.pilesec-head::-webkit-details-marker { display: none; }
.pilesec-head:hover { color: var(--text-strong); }
/* The arrow is drawn rather than typed, for the reason the tick on a tile is:
   a glyph sits on the baseline and would swing off centre when it turns. */
.pilesec-mark { flex: none; width: 7px; height: 7px; border: solid currentColor;
  border-width: 0 1.6px 1.6px 0; transform: rotate(-45deg) translate(-1px, -1px);
  transition: transform .15s ease; }
.pilesec[open] > .pilesec-head .pilesec-mark { transform: rotate(45deg) translate(-2px, -2px); }
.pilesec:not([open]) > .pilesec-head { margin-bottom: 0; }

.pile-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); gap: 18px; }
.pile { display: block; border-radius: 15px; overflow: hidden; background: var(--surface);
  border: 1px solid var(--border); transition: transform .18s ease, box-shadow .18s ease,
  border-color .18s ease; }
.pile:hover { transform: translateY(-3px); border-color: var(--accent-border);
  box-shadow: 0 16px 34px -16px rgba(0,0,0,.8); }
.pile-art { position: relative; aspect-ratio: 1.5; overflow: hidden; }
/* the art carries text, so it needs a floor of contrast under it */
.pile-scrim { position: absolute; inset: 0;
  background: linear-gradient(180deg, rgba(10,8,4,.42) 0%, rgba(10,8,4,.05) 38%, rgba(10,8,4,.82) 100%); }
.pile-badges { position: absolute; top: 9px; left: 9px; display: flex; align-items: center;
  gap: 6px; max-width: calc(100% - 56px); }
.pile-kind { padding: 2px 8px; border-radius: 999px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .4px; backdrop-filter: blur(3px);
  background: rgba(12,13,15,.6); color: #dfe3e7; border: 1px solid rgba(255,255,255,.18); }
.pile-kind.commander { background: rgba(120,96,32,.72); color: #f6ecc9; border-color: #c9b482; }
.pile-title { position: absolute; left: 12px; right: 12px; bottom: 10px; }
.pile-name { display: block; font-size: 16px; font-weight: 800; color: #f2f5f7;
  letter-spacing: -.2px; text-shadow: 0 1px 10px rgba(0,0,0,.7);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pile-face { display: block; margin-top: 2px; font-size: 11.5px; color: rgba(251,247,238,.72);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pile-foot { display: flex; align-items: baseline; gap: 10px; padding: 11px 13px; }
.pile-count { font-size: 13px; font-weight: 700; color: var(--text-strong); }
.pile-count small { font-weight: 500; color: var(--text-faint); }
.pile-value { font-size: 14px; font-weight: 800; color: var(--text-strong); }
.pile-note { padding: 0 13px 12px; font-size: 12px; color: var(--text-faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* still used by the order detail header */
.stack-lbl { font-size: 12px; color: var(--text-faint); font-weight: 600; }
.stack-detail-head { display: flex; align-items: center; gap: 20px; flex-wrap: wrap; padding: 18px 20px; border-radius: 15px; background: var(--surface); border: 1px solid var(--border); }
.sdh-num { display: flex; align-items: baseline; gap: 10px; }
.sdh-big { font-size: 30px; font-weight: 800; color: var(--text-strong); letter-spacing: -.5px; word-break: break-all; }
.sdh-sep { width: 1px; height: 34px; background: var(--border); }
.sdh-l { font-size: 11px; color: var(--text-faint); text-transform: uppercase; letter-spacing: .6px; }
.sdh-v { font-size: 16px; font-weight: 700; color: var(--text); margin-top: 3px; }
.sdh-v small { font-size: 12px; color: var(--text-faint); font-weight: 500; }
.sdh-v.val { color: var(--accent); }
.sdh-v.gap { color: var(--warn); }
.pos-hint { font-size: 12.5px; color: var(--text-faint); margin: 14px 2px 12px; }
.pos-list { display: flex; flex-direction: column; gap: 7px; }
.posrow { display: flex; align-items: center; gap: 12px; padding: 9px 12px; border-radius: 10px; background: var(--surface); border: 1px solid var(--border); }
.posrow.gap { background: color-mix(in srgb, var(--warn) 6%, transparent); border: 1px dashed color-mix(in srgb, var(--warn) 40%, var(--border)); }
.posnum { flex: none; width: 52px; text-align: center; font-size: 14px; font-weight: 700; color: var(--accent); }
.posnum.gap { color: var(--text-faint); }
.posart { flex: none; width: 34px; height: 47px; border-radius: 6px; overflow: hidden; position: relative; box-shadow: 0 3px 8px -3px rgba(0,0,0,.7), inset 0 0 0 1px rgba(0,0,0,.3); }
.posart img { width: 100%; height: 100%; object-fit: cover; }
.posart .art-fill { position: absolute; inset: 0; }
.posart-gap { flex: none; width: 34px; height: 47px; border-radius: 6px; border: 1px dashed color-mix(in srgb, var(--warn) 45%, var(--border));
  background: repeating-linear-gradient(135deg, transparent, transparent 4px, color-mix(in srgb, var(--warn) 8%, transparent) 4px, color-mix(in srgb, var(--warn) 8%, transparent) 8px); }
.posmain { flex: 1; min-width: 0; }
.posname { font-size: 13.5px; font-weight: 600; color: var(--text-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; display: flex; align-items: center; gap: 8px; }
.posname-gap { font-size: 13px; color: var(--warn); font-weight: 600; }
.posmeta { font-size: 11.5px; color: var(--text-faint); margin-top: 2px; }
.posfoil { flex: none; width: 7px; height: 7px; border-radius: 50%; background: conic-gradient(from 0deg,#8fd6c4,#b7b0e0,#e0b7c8,#d6d68f,#8fd6c4); }
.posreview { flex: none; font-size: 10px; font-weight: 700; color: var(--warn); padding: 1px 6px;
  border-radius: 5px; background: color-mix(in srgb, var(--warn) 16%, transparent);
  border: 1px solid color-mix(in srgb, var(--warn) 40%, transparent); }
.posprice { flex: none; font-size: 14px; font-weight: 700; color: var(--text-strong); min-width: 80px; text-align: right; }

/* ---------- Warenkorb ---------- */
.cart-total { display: flex; flex-direction: column; align-items: flex-end; }
.cart-total span { font-size: 11px; color: var(--text-faint); text-transform: uppercase; letter-spacing: .6px; }
.cart-total strong { font-size: 20px; color: var(--accent); }
.cart-list { display: flex; flex-direction: column; }
.cartrow { display: flex; align-items: center; gap: 12px; padding: 11px 18px; border-top: 1px solid var(--border-soft); }
.cartrow form { margin: 0; }
.cart-art { flex: none; width: 38px; height: 52px; border-radius: 6px; overflow: hidden; position: relative; box-shadow: 0 3px 8px -3px rgba(0,0,0,.7), inset 0 0 0 1px rgba(0,0,0,.3); }
.cart-art img { width: 100%; height: 100%; object-fit: cover; }
.cart-art .art-fill { position: absolute; inset: 0; }
.cart-main { flex: 1; min-width: 0; }
.cart-name { font-size: 13.5px; font-weight: 600; color: var(--text-strong); display: flex; align-items: center; gap: 8px; }
.cart-meta { font-size: 11.5px; color: var(--text-faint); margin-top: 2px; }
.cart-price { display: flex; align-items: center; gap: 5px; }
.cart-price span { color: var(--text-faint); font-size: 13px; }
.cart-price input { width: 84px; padding: 7px 9px; background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px; color: var(--text); font-size: 13px; text-align: right; outline: none; }
.cart-price input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.cart-price button, .symbtn { width: 32px; height: 32px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface-2); color: var(--text-dim); cursor: pointer; font-size: 14px; }


/* ---------- Scanner (PWA) ---------- */
.scan-wrap { flex: 1; display: flex; flex-direction: column; min-height: 0; }
.scan-bar { display: flex; align-items: center; gap: 12px; padding: 10px 14px;
  border-bottom: 1px solid var(--border-soft); flex: none; min-height: 52px; flex-wrap: nowrap; }
.scan-stack { display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--text-faint); }
.scan-stack input { width: 130px; padding: 7px 9px; background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 8px; color: var(--text); font-size: 13px; outline: none; }
.scan-stack input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
/* Fixed width + no wrapping: the text changes constantly while scanning and a
   reflowing bar made the whole page jump on the phone. */
.scan-status { margin-left: auto; font-size: 12px; font-weight: 700; padding: 4px 11px; border-radius: 20px;
  border: 1px solid var(--border); background: var(--surface-2); color: var(--text-dim);
  min-width: 104px; text-align: center; white-space: nowrap; flex: none; }
.scan-status[data-state="stable"] { color: var(--warn); border-color: color-mix(in srgb, var(--warn) 45%, var(--border)); }
.scan-status[data-state="locked"], .scan-status[data-state="busy"] { color: var(--text-faint); }
.scan-status[data-state="ok"] { color: var(--up); border-color: var(--accent-border); background: var(--accent-soft); }
.scan-status[data-state="review"] { color: var(--warn);
  background: color-mix(in srgb, var(--warn) 14%, transparent); }
.scan-status[data-state="error"] { color: var(--down); }
.scan-count { font-size: 12px; color: var(--text-faint); }
.scan-count strong { font-size: 15px; color: var(--text-strong); }

.scan-stage { position: relative; flex: none; height: 46vh; background: #0c0d0f; overflow: hidden; }
.scan-stage video { width: 100%; height: 100%; object-fit: cover; display: block; }
/* Guide in real card proportions (63:88) — a square frame told you nothing
   about how to hold the card. Height-driven so it always fits the stage. */
.scan-frame { position: absolute; top: 6%; bottom: 6%; left: 50%; transform: translateX(-50%);
  aspect-ratio: 63 / 88; border: 2px dashed color-mix(in srgb, var(--accent) 60%, transparent);
  border-radius: 12px; pointer-events: none;
  box-shadow: 0 0 0 100vmax color-mix(in srgb, #000 34%, transparent); }
.scan-frame::after { content: ''; position: absolute; inset: -1px; border-radius: 12px;
  box-shadow: inset 0 0 0 1px color-mix(in srgb, #fff 22%, transparent); }
.scan-hint { position: absolute; inset: auto 12px 12px; text-align: center; font-size: 13px; color: var(--text-dim);
  background: var(--overlay); padding: 9px 12px; border-radius: 10px; }

.scan-controls { display: flex; align-items: center; gap: 14px; padding: 12px 14px; flex: none; flex-wrap: wrap; }
.switch { display: flex; align-items: center; gap: 8px; font-size: 13px; color: var(--text-dim); }
.switch input { width: 18px; height: 18px; accent-color: var(--accent); }
.shutter { flex: 1; min-width: 140px; padding: 15px 20px; border: none; border-radius: 12px; cursor: pointer;
  background: linear-gradient(180deg, var(--accent), color-mix(in srgb, var(--accent) 68%, #000));
  color: var(--on-accent); font-size: 15px; font-weight: 700; box-shadow: 0 8px 20px -8px var(--accent-glow); }
.shutter:active { transform: translateY(1px); }
.photo-fallback { flex: 1; text-align: center; padding: 15px 20px; border-radius: 12px; cursor: pointer;
  background: var(--accent-soft); border: 1px solid var(--accent-border); color: var(--chip-a-text); font-weight: 700; }

.scan-results { flex: 1; overflow-y: auto; padding: 4px 14px 30px; display: flex; flex-direction: column; gap: 8px; }
.scan-row { border: 1px solid var(--border); border-radius: 12px; background: var(--surface); padding: 10px 12px; }
.scan-row.review { border-color: color-mix(in srgb, var(--warn) 45%, var(--border)); }
.scan-row.fixed { border-color: var(--accent-border); }
.scan-row-head { display: flex; align-items: center; gap: 11px; }
.scan-pos { flex: none; min-width: 26px; font-size: 13px; font-weight: 700; color: var(--accent); }
.scan-thumb { flex: none; width: 46px; height: 34px; object-fit: cover; border-radius: 6px; }
.scan-row-main { flex: 1; min-width: 0; }
.scan-name { font-size: 14px; font-weight: 600; color: var(--text-strong); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.scan-meta { font-size: 11.5px; color: var(--text-faint); margin-top: 2px; }
.scan-conf { flex: none; font-size: 13px; font-weight: 700; color: var(--text-dim); }
.scan-alts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 9px; padding-top: 9px; border-top: 1px solid var(--border-soft); }
.alt-btn { display: flex; flex-direction: column; align-items: flex-start; gap: 2px; padding: 7px 11px; cursor: pointer;
  border: 1px solid var(--border); border-radius: 9px; background: var(--surface-2); color: var(--text); }
.alt-btn:active { border-color: var(--accent); background: var(--accent-soft); }
.alt-set { font-size: 12px; font-weight: 700; letter-spacing: .04em; }
.alt-price { font-size: 11px; color: var(--text-faint); }

/* ---------- Mobile: das Dashboard wird jetzt auch am Handy benutzt ---------- */
@media (max-width: 720px) {
  .header { gap: 12px; padding: 0 12px; }
  .nav { overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none; }
  .nav::-webkit-scrollbar { display: none; }
  .nav a { white-space: nowrap; }
  /* Narrow: the first half goes and the accented one stays, so the corner still
     has a mark in it — the nav needs the room more than the word does. */
  .brand-name { font-size: 0; }
  .brand-hi { font-size: 15px; }
  .container { padding: 16px 14px 40px; }
  .grid.g-32, .grid.g-11 { grid-template-columns: 1fr; }
  .page-head { flex-direction: column; align-items: flex-start; }
  .col-layout { flex-direction: column; }
  .sidebar { width: auto; border-right: none; border-bottom: 1px solid var(--border-soft); max-height: 44vh; }
  .mainbar { flex-wrap: wrap; height: auto; padding: 10px 14px; gap: 10px; }
  .active-badge { order: -1; }
  .tilegrid { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); }
  .cartrow { flex-wrap: wrap; }
  /* still used by the order detail header */
.stack-lbl { font-size: 12px; color: var(--text-faint); font-weight: 600; }
.stack-detail-head { gap: 14px; }
  .scan-stage { height: 40vh; }
}

.token-row { display: flex; align-items: center; gap: 8px; padding: 10px 14px; flex: none;
  background: var(--surface-2); border-bottom: 1px solid var(--border-soft); font-size: 12px; color: var(--text-faint); }
.token-row input { flex: 1; padding: 8px 10px; background: var(--surface); border: 1px solid var(--border);
  border-radius: 8px; color: var(--text); font-size: 13px; outline: none; }
.token-row button { padding: 8px 13px; border: 1px solid var(--accent-border); border-radius: 8px;
  background: var(--accent-soft); color: var(--chip-a-text); font-size: 12px; font-weight: 700; cursor: pointer; }

.review-done { flex: none; margin: 0; }
.review-done button { padding: 5px 11px; border: 1px solid var(--border); border-radius: 7px;
  background: var(--surface-2); color: var(--text-dim); font-size: 11.5px; font-weight: 600; cursor: pointer; }
.review-done button:hover { border-color: var(--accent-border); color: var(--chip-a-text); background: var(--accent-soft); }

.ph-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 8px; flex-wrap: wrap; }
.ph-legend { display: flex; align-items: center; gap: 6px; font-size: 11px; color: var(--text-dim); }
.ph-dash { width: 14px; height: 0; border-top: 2px dashed var(--warn); }
/* Since when a copy has been on offer. Lives in the dialog: on every tile it
   cost a line per card for something you ask about one card at a time. */
.sale-since { margin-top: 10px; font-size: 11.5px; color: var(--text-faint); }


/* ---------- Bestellungen + Sammelliste ---------- */
.order-actions { display: flex; align-items: center; gap: 10px; }
.import-form { margin: 0; }
.order-progress { flex: none; font-size: 12px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.pick-summary { font-size: 13px; color: var(--text-dim); }
.pick-summary span { font-size: 22px; font-weight: 800; color: var(--accent); }
.pick-done { font-size: 11px; font-weight: 700; color: var(--up); }
.pickrow { gap: 10px; }
.pickrow.picked { opacity: .5; }
.pickrow.picked .cart-name { text-decoration: line-through; }
.pick-pos { flex: none; min-width: 34px; text-align: center; font-size: 14px; font-weight: 700; color: var(--accent); }

/* ---------- Versand ---------- */
.stack-bulk { display: flex; align-items: center; gap: 10px; }
.stack-bulk form { margin: 0; }
.addr-form { display: flex; flex-direction: column; gap: 8px; }
.addr-row { display: flex; gap: 8px; }
.addr-form input, .addr-form select { padding: 8px 10px; background: var(--surface-2);
  border: 1px solid var(--border); border-radius: 8px; color: var(--text); font-size: 13px;
  outline: none; width: 100%; }
.addr-form input:focus, .addr-form select:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.addr-form button { align-self: flex-start; }
.ship-log { margin-top: 12px; border-top: 1px solid var(--border-soft); padding-top: 10px; }
.ship-log-row { display: flex; justify-content: space-between; font-size: 12.5px; padding: 3px 0; }
.ship-card { margin-bottom: 14px; }
.ship-head { display: flex; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  border-bottom: 1px solid var(--border-soft); padding-bottom: 12px; margin-bottom: 12px; }
.ship-order { font-size: 16px; font-weight: 700; color: var(--text-strong); }
.ship-product { text-align: right; }
.ship-body { display: grid; grid-template-columns: 260px 1fr; gap: 20px; }
@media (max-width: 720px) { .ship-body { grid-template-columns: 1fr; } }
.ship-address address { font-style: normal; font-size: 14px; line-height: 1.6; color: var(--text-strong); margin-top: 4px; }
.ship-sender { font-size: 11.5px !important; color: var(--text-faint) !important; }
.ship-list { list-style: none; margin: 6px 0 0; padding: 0; font-size: 13px; }
.ship-list li { display: flex; justify-content: space-between; gap: 12px; padding: 4px 0;
  border-bottom: 1px solid var(--border-soft); }
.ship-list li:last-child { border-bottom: none; }

@media print {
  .header, .no-print { display: none !important; }
  body { background: #fff; color: #000; height: auto; display: block; }
  .scroll { overflow: visible; }
  .container { max-width: none; padding: 0; }
  .panel, .ship-card { background: #fff; border: 1px solid #999; break-inside: avoid;
    page-break-inside: avoid; margin-bottom: 10px; }
  .sdh-l, .cart-meta { color: #444 !important; }
  .ship-order, .ship-address address { color: #000 !important; }
}

.ship-method { display: flex; flex-direction: column; gap: 2px; padding: 8px 11px; margin-bottom: 10px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 9px; font-size: 13px; }

.scan-error { margin: 0 14px 10px; padding: 10px 12px; border-radius: 10px; font-size: 13px;
  color: var(--down); background: color-mix(in srgb, var(--down) 12%, transparent);
  border: 1px solid color-mix(in srgb, var(--down) 40%, transparent); }

.scan-stack select { padding: 7px 9px; background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 8px; color: var(--text); font-size: 13px; outline: none; max-width: 150px; }
.scan-del { flex: none; width: 30px; height: 30px; border-radius: 8px; cursor: pointer;
  border: 1px solid var(--border); background: var(--surface-2); color: var(--text-faint); font-size: 13px; }
.scan-del:active { border-color: var(--down); color: var(--down); }

/* ---------- Scanner-Flow (mehrere Schritte statt einer vollen Seite) ---------- */
.step-head { margin-bottom: 22px; }
.step-label { font-size: 11px; font-weight: 700; letter-spacing: .12em; text-transform: uppercase;
  color: var(--text-faint); }
.step-title { font-size: 24px; font-weight: 800; color: var(--text-strong); margin: 6px 0 0;
  letter-spacing: -.3px; text-wrap: balance; }
.step-sub { font-size: 14px; color: var(--text-dim); margin: 8px 0 0; max-width: 44ch; }
.step-foot { margin-top: 28px; padding-top: 16px; border-top: 1px solid var(--border-soft);
  font-size: 13px; color: var(--text-dim); }

.big-action { display: flex; align-items: center; gap: 14px; padding: 18px;
  border-radius: 14px; border: 1px solid var(--accent-border); background: var(--accent-soft); }
.big-action-icon { flex: none; width: 42px; height: 42px; border-radius: 12px; display: flex;
  align-items: center; justify-content: center; font-size: 22px; font-weight: 700;
  background: var(--accent); color: var(--on-accent); }
.big-action strong { display: block; font-size: 16px; color: var(--text-strong); }
.big-action small { display: block; font-size: 12.5px; color: var(--text-dim); margin-top: 2px; }

.stack-picklist { display: flex; flex-direction: column; gap: 8px; }
.stack-pick { display: flex; align-items: center; gap: 12px; padding: 14px 16px; border-radius: 12px;
  background: var(--surface); border: 1px solid var(--border); }
.stack-pick:active { border-color: var(--accent-border); }
.stack-pick-main { flex: 1; min-width: 0; }
.stack-pick-name { font-size: 15px; font-weight: 600; color: var(--text-strong); }
.stack-pick-meta { font-size: 12.5px; color: var(--text-faint); margin-top: 2px; }
.stack-pick-go { flex: none; font-size: 22px; color: var(--text-faint); }

.step-form { display: flex; flex-direction: column; gap: 16px; max-width: 460px; }
.step-form label { display: flex; flex-direction: column; gap: 6px; }
.step-form label > span { font-size: 12.5px; font-weight: 600; color: var(--text-dim); }
.step-form label small { font-weight: 400; color: var(--text-faint); }
.step-form input { padding: 12px 13px; background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 10px; color: var(--text); font-size: 16px; outline: none; }
.step-form input:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.step-form .shutter { flex: none; margin-top: 4px; }
.token-state { font-size: 13px; color: var(--accent); }

.scan-back { flex: none; width: 34px; height: 34px; border-radius: 9px; display: flex;
  align-items: center; justify-content: center; font-size: 22px; line-height: 1;
  background: var(--surface-2); border: 1px solid var(--border); color: var(--text-dim); }
.scan-stackname { flex: 1; min-width: 0; }
.scan-stackname strong { display: block; font-size: 14px; color: var(--text-strong);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.scan-stackname small { font-size: 11.5px; color: var(--text-faint); }

/* ---------- Verkauf: Preis pro Exemplar, Prozentstufen, Mindestpreis ----------
   The market figure and the asking price are two different numbers, and the
   whole screen exists to hold the difference between them. So the asking price
   is never shown in the same style as the market one. */

.salebox { margin: 4px 0 2px; padding: 12px; border: 1px solid var(--border-soft);
  border-radius: 11px; background: var(--surface-2); }
.sale-line { display: flex; align-items: center; gap: 12px; }
/* a currency symbol that belongs to the field rather than floating beside it */
.moneyin-sym { position: absolute; left: 11px; font-size: 13px; font-weight: 700;
  color: var(--text-faint); pointer-events: none; }
.moneyin .textin { padding-left: 26px; font-weight: 700; }
.sale-delta { flex: none; font-size: 11.5px; font-weight: 700; color: var(--text-faint); }
.sale-delta.up { color: var(--up); }
.sale-delta.down { color: var(--down); }

/* Number fields here are money and counts, never something you nudge one step at
   a time — and the spinner arrows sat right where the eye lands on the figure. */
.textin[type="number"], .ask-num { appearance: textfield; -moz-appearance: textfield; }
.textin[type="number"]::-webkit-outer-spin-button,
.textin[type="number"]::-webkit-inner-spin-button,
.ask-num::-webkit-outer-spin-button,
.ask-num::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* Five fixed steps became a slider: the same move, but any percentage between.
   The ends carry the range, and the amount rides under the thumb — a readout
   parked at the edge belongs to the row, not to the position you are holding. */
.pctpick { display: flex; align-items: center; gap: 10px; margin-top: 12px;
  padding-bottom: 20px; }
.pct-end { flex: none; font-size: 10.5px; font-weight: 600; color: var(--text-faint);
  white-space: nowrap; }
.pct-track { position: relative; flex: 1; min-width: 0; display: flex; align-items: center; }
.pctrange { width: 100%; height: 4px; -webkit-appearance: none; appearance: none;
  border-radius: 999px; cursor: pointer; border: 1px solid var(--border);
  background: var(--surface-2); }
.pctrange::-webkit-slider-thumb { -webkit-appearance: none; appearance: none;
  width: 18px; height: 18px; border-radius: 50%; background: var(--accent);
  border: 2px solid var(--surface); box-shadow: 0 1px 4px rgba(0,0,0,.4); cursor: grab; }
.pctrange::-moz-range-thumb { width: 16px; height: 16px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--surface); cursor: grab; }
.pctrange:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; }

.sale-foot { display: flex; align-items: center; gap: 12px; margin-top: 10px;
  font-size: 11.5px; color: var(--text-faint); }
.sale-warn { color: var(--down); font-weight: 700; }
.cm-link { margin-left: auto; font-weight: 700; color: var(--chip-a-text); }
.cm-link:hover { text-decoration: underline; }

/* Eine Hoehe fuer alles, was in einer Leiste nebeneinander steht. Ein `select`
   bringt auf macOS seine eigene mit und ignoriert `line-height` — es stand zwei
   Pixel hoeher als seine Nachbarn, und zwei Pixel sieht man in einer Reihe. Also
   wird die Hoehe gesagt statt gerechnet, an einer Stelle. */
.ghost-btn { display: inline-flex; align-items: center; white-space: nowrap; height: var(--ctl-h);
  padding: 0 13px; border: 1px solid var(--border);
  border-radius: 8px; background: var(--surface-2); color: var(--text-dim);
  font-size: 12.5px; font-weight: 700; cursor: pointer; }
.ghost-btn:hover { border-color: var(--accent-border); color: var(--chip-a-text); }

/* ---------- Bezugsgrößen: Durchschnitt und CM (Cardmarket) ----------
   Three figures side by side because they disagree, and the disagreement is
   what the sale screen is for. The active one is the yardstick the percentage
   steps below are measured against. */
/* ---------- Verkaufsblock im Dialog ----------
   Three sections instead of one grey box: the price you are setting (with the
   percentage steps, which are just a faster way to fill the same field), the two
   figures you are setting it against, and the facts about the listing. Before,
   all of it sat in one box in no particular order, and the two reference figures
   had two different shapes side by side — one read, one typed — which is what
   made it hard to take in. */
.salebox { margin: 4px 0 2px; padding: 0; border: 0; background: none; }
.sale-sec { padding: 12px 13px; border: 1px solid var(--border-soft);
  border-radius: 11px; background: var(--surface-2); margin-bottom: 8px; }
.sale-head { display: flex; align-items: baseline; gap: 10px; margin-bottom: 8px; }
.sale-title { font-size: 11px; font-weight: 700; letter-spacing: .8px;
  text-transform: uppercase; color: var(--text-dim); }

/* The price field fills its section. It used to be an inline-flex with `flex: 1`
   in a block parent, where that does nothing — so it sat there at the browser's
   default input width with a full-width row of percentage buttons under it. */
.moneyin { position: relative; display: flex; align-items: center; width: 100%; }
.moneyin.big .textin { font-size: 22px; padding: 12px 12px 12px 32px; }
.moneyin.big .moneyin-sym { left: 13px; font-size: 16px; }

/* Two cards of the same shape. Two *different* shapes side by side — one read,
   one typed — is what made the earlier version unreadable, so the typed figure
   simply carries an input where the other carries text. */
/* two or three, depending on whether a floor has been set */
/* Gleich breit und gleich hoch: drei Kaesten, die dieselbe Art Frage
   beantworten, duerfen nicht verschieden gross sein — der groesste sieht sonst
   aus wie der wichtigste. `1fr` statt auto-fit haelt die Breite gleich,
   `stretch` die Hoehe, und die Unterzeile steht in jedem Kasten (leer, wo es
   nichts zu sagen gibt), damit keiner um ihre Zeile schrumpft. */
/* As many equal columns as there are cards, not a fixed three. The floor is only
   a yardstick once one is set, so without it there are two — and on a fixed grid
   those sat in the left two thirds with a hole beside them, which reads as a card
   that failed to load rather than one that does not apply. */
.basisgrid { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr;
  gap: 8px; align-items: stretch; }
.basiscard { display: flex; flex-direction: column; gap: 7px; padding: 11px 11px 10px;
  border: 1px solid var(--border); border-radius: 10px; background: var(--surface);
  cursor: pointer; transition: border-color .12s ease, background .12s ease; }
.basiscard input[type="radio"] { position: absolute; opacity: 0; width: 0; height: 0; }
.basiscard:hover { border-color: var(--accent-border); }
/* The active one is not tinted. CM is the default basis, so its card would carry
   an accent border and a coloured background permanently — which reads as "this
   number is special" when the only thing it is, is the one being measured
   against. The small "Basis" tag says that, and nothing else needs to. */
/* nothing to measure against yet — visible, so its absence is a fact, not a gap */
.basiscard.empty { cursor: default; }
.basiscard.empty:hover { border-color: var(--border); }
.bc-top { display: flex; align-items: center; gap: 6px; }
.bc-lab { flex: 1; min-width: 0; font-size: 11px; font-weight: 700; letter-spacing: .5px;
  text-transform: uppercase; color: var(--text-faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bc-on { flex: none; font-size: 9.5px; font-weight: 700; letter-spacing: .5px;
  text-transform: uppercase; padding: 1px 6px; border-radius: 5px;
  background: var(--surface-2); color: var(--text-dim); }
.bc-val { padding: 3px 0; font-size: 19px; font-weight: 700; color: var(--text-strong);
  font-variant-numeric: tabular-nums; }
/* How old the typed figure is, in the label row: a mark, not a sentence. Amber
   past a fortnight — such an anchor is not wrong, it is old, and those call for
   different reactions. */
/* The typed figure reads exactly like the one beside it — same size, same
   weight, same digits — because they are the same kind of number and a smaller
   one would look like a lesser one. What it does *not* carry is a border or a
   hover: it is a number you can click into, not a form to fill in. Marked only
   while it actually has focus. */
.basiscard .moneyin .textin { padding: 3px 6px 3px 19px; font-size: 19px;
  font-weight: 700; color: var(--text-strong); font-variant-numeric: tabular-nums;
  border-color: transparent; background: transparent; }
.basiscard .moneyin .textin:focus { border-color: transparent; box-shadow: none; }
/* the € reads as part of the figure, like the one on the card beside it */
.basiscard .moneyin-sym { left: 2px; font-size: 14px; font-weight: 700;
  color: var(--text-strong); }
/* Only drawn when there is one, so a card without an alternative grade stays
   the height it needs — reserving the line made every box taller for a fact most
   of them do not have. */
.bc-sub { font-size: 10.5px; color: var(--text-faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.basiscard.on .bc-sub { color: var(--chip-a-text); }
/* Two marks on the CM card: where the figure comes from, and fetch it again.
   Sized to be hit, not to be tidy — they are the two things you press while
   pricing, and as 12px specks in a corner they were a game of darts. The box is
   24px square with the row pulled back around it, so the target is the size of a
   fingertip without the label row growing to match. */
.bc-go { flex: none; width: 24px; height: 24px; margin: -5px 0; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: none; background: none; cursor: pointer;
  color: var(--text-faint); font-size: 17px; line-height: 1; text-decoration: none; }
/* The refresh sign is an SVG rather than the ⟳ character, so that it can turn on
   its own axis: a glyph's ink sits above the middle of its box, and rotating the
   box swung it round that middle instead. The ring is centred on the viewBox, and
   the box is centred in the button, so the two centres are the same point. */
.bc-go.rf svg { display: block; }
/* It opens a browser tab and waits out a security check, so several seconds pass
   with nothing on screen moving — long enough to press it again. htmx hangs
   `htmx-request` on the element for exactly the life of the request, so the sign
   turns while its own answer is on the way and stops when the dialog comes back.
   The symbol is already a circle; it only had to be told to go round.

   Slowly, and without changing colour. Its job is to say "still going", not to
   ask for attention — a fast spin next to a price you are reading is something
   you end up waiting out rather than ignoring. */
.bc-go.rf.htmx-request { animation: bcSpin 2s linear infinite;
  pointer-events: none; }
@keyframes bcSpin { to { transform: rotate(360deg); } }
.bc-go:hover:not(:disabled) { color: var(--chip-a-text); }
.bc-go:disabled { opacity: .35; cursor: not-allowed; }
.bc-days { flex: none; padding: 1px 5px; border-radius: 5px; background: var(--surface-2);
  font-size: 9.5px; font-weight: 700; color: var(--text-faint);
  font-variant-numeric: tabular-nums; }
.bc-days.stale { background: color-mix(in srgb, var(--warn) 16%, transparent); color: var(--warn); }


/* The one thing the sale dialog *does*, up in its header beside the close mark.
   Everything under it is a number you read; a button sitting among those read
   as one more fact until you noticed it had a border.

   Both directions wear the same quiet coat. They never appear together — a copy
   is either on offer or it is not — so an accent on one of them would not be
   distinguishing anything; it would only make whichever you were looking at
   today seem like the pressing thing to do. */
.head-act { flex: none; margin-left: auto; padding: 7px 11px; border-radius: 9px;
  border: 1px solid var(--border); background: var(--surface-2);
  color: var(--text-dim); font-family: inherit; font-size: 12px;
  font-weight: 600; cursor: pointer; white-space: nowrap; }
.head-act:hover { border-color: var(--accent-border); color: var(--text-strong); }



/* ---------- Preise holen: der eine langsame Knopf ---------- */
/* Minimised: the dialog gets out of the way but keeps polling, so a run can
   finish while you look at something else. Only a class on the frame — the
   fragment inside carries on either way. */
.sync-bar { display: flex; align-items: center; gap: 10px; }
.syncpill { display: none; position: fixed; right: 20px; bottom: 20px; z-index: 60;
  align-items: center; gap: 8px; padding: 9px 14px; border-radius: 999px;
  border: 1px solid var(--accent-border); background: var(--surface);
  color: var(--text-strong); font-size: 12.5px; font-weight: 700; cursor: pointer;
  box-shadow: 0 14px 30px -12px rgba(0,0,0,.6); }
.syncpill-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--accent);
  animation: livePulse 1.8s ease-out infinite; }
.syncwrap.mini { background: none; backdrop-filter: none; pointer-events: none; }
.syncwrap.mini > .askbox { display: none; }
.syncwrap.mini > .syncpill { display: inline-flex; pointer-events: auto; }

/* the run, while it happens */
.sync { margin-top: 14px; }
.sync-head { display: flex; align-items: baseline; margin-bottom: 6px; }
.sync-count { font-size: 13px; font-weight: 700; color: var(--text-strong);
  font-variant-numeric: tabular-nums; }
.sync-ok { font-size: 12px; font-weight: 700; color: var(--chip-a-text); }
.sync-track { height: 5px; border-radius: 999px; background: var(--surface-2);
  border: 1px solid var(--border); overflow: hidden; }
.sync-fill { height: 100%; background: var(--accent); transition: width .3s ease; }
.sync-list { margin-top: 12px; max-height: 40vh; }
.sync-bad { font-size: 12.5px; font-weight: 600; color: var(--down); }
/* what came back when it was not a price — same column, so the eye runs down one
   edge instead of hunting for two kinds of answer in two places */
.exp-price.note { font-weight: 600; color: var(--text-faint); }
.exp-go:disabled { opacity: .5; cursor: default; }

/* the report is a dialog now — the toolbar is not where you look after a
   minute of waiting, and a line there has no room to name the cards that failed */
.fetch-hint { margin-top: 8px; font-size: 12px; color: var(--text-dim);
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: 8px; padding: 6px 9px; font-family: ui-monospace, monospace; }
.fetch-note { margin-top: 8px; font-size: 11.5px; color: var(--text-faint); }
/* The spinner that used to sit here is gone with the run it described: the
   request now returns at once and the dialog shows the progress. */

/* Die naechstbessere Kondition: eine Zeile, kein Absatz. Sie steht unter den
   Vergleichskarten, weil sie zu ihnen gehoert — aber leiser, weil sie nichts
   entscheidet. */


/* Ein Knopf, der gerade nicht kann, sieht auch so aus — und faengt keine Klicks. */
.ghost-btn:disabled { opacity: .45; cursor: not-allowed; }
.ghost-btn:disabled:hover { border-color: var(--border); color: var(--text-dim); }

/* Im Verkauf: der Preis ist veroeffentlicht, also ist hier nichts mehr zu
   entscheiden. Ausgegraut statt versteckt — nachschlagen soll man koennen, was
   man verlangt hat, nur eben nichts mehr daran drehen. */
.salebox.frozen { opacity: .55; }
.salebox.frozen .textin,
.salebox.frozen .basiscard { cursor: not-allowed; }
.salebox.frozen .pctpick { pointer-events: none; }

/* Drei Paare, je zwei nebeneinander. Zusammengehoerendes steht nebeneinander,
   statt dass man beim Vergleichen ueber die Spalte springt. */
.statpairs { display: grid; grid-template-columns: 1fr 1fr; gap: 0 14px; }
/* Set spans both columns so it keeps its own line without a grid of its own —
   two grids stacked left a gap between the set and the grade that read as a
   section break. */
.strow.wide { grid-column: 1 / -1; }
/* the way into the pile, on the pile's own name */
.with-go { display: flex; align-items: baseline; gap: 6px; }
.gosym { flex: none; color: var(--text-faint); font-size: 12px; text-decoration: none; }
.gosym:hover { color: var(--chip-a-text); }

/* --- Bestellungen ---------------------------------------------------------
 *
 * Zwei Listen und eine Sammelliste. Die Sammelliste ist der Bildschirm, vor dem
 * man wirklich steht — mit einer Kiste in der Hand —, also fuehrt sie die
 * Platznummer gross und links, und alles andere ist nur Bestaetigung, die
 * richtige Karte erwischt zu haben.
 */
.ordpanel { padding: 14px 16px 10px; margin-bottom: 14px; }
.panel-note { font-size: 12px; color: var(--text-faint); }
.ordlist { display: flex; flex-direction: column; }

/* Aufklappbare Bestellungen. <details> statt eigener Klapp-Logik: ohne
   Javascript, mit der Tastatur bedienbar, und der Browser kuemmert sich um
   den Zustand. */
/* Luft zwischen Reiterleiste und erster Bestellung — buendig gelesen
   sieht die Liste aus, als haenge sie am Strich. */
.ordacc { display: flex; flex-direction: column; gap: 8px; margin-top: 16px; }
.ordcard { border: 1px solid var(--border-soft); border-radius: 12px;
  background: var(--surface); overflow: hidden; }
.ordcard[open] { border-color: var(--border); }
.ordsum { display: grid; grid-template-columns: 14px auto 1fr auto auto auto auto auto;
  align-items: center; gap: 14px; padding: 11px 14px; cursor: pointer;
  list-style: none; }
.ordsum::-webkit-details-marker { display: none; }
/* Kein flaechiges Einfaerben: die Zeile fuehrt nirgendwohin, sie klappt
   auf. Ein Hover wie bei einem Link versprach einen Sprung, den es nicht
   gibt — es genuegt, dass das Dreieck reagiert. */
.ordsum:hover .ord-caret { border-left-color: var(--accent); }
/* Dasselbe Zeichen wie ueber den Stapelbloecken (`.pilesec-mark`) — es ist
   dieselbe Geste, und zwei verschiedene Marken fuer "hier klappt etwas auf"
   waren zwei Schirme weit auseinander gerade noch zu uebersehen. */
.ord-caret { flex: none; width: 7px; height: 7px; border: solid var(--text-faint);
  border-width: 0 1.6px 1.6px 0; transform: rotate(-45deg) translate(-1px, -1px);
  transition: transform .15s ease; }
.ordcard[open] .ord-caret { transform: rotate(45deg) translate(-2px, -2px); }
.ordsum:hover .ord-caret { border-color: var(--text-dim); }
.ordbody { border-top: 1px solid var(--border-soft); padding: 4px 16px 14px; }
/* Die ganze Zeile hakt ab — also ein Knopf, der aussieht wie eine Zeile.
   Trennstriche dazwischen, sonst verschwimmen zwei Karten mit aehnlichem Namen
   ineinander. */
.orditem-form { margin: 0; border-radius: 8px; }
/* Die ganze Zeile faerbt sich, nicht nur ein Teil davon: das Formular traegt
   den Ton, damit auch die Raender links und rechts vom Knopf mitgehen. */
.orditem-form:hover { background: var(--accent-soft); }
/* Bei einer versendeten Bestellung nicht: dort ist nichts mehr zu holen, und
   eine Zeile, die auf den Zeiger reagiert, verspricht eine Handlung, die es
   nicht gibt. */
.ordcard.done .orditem-form:hover { background: none; }
.ordcard.done .orditem { cursor: default; }
.ordcard.done .orditem-form:hover .placetag { background: var(--surface-2);
  border-color: var(--border-soft); }
/* `--border-soft` auf dem Kastenhintergrund ist praktisch unsichtbar — die
   Linie war da und trotzdem nicht zu sehen. Eine Spur dunkler, und sie trennt
   auch wirklich. */
.orditem-form + .orditem-form .orditem { border-top: 1px solid var(--border); }
.orditem { display: grid; grid-template-columns: minmax(120px, auto) 40px 1fr auto;
  align-items: center; gap: 14px; padding: 9px 4px; width: 100%;
  background: none; border: 0; border-radius: 8px; cursor: pointer;
  text-align: left; font: inherit; color: inherit; }

/* Nur dort durchgestrichen, wo noch gesammelt wird. In einer versendeten
   Bestellung ist "geholt" kein Zwischenstand mehr, sondern Vergangenheit — und
   eine durchgestrichene Liste liest sich wie storniert. */
.ordcard.picking .orditem.picked { opacity: .45; }
.ordcard.picking .orditem.picked .oi-name { text-decoration: line-through; }
/* Wo sie liegt, steht vorn — das ist die Frage, mit der man die Zeile ansieht. */
.oi-where { display: flex; min-width: 0; }
/* Stapel und Platz sind *eine* Auskunft — wohin greifen —, also ein Schild und
   nicht zwei Angaben nebeneinander. Die Nummer traegt den Ton, der Name sagt
   nur, in welcher Kiste. */
.placetag { display: inline-flex; align-items: center; gap: 8px; min-width: 0;
  padding: 3px 8px 3px 9px; border-radius: 8px; text-decoration: none;
  background: var(--surface-2); border: 1px solid var(--border-soft); }
/* Der Pfeil ist die Schaltflaeche, nicht der Name. Als ganzer Knopf traf man
   den Namen beim Lesen und sprang aus der Liste. */
.oi-where { display: flex; align-items: center; gap: 6px; min-width: 0; }
.oi-where .gosym { flex: none; font-size: 13px; line-height: 1; text-decoration: none;
  color: var(--text-faint); padding: 3px 4px; border-radius: 6px; }
.oi-where .gosym { transition: color .1s ease, transform .1s ease; }
/* Beim Ueberfahren dicker und dunkler, nicht farbig: eine Farbe sagt "Achtung"
   oder "Zustand", und beides trifft hier nicht zu — der Pfeil wird nur
   greifbarer. Kein Kaestchen, das saehe aus wie ein zweiter Knopf neben dem,
   auf dem man ohnehin steht. */
.oi-where .gosym:hover { color: var(--text-strong);
  -webkit-text-stroke: .7px currentColor; transform: scale(1.12); }
.pt-stack { font-size: 11px; color: var(--text-dim); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; max-width: 120px; }
.pt-pos { font-size: 15px; font-weight: 800; color: var(--text-strong);
  font-variant-numeric: tabular-nums; }
.placetag.none { color: var(--warn); font-size: 11px; border-style: dashed; }
/* Das Schild traegt einen eigenen Hintergrund und Rahmen, und beides lag beim
   Ueberfahren wie ein Fleck ueber dem getoenten Rest der Zeile — als waere genau
   dieses Stueck ausgenommen. Beim Hover verschwindet beides, dann faerbt sich
   die Zeile wirklich in einem Stueck. */
.orditem-form:hover .placetag { background: transparent; border-color: transparent; }
/* `position: relative` ist hier keine Kosmetik: `.art-fill` — der
   Ersatzverlauf fuer Karten ohne Bild — ist `position: absolute;
   inset: 0` und sucht sich den naechsten positionierten Vorfahren.
   Ohne diese Zeile war das der Viewport, und ein einziger Eintrag
   ohne Kartenbild legte einen Farbverlauf ueber die ganze Seite.
   Der Text stand im DOM, die Kaesten hatten ihre Groesse — es war
   nur alles zugedeckt. */
.oi-art { position: relative; width: 40px; height: 30px; border-radius: 5px; overflow: hidden;
  display: block; background: var(--surface-2); }
.oi-art img, .oi-art .art-fill { width: 100%; height: 100%; object-fit: cover; display: block; }
.oi-main { display: flex; flex-direction: column; min-width: 0; }
.oi-name { font-weight: 600; color: var(--text-strong); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.oi-meta { font-size: 11px; color: var(--text-faint); }
.oi-price { font-variant-numeric: tabular-nums; font-size: 13px;
  color: var(--text-dim); padding-right: 10px; }
/* Anschrift und Versand stehen in der Zeile selbst — eine eigene Seite dafuer
   hiess: Liste verlassen, nachsehen, zurueck, Platz suchen, je Bestellung. */
.ordtop { display: grid; grid-template-columns: 1fr 1fr; gap: 12px;
  padding: 0; align-items: stretch; }
/* Steht nur ein Kasten da — unbezahlt, also noch ohne Anschrift —, bekommt er
   die Breite ganz. Eine halbe Zeile mit Luft daneben sieht aus, als fehle dort
   etwas; genau das soll sie nicht sagen. */
.ordtop.solo { grid-template-columns: 1fr; }
/* Was in einem Kasten fehlt und weitergeht — dieselbe Warnfarbe wie sonst, aber
   in der Groesse der Zeile, in der es steht: es ist kein Alarm, sondern der
   naechste Schritt. */
.ordmiss { font-size: 12px; color: var(--warn); }
/* Nur ein Traeger fuer den Hovertext eines gesperrten Knopfes. Er steht in der
   Leiste wie ein Knopf und bekommt deshalb auch keinen eigenen Rand: die Leiste
   hat `gap: 14px`, und jeder zusaetzliche Rand macht aus einem Abstand zwei.
   Genau daran krankte die Leiste schon einmal, als die Knoepfe darin sich
   ausserdem noch selbst einen Rand nach rechts gaben. */
.btnwrap { display: inline-flex; }
/* `min-width: 0`, weil ein Grid-Kind sonst so breit wird wie sein laengster
   Inhalt — hier die laengste Versandart — und aus dem Kasten herauslaeuft. */
/* Beide Kaesten gleich hoch, und der Inhalt verteilt sich darin, statt oben zu
   kleben: das Versandfeld hat weniger Zeilen als eine Anschrift, und ein halb
   leerer Kasten neben einem vollen sieht aus wie ein Ladefehler. */
.ordbox { border: 1px solid var(--border-soft); border-radius: 10px;
  padding: 12px 14px; background: var(--surface-2); min-width: 0;
  display: flex; flex-direction: column; }
.ordbox > .addr, .ordbox > .statpairs { flex: 1 1 auto; }
.ordbox .trackform { margin-top: auto; padding-top: 10px; }
.ordbox-head { display: flex; align-items: center; gap: 8px; margin-bottom: 8px;
  min-height: 28px; }
.ordbox-title { font-size: 12px; font-weight: 700; color: var(--text-dim); }
/* `.symbtn` ist ein 32x32-Symbolknopf mit fester Breite — gedacht fuer ein
   Zeichen, nicht fuer ein Wort. Beschriftete Knoepfe nehmen `.ghost-btn`
   (aussen liniert) oder `.primary-btn` (gefuellt) — dieselben zwei, die der Rest
   des Dashboards benutzt. Er hiess `.btn-ghost`, war also von `.ghost-btn` nur
   durch die Reihenfolge zu unterscheiden: zwei verschiedene Knoepfe, die gleich
   heissen. `.tiny` bleibt fuer die kleinen Textknoepfe, die es schon gab. */
.symbtn.tiny { width: auto; height: auto; padding: 2px 8px; font-size: 11px; }
/* In der Ecke ihres Kastens, wo ein Symbol nicht mit dem Inhalt konkurriert. */
.symbtn.boxcorner { display: inline-flex; align-items: center;
  justify-content: center; width: 28px; height: 28px; margin-left: auto; }
.ordnote { padding: 10px 0; font-size: 12px; color: var(--text-faint); }
.ordcards { display: flex; flex-direction: column; }
.ordship { display: flex; justify-content: flex-end; padding: 10px 0 4px; }
/* Die laufende Nummer ist eine Kennung, kein Zustand — sie traegt deshalb keine
   Akzentfarbe mehr. Die ist in diesem System dem vorbehalten, was gerade gewaehlt
   oder faellig ist, und ein Schild in Ember an jeder Zeile machte jede Bestellung
   gleich dringlich. */
.ord-mark { font-size: 11.5px; font-weight: 700; color: var(--text-faint);
  background: var(--surface-2); border: 1px solid var(--border-soft);
  border-radius: 6px; padding: 1px 7px; font-variant-numeric: tabular-nums; }

@media (max-width: 780px) { .ordtop { grid-template-columns: 1fr; } }

.ordrow { display: grid; grid-template-columns: auto 1fr auto auto auto auto;
  align-items: center; gap: 12px; padding: 10px 4px; text-decoration: none;
  color: var(--text); border-top: 1px solid var(--border-soft); }
.ordrow:first-child { border-top: 0; }
.ordrow:hover { background: var(--accent-soft); }
/* Dieselbe Tonleiter wie jede andere Liste im Dashboard (`.row-name`,
   `.row-sub`, `.row-mid`, `.row-end`): 13px fuer den Namen, 11,5px fuer das,
   was darunter oder daneben steht, 12,5px fuer die Zahlenpaare. Die Zeile hatte
   ihre eigenen Groessen, und nebeneinander sah eine Bestellung eine Spur groesser
   aus als eine Karte, ohne dass es etwas bedeutet haette. */
.ord-no { font-variant-numeric: tabular-nums; font-size: 11.5px; color: var(--text-faint); }
.ord-buyer { font-size: 13px; font-weight: 600; color: var(--text-strong);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.ord-items, .ord-prog { font-size: 12.5px; font-weight: 700; color: var(--text-dim);
  font-variant-numeric: tabular-nums; white-space: nowrap; }
.ord-prog.done { color: var(--up); }
.ord-warn { font-size: 11px; font-weight: 700; padding: 1px 7px; border-radius: 20px;
  background: color-mix(in srgb, var(--warn) 14%, transparent); color: var(--warn); }
/* Ein Zeichen statt eines Satzes: dass etwas nicht stimmt, soll man ueber eine
   Liste hinweg sehen, ohne zu lesen. Was genau, sagt der Hovertext. */
.panel-title .ord-bang { margin-right: 6px; }
.ord-bang { display: inline-flex; align-items: center; justify-content: center;
  width: 20px; height: 20px; border-radius: 50%; font-size: 12px; font-weight: 800;
  color: var(--down); border: 1.5px solid var(--down); cursor: default;
  background: color-mix(in srgb, var(--down) 10%, transparent); }
.ord-sum { font-size: 13px; font-variant-numeric: tabular-nums; font-weight: 700;
  color: var(--text-strong); }

.ord-head { display: flex; align-items: center; gap: 22px; flex-wrap: wrap;
  padding: 14px 18px; margin: 12px 0 14px; border: 1px solid var(--border-soft);
  border-radius: 12px; background: var(--surface); }
.ord-head-id { display: flex; flex-direction: column; }
.ord-head-do { margin-left: auto; }
.paystate { font-size: 12px; font-weight: 700; padding: 2px 9px; border-radius: 20px;
  background: var(--surface-2); color: var(--text-dim); }
.paystate.on { background: var(--accent-soft); color: var(--chip-a-text); }

/* Die Anschrift wird abgeschrieben oder kopiert — also als Block, mit dem
   Zeilenumbruch, den ein Briefumschlag braucht, und nicht als Fliesstext. */
.addrpanel { padding: 14px 16px; margin-bottom: 14px; }
.addr { font-style: normal; white-space: pre-line; line-height: 1.5;
  font-size: 15px; color: var(--text-strong); }
.ord-cards-head { margin: 18px 2px 8px; }

.pickgroup { padding: 12px 14px 8px; margin-bottom: 12px; }
.picklist { display: flex; flex-direction: column; }
.pickrow { display: grid; align-items: center; gap: 14px;
  grid-template-columns: 44px 42px 40px 1fr auto auto;
  padding: 10px 2px; border-top: 1px solid var(--border-soft); }
.pickrow:first-child { border-top: 0; }
.pickrow.picked { opacity: .42; }
.pickrow.picked .pick-name { text-decoration: line-through; }
/* Die Zahl, die du suchst, waehrend du die Karten durchblaetterst. */
.pick-pos { font-size: 20px; font-weight: 700; text-align: right;
  font-variant-numeric: tabular-nums; color: var(--text-strong); }
/* `position: relative` ist hier keine Kosmetik: `.art-fill` — der
   Ersatzverlauf fuer Karten ohne Bild — ist `position: absolute;
   inset: 0` und sucht sich den naechsten positionierten Vorfahren.
   Ohne diese Zeile war das der Viewport, und ein einziger Eintrag
   ohne Kartenbild legte einen Farbverlauf ueber die ganze Seite.
   Der Text stand im DOM, die Kaesten hatten ihre Groesse — es war
   nur alles zugedeckt. */
.pick-art { position: relative; width: 40px; height: 30px; border-radius: 5px; overflow: hidden;
  display: block; background: var(--surface-2); }
.pick-art img, .pick-art .art-fill { width: 100%; height: 100%; object-fit: cover;
  display: block; }
.pick-main { display: flex; flex-direction: column; min-width: 0; }
.pick-name { font-weight: 600; color: var(--text-strong); overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.pick-meta { font-size: 11px; color: var(--text-faint); }
.pick-price { font-size: 13px; font-variant-numeric: tabular-nums; color: var(--text-dim); }
/* In welchen Umschlag die Karte danach gehoert. Ein Buchstabe findet das Auge,
   zehnstellige Bestellnummern nicht. */
.pick-mark { display: inline-flex; align-items: center; justify-content: center;
  min-width: 22px; height: 22px; padding: 0 5px; border-radius: 6px;
  background: var(--accent-soft); border: 1px solid var(--accent-border);
  color: var(--chip-a-text); font-size: 12px; font-weight: 800; }
.pick-legend { display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  margin-bottom: 12px; }
.legend-item { display: inline-flex; align-items: center; gap: 7px; padding: 5px 10px 5px 6px;
  border: 1px solid var(--border-soft); border-radius: 20px; background: var(--surface);
  text-decoration: none; color: var(--text); font-size: 12px; }
.legend-item:hover { border-color: var(--accent-border); }
.legend-who { font-weight: 600; color: var(--text-strong); }
.legend-n { color: var(--text-faint); font-variant-numeric: tabular-nums; }
/* Abhaken ist eine Handbewegung, keine Entscheidung — also ein Kaestchen,
   kein beschrifteter Knopf. */
.pick-tick { width: 26px; height: 26px; border-radius: 7px; cursor: pointer;
  border: 1.5px solid var(--border); background: var(--surface); padding: 0; }
/* Beim Ueberfahren wie der Knopf daneben: derselbe Rahmen, derselbe Ton. Zwei
   Bedienelemente in einer Zeile, die sich verschieden anfuehlen, lesen sich wie
   zwei verschiedene Arten von Handlung. */
.pick-tick:hover { border-color: var(--accent-border);
  background: var(--accent-soft); }
/* Ein gezeichneter Haken, kein diagonaler Streifen: der Verlauf sah aus wie ein
   durchgestrichenes Feld, also nach Fehler statt nach erledigt. */
.pick-tick.on { background: var(--accent); border-color: var(--accent);
  position: relative; }
.pick-tick.on::after { content: ''; position: absolute; left: 8px; top: 4px;
  width: 6px; height: 11px; border: solid var(--on-accent);
  border-width: 0 2px 2px 0; transform: rotate(43deg); }
/* Kein Zusatzrand mehr. Die Leiste hat eine Luecke (`gap: 14px`), und wer
   einzelnen Knoepfen noch 14 px dazugibt, hat zwei Abstaende in einer Reihe:
   zwischen Suche und „Alle vormerken" 14, dahinter 28. Man sieht es sofort und
   findet nie, woran es liegt. */
/* Ganz fett und in der Textfarbe: es ist die Zahl, die man beim Durchgehen im
   Auge behaelt, nicht eine Randnotiz. */
.pick-summary { margin-left: auto; font-size: 13px; font-weight: 800;
  color: var(--text-strong); font-variant-numeric: tabular-nums; }
/* Auswahl oben, Knopf darunter rechts. Nebeneinander gedraengt schnitt der
   laengste Versandname dem Knopf die Beschriftung ab. */
.ship-pick { display: flex; flex-wrap: wrap; gap: 10px; width: 100%; }
/* Dasselbe Auswahlfeld wie in der Sortierleiste — ein Schirm, eine Sprache. */
.ship-pick select { flex: 1 1 100%; min-width: 0; background: var(--surface);
  border: 1px solid var(--border); border-radius: 8px; color: var(--text);
  font-size: 13px; padding: 8px 10px; outline: none; }
.ship-pick select:focus { border-color: var(--accent-border); }
.ship-pick button { flex: 0 0 auto; margin-left: auto; white-space: nowrap; }
.stalebar { padding: 7px 14px; text-align: center; font-size: 12px; font-weight: 600;
  background: color-mix(in srgb, var(--warn) 18%, transparent); color: var(--warn);
  border-bottom: 1px solid color-mix(in srgb, var(--warn) 35%, transparent); }

/* Ein Preis, der nicht gesetzt werden kann, wird ausgegraut — nicht mit einem
   Satz erklaert. Eine eingestellte Karte ist bei Cardmarket bepreist; ohne
   angemeldeten Browser fuehrt von hier kein Weg dorthin, und das sieht man dem
   Block dann an. Die Zahlen bleiben lesbar, nur eben erkennbar unantastbar. */
.salebox.locked { opacity: .5; }
.salebox.locked .textin,
.salebox.locked .pctrange,
.salebox.locked .bcard { pointer-events: none; }

/* --- Bestellzeile, aufgeklappt -------------------------------------------- */
/* Der Inhalt sass ohne Luft am Trennstrich. Ein Kasten, der die Kante beruehrt,
   sieht aus, als sei er hineingerutscht. */
.ordbody { padding: 16px !important; display: flex; flex-direction: column; gap: 12px; }
.ordtop { padding: 0; }
.cardsbox { padding: 12px 14px; }
.cardsbox .ordcards { margin: 0 -2px; }

/* Versandart: sie steht fest, sie wird nicht gewaehlt. Also eine Angabe. */
/* Die Verfolgung ist keine Fussnote hinter dem Brieftyp — sie entscheidet, ob
   man dem Brief hinterhersehen kann. Eigene Zeile, eigene Groesse. */
.shipway { display: flex; flex-direction: column; gap: 2px; margin-bottom: 8px; }
.cardcount { margin-left: auto; font-size: 11px; color: var(--text-faint);
  font-variant-numeric: tabular-nums; }
.shipway-name { font-size: 15px; font-weight: 700; color: var(--text-strong); }
.shipway-track { font-size: 12px; color: var(--text-faint); }
.shipway-track.yes { color: var(--chip-a-text); font-weight: 600; }
.trackform { display: flex; gap: 8px; margin-top: 10px; }
.trackform input { flex: 1 1 0; min-width: 0; background: var(--surface);
  border: 1px solid var(--border); border-radius: 8px; color: var(--text);
  font-size: 13px; padding: 8px 10px; outline: none;
  font-variant-numeric: tabular-nums; }
.trackform input:focus { border-color: var(--accent-border); }
.trackform button { flex: 0 0 auto; }
.statpairs.tight .strow { padding: 3px 0; }

/* Der Knopf dieser Zeile links, wie "Verkaufen" im Kartendialog. */
/* `.head-act` traegt `margin-left: auto` — im Kopf des Kartendialogs schiebt
   das den Knopf ans Ende. Hier steht er allein und soll links stehen, also wird
   der Automatik-Rand zurueckgenommen. */
/* Rechts unten: die Handlung steht am Ende dessen, was man gelesen hat. */
.ordship { display: flex; justify-content: flex-end; padding: 0 16px 16px; }

/* Kopieren: Zeichen, und nach dem Klick kurz ein Haken. Ohne Rueckmeldung
   weiss man nicht, ob etwas passiert ist — der Knopf sieht davor und danach
   gleich aus, und die Zwischenablage zeigt sich nicht von selbst. */
.symbtn.boxcorner { margin-left: auto; }
.symbtn .ic-done { display: none; }
.symbtn.copied .ic-copy { display: none; }
.symbtn.copied .ic-done { display: block; }
.symbtn.copied { color: var(--accent); border-color: var(--accent-border); }
.symbtn.copyfail { color: var(--down); border-color: var(--down); }


/* ---------- Kuchen: woraus der Wert besteht ----------
   Ein conic-gradient statt SVG-Boegen. Die Anteile rechnet der Server als
   Umdrehungen aus, die Farben sind Mischungen der Akzentfarbe mit der Flaeche
   darunter — damit dreht der Kuchen beide Themen mit und braucht keine eigene
   Palette, die man beim naechsten Umbau vergessen wuerde. */
.pie-wrap { display: flex; align-items: center; gap: 28px; flex-wrap: wrap; }
.pie { flex: none; width: 168px; height: 168px; border-radius: 50%;
  box-shadow: inset 0 0 0 1px var(--border-soft); }
/* Baut sich im Uhrzeigersinn auf, sobald er ins Bild kommt: eine Maske aus einem
   zweiten conic-gradient, deren Winkel wandert. Die Farbstopps des Kuchens
   selbst lassen sich nicht ueberblenden, ein per `@property` registrierter
   Winkel schon.

   Verdeckt wird **nur unter `.growing`**, und diese Klasse setzt das Skript.
   Zuerst stand hier `.pie[data-grow] { --sweep: 0deg }`, also im Stylesheet
   selbst — damit war der Kuchen unsichtbar, bis Javascript ihn freigab, und
   blieb es fuer immer, wenn irgendein Glied der Kette fehlte. Genau das ist
   passiert. Eine Animation darf das, was sie zeigt, nicht als Geisel nehmen. */
@property --sweep { syntax: '<angle>'; inherits: false; initial-value: 360deg; }
.pie.growing { --sweep: 0deg;
  -webkit-mask-image: conic-gradient(#000 var(--sweep), transparent 0);
  mask-image: conic-gradient(#000 var(--sweep), transparent 0); }
.pie.growing.grown { --sweep: 360deg;
  transition: --sweep 1.1s cubic-bezier(.22,.61,.36,1); }
@media (prefers-reduced-motion: reduce) {
  .pie.growing { --sweep: 360deg; }
  .pie.growing.grown { transition: none; }
}
/* Der Ring: die Mitte ist ausgestanzt, weil ein voller Kreis die kleinen Stuecke
   zu Splittern macht und der Rand das ist, was man vergleicht. */
.pie::after { content: ''; display: block; width: 46%; height: 46%; margin: 27% auto;
  border-radius: 50%; background: var(--surface); }
.pie-legend { flex: 1; min-width: 240px; display: flex; flex-direction: column; gap: 2px; }
.pie-row { display: flex; align-items: center; gap: 10px; padding: 5px 2px;
  border-bottom: 1px solid var(--border-soft); }
.pie-row:last-child { border-bottom: none; }

/* Eine Zeile, die in ihr Spiel fuehrt. Kein Knopf und keine Unterstreichung:
   Sie sieht aus wie die Zeilen daneben, weil sie dieselbe Zeile ist — nur dass
   man sie anfassen kann. Der Hinweis darauf ist der Zeiger und ein Hauch
   Untergrund beim Ueberfahren, nicht ein zweites Aussehen. */
.pie-link { text-decoration: none; color: inherit; border-radius: 6px; }
.pie-link:hover { background: var(--hover); }

/* Die Auswahl darunter. Textlinks statt Reitern: Es sind zwei bis elf Ziele,
   und eine Reiterleiste, die bei jedem neuen Spiel laenger wird, saehe irgendwann
   aus wie eine Navigation, die es nicht ist. */
.pie-switch { display: flex; flex-wrap: wrap; gap: 4px 14px; margin-top: 12px;
  padding-top: 10px; border-top: 1px solid var(--border-soft); }
.pie-switch a { font-size: 13px; color: var(--text-dim); text-decoration: none; }
.pie-switch a:hover { color: var(--text); }
.pie-switch a.on { color: var(--accent); font-weight: 500; }
.pie-dot { flex: none; width: 13px; height: 13px; border-radius: 4px;
  box-shadow: inset 0 0 0 1px rgba(0,0,0,.18); }
/* Ein Balken neben dem Namen: er traegt dieselbe Farbe wie das Stueck und
   dieselbe Laenge wie der Anteil, also muss man Farben nicht mehr zwischen
   Kuchen und Legende zuordnen, um zu sehen, was gross ist. */
.pie-bar { flex: none; width: 84px; height: 6px; border-radius: 3px;
  background: var(--surface-2); overflow: hidden; }
.pie-bar span { display: block; height: 100%; border-radius: 3px; }
.pie-name { flex: 1; min-width: 0; font-size: 12.5px; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.pie-share { flex: none; min-width: 52px; text-align: right; font-size: 12.5px; font-weight: 700; color: var(--text-dim);
  font-variant-numeric: tabular-nums; }
.pie-val { flex: none; min-width: 92px; text-align: right; font-size: 13px;
  font-weight: 700; color: var(--text-strong); font-variant-numeric: tabular-nums; }


/* ---------- Setwaehler in der Filterleiste ----------
   Ein Suchfeld mit einer Liste darunter statt eines Ausklappmenues: bei
   sechsundsechzig Codes ist das Menue eine Liste, in der man nichts findet.
   Die Liste ist hoch genug fuer ein halbes Dutzend Zeilen und scrollt — sie
   steht dauerhaft offen, weil in einer Filterleiste ohnehin alles offen steht. */
.setpick { position: relative; }
.setsearch { width: 100%; padding: 7px 26px 7px 10px; border: 1px solid var(--border);
  border-radius: 8px; background: var(--surface-2); color: var(--text);
  font-size: 12px; outline: none; }
.setsearch:focus { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.setclear { position: absolute; right: 4px; top: 4px; width: 22px; height: 22px;
  border: none; border-radius: 6px; background: none; color: var(--text-faint);
  font-size: 11px; cursor: pointer; }
.setclear:hover { color: var(--text-strong); background: var(--surface-2); }
/* Ueber dem, was darunter steht, statt dazwischen: die Liste kommt und geht
   beim Tippen, und der Rest der Leiste soll dabei stehen bleiben. */
.setlist { position: absolute; left: 0; right: 0; top: calc(100% + 5px); z-index: 6;
  max-height: 208px; overflow-y: auto; border: 1px solid var(--border);
  border-radius: 8px; background: var(--surface);
  box-shadow: 0 14px 28px -12px rgba(0,0,0,.45); }
.setopt { display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 5px 9px; border: none; border-bottom: 1px solid var(--border-soft);
  background: none; color: var(--text-dim); font-family: inherit; font-size: 12px;
  cursor: pointer; text-align: left; }
.setopt:last-child { border-bottom: none; }
.setopt:hover { background: var(--accent-soft); color: var(--text-strong); }
.setopt.on { background: var(--accent-soft); color: var(--chip-a-text); font-weight: 700; }
.so-code { flex: 1; letter-spacing: .5px; }
.so-n { flex: none; font-size: 11px; color: var(--text-faint);
  font-variant-numeric: tabular-nums; }
.setnone { padding: 7px 2px; font-size: 11.5px; color: var(--text-faint); }

/* Ein Ring statt einer Zahl: geschlossen heisst fertig, und das erkennt man ueber
   eine ganze Liste hinweg, ohne jede Zeile zu lesen. */
.ringwrap { display: inline-flex; }
.ring { transform: rotate(-90deg); }          /* Start oben, nicht rechts */
.ring-bg { stroke: var(--border); }
.ring-fg { stroke: var(--accent); transition: stroke-dasharray .25s ease; }
.ringwrap.full .ring-fg { stroke: var(--up); }


/* Waehrend htmx das Raster holt. Kein Ladekreis und kein Balken: es dauert
   Millisekunden, und alles, was in dieser Zeit erscheint und wieder geht, ist
   mehr Bewegung als das Warten. Nur ein leichtes Abblenden, damit ein Druck
   ueberhaupt eine Antwort hat. */
#colmain.htmx-request { opacity: .55; transition: opacity .12s ease; }


/* ---------- Bestellzeile auf der Startseite ----------
   Zwei Zeilen statt fuenf Spalten: oben, wer und wieviel — das Paar, das eine
   Bestellung ausmacht —, darunter das Kleingedruckte. Vorher stritten Kaeufer,
   Nummer, Datum, Fortschritt und Betrag um dieselbe Aufmerksamkeit.

   Beim Ueberfahren nur ein Hauch Flaeche und ein Strich an der Kante, kein
   Farbwaschgang: die Akzentfarbe heisst in diesem System "hier ist etwas
   ausgewaehlt", und unter dem Zeiger ist nichts ausgewaehlt. */
/* Nur der Bestellkasten: die Zeilen bringen ihre eigene Flaeche mit. Vorher
   stand die Regel ohne Einschraenkung da und drueckte jeden tight-Kasten der
   Anwendung auf sechs Pixel zusammen — auf der Auswertung klebten die Listen
   deshalb am Rand. */
.panel.tight:has(.ordrows) { padding: 6px; }
.ordrows { display: flex; flex-direction: column; gap: 2px; }
.ordrow { display: block; padding: 9px 12px; border-radius: 9px; }
/* Nur ein Hauch Flaeche. Der Strich an der Kante war ein roter Balken neben
   jeder Zeile, ueber die man faehrt — die Akzentfarbe heisst in diesem System
   "hier ist etwas ausgewaehlt", und unter dem Zeiger ist nichts ausgewaehlt. */
.ordrow:hover { background: var(--surface-2); }
/* Ueber jedem der beiden Stapel: bezahlt kann raus, unbezahlt bleibt liegen. */
.ordgroup { display: flex; align-items: baseline; gap: 8px; padding: 6px 12px 8px;
  font-size: 11px; font-weight: 700; letter-spacing: 1px; text-transform: uppercase;
  color: var(--text-dim); }
.ordgroup span { font-size: 11px; font-weight: 700; color: var(--text-faint);
  font-variant-numeric: tabular-nums; }
.ordrow-top { display: flex; align-items: baseline; gap: 9px; }
.ordrow-buyer { font-size: 13.5px; font-weight: 700; color: var(--text-strong);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.ordrow-sum { flex: none; font-size: 14px; font-weight: 700; color: var(--text-strong);
  font-variant-numeric: tabular-nums; }
.ordrow-sub { display: flex; align-items: center; gap: 6px; margin-top: 3px;
  font-size: 11.5px; color: var(--text-faint); }
.ordrow-dot { color: var(--border); }
.ordrow-todo { flex: none; font-weight: 700; color: var(--text-dim); }
.ordrow-done { flex: none; font-weight: 700; color: var(--up); }

/* ---------- Umsatz ----------
   Drei Zahlen in einer Zeile statt zweier Kacheln mit Ewigkeitssummen. Die
   dritte ist die Summe der beiden davor und traegt deshalb den Rahmen. */
.revrow { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.rev { padding: 4px 2px; }
.rev.strong { padding-left: 16px; border-left: 1px solid var(--border-soft); }
.rev-label { font-size: 11.5px; font-weight: 600; color: var(--text-dim); }
.rev-value { margin-top: 5px; font-size: 23px; font-weight: 800;
  color: var(--text-strong); letter-spacing: -.4px; font-variant-numeric: tabular-nums; }
.rev.strong .rev-value { color: var(--chip-a-text); }
.rev-sub { margin-top: 3px; font-size: 11.5px; color: var(--text-faint); }

/* Titel mit Zurueck-Pfeil davor, wie in der Stapelansicht. */
.page-lead { display: flex; align-items: center; gap: 12px; }

/* Was diese Bestellzeile tun kann: Stornieren links, Versenden rechts. Die eine
   nimmt zurueck, die andere geht vorwaerts — nebeneinander waeren sie zwei
   gleich aussehende Knoepfe fuer entgegengesetzte Dinge. */
.ordacts { display: flex; justify-content: space-between; align-items: center;
  gap: 12px; padding: 0 16px 16px; }
.ordacts form { margin: 0; }
.ordacts form:only-child { margin-right: auto; }
/* Zuruecknehmen faerbt sich erst, wenn man darauf zielt: dauerhaft rot waere es
   das Auffaelligste in einer Liste, in der man es fast nie braucht. */
.head-act.danger:hover { border-color: var(--down); color: var(--down); }

/* Frankierlauf. Steht ueber der Liste und ist leer, solange nichts laeuft —
   eine Zeile, die immer da ist, wird zur Tapete und dann uebersehen. */
.frankbox:empty { display: none; }
.frankbox { display: flex; align-items: center; gap: 10px; margin: 0 0 14px;
  padding: 10px 14px; border: 1px solid var(--border); border-radius: 12px;
  background: var(--panel); font-size: 13px; font-weight: 600; }
.frank-say { color: var(--text); }
.frank-ok { color: var(--up); font-weight: 800; }
.frank-bang { display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 18px; border-radius: 50%; font-size: 12px; font-weight: 800;
  color: #fff; background: var(--down); }
.frankbox .ghost-btn { margin-left: auto; }
/* Der Kringel dreht sich, solange der Shop antwortet — kein Balken, weil es
   nichts zu messen gibt. */
.frank-spin { width: 14px; height: 14px; border-radius: 50%; flex: none;
  border: 2px solid var(--border); border-top-color: var(--accent);
  animation: bcSpin .8s linear infinite; }


.condbtn:disabled, .printopt:disabled { opacity: .45; cursor: not-allowed; }
.condbtn:disabled:hover, .printopt:disabled:hover { border-color: var(--border);
  background: var(--surface-2); }


/* ---------- der Druckwaehler, zugeklappt ----------
   Offen stand die Liste unter dem Rand des Dialogs, und ein Klick, der dem
   Oeffnen galt, landete auf einem fremden Druck. Zusammengefasst steht dort
   jetzt, was gerade gilt; aufklappen ist eine eigene Geste, und ein Fehlklick
   darauf aendert nichts. */
.printnow { display: flex; align-items: center; gap: 9px; padding: 9px 11px;
  border: 1px solid var(--border); border-radius: 9px; background: var(--surface-2);
  cursor: pointer; list-style: none; user-select: none; }
.printnow::-webkit-details-marker { display: none; }
.printnow:hover { border-color: var(--accent-border); }
.printpick[open] .printnow { border-bottom-left-radius: 0; border-bottom-right-radius: 0;
  border-bottom-color: transparent; }
.pn-set { font-size: 12.5px; font-weight: 700; letter-spacing: .6px;
  color: var(--text-strong); }
.pn-num { font-size: 12px; color: var(--text-dim); font-variant-numeric: tabular-nums; }
.pn-hint { font-size: 11.5px; color: var(--text-faint); }
.printnow .pilesec-mark { color: var(--text-faint); }
.printpick[open] > .printnow .pilesec-mark { transform: rotate(45deg) translate(-2px, -2px); }

/* Der Weg von der Nummer zur Sendung. Klein und leise: man braucht ihn einmal
   pro Brief, nicht bei jedem Blick auf die Bestellung. */
/* Nur noch der Pfeil, und der sitzt in der Zustandszeile. Ausgeschrieben war es
   eine Aufforderung, die lauter war als das Ergebnis, wegen dem man sie
   anklickt. */
.tracklink { flex: none; margin-left: 2px; font-size: 12px; line-height: 1;
  color: var(--text-faint); text-decoration: none; }
.tracklink:hover { color: var(--accent); }

/* Was die Post zuletzt gemeldet hat. Ein Punkt statt eines Wortes: unterwegs
   oder angekommen sieht man so, ohne den Satz zu lesen. */
.trackstate { display: inline-flex; align-items: center; gap: 7px; margin-top: 8px;
  font-size: 12px; font-weight: 600; color: var(--text-dim); }
/* Nur so breit wie ihr Inhalt: der Versandkasten ist eine Spalte, und darin
   wuerde die Zeile sonst auf volle Breite gezogen — der Hovertext haengt an
   ihrem rechten Rand und stuende dann am anderen Ende des Kastens, neben etwas
   ganz anderem. */
.trackstate { width: max-content; max-width: 100%; align-self: flex-start; }
/* Nach rechts aufgeklappt, weil links davon der Kastenrand kommt — und sonst
   so klein und so leise wie jeder andere Hinweis im Haus. Mit 280 px und fetter
   Schrift war der Kasten groesser als die Auskunft darin. */
.trackstate.has-tip::after { left: 0; right: auto; top: calc(100% + 5px);
  max-width: 196px; text-align: left; font-weight: 500; white-space: normal; }
.trackdot { flex: none; width: 7px; height: 7px; border-radius: 50%;
  background: var(--warn); }
/* Derselbe Zustand wie im Versandkasten, nur in der zugeklappten Zeile: ein
   Punkt und zwei Worte. Sie steht dort, wo sonst nichts stuende, und beantwortet
   die eine Frage, wegen der man die Zeile sonst aufklappt. */
.ordstate { display: inline-flex; align-items: center; gap: 6px; flex: none;
  font-size: 11.5px; font-weight: 700; color: var(--text-faint); }
.ordstate.done { color: var(--up); }
.ordstate.done .trackdot { background: var(--up); }
.ordstate.back { color: var(--down); }
.ordstate.back .trackdot { background: var(--down); }
.trackstate.done { color: var(--up); }
.trackstate.done .trackdot { background: var(--up); }
/* Ein Brief, der zurueckkommt, meldet dasselbe wie einer, der ankommt —
   deshalb faerbt sich hier die Zeile, statt nur den Satz zu wechseln. */
.trackstate.back { color: var(--down); }
.trackstate.back .trackdot { background: var(--down); }

/* ---------- Filterleiste ein- und ausklappen ---------- */

/* Ein Knopf, an der Naht zwischen Leiste und Raster. Er bleibt auch stehen,
   wenn die Leiste offen ist: die Zahl darauf ist die Auskunft, dass die Sammlung
   einen Ausschnitt zeigt, und genau die ist hinter einer eingeklappten Leiste
   sonst nicht mehr zu sehen. */
.filterbtn { position: relative; flex: none; display: flex; align-items: center;
  justify-content: center; width: 30px; height: 30px; margin-right: 4px;
  border-radius: 8px; cursor: pointer; border: 1px solid var(--border);
  background: var(--surface-2); color: var(--text-faint); }
.filterbtn:hover { border-color: var(--accent-border); color: var(--text-strong); }
/* Eingeklappt bleibt der Knopf sichtbar angefasst — sonst waere die Leiste weg
   und nichts sagte, dass sie sich holen laesst. */
:root[data-filters="off"] .filterbtn { border-color: var(--accent-border);
  background: var(--accent-soft); color: var(--chip-a-text); }
.fb-n { position: absolute; top: -6px; right: -6px; min-width: 16px; height: 16px;
  padding: 0 4px; border-radius: 9px; background: var(--accent); color: var(--on-accent);
  font-size: 10px; font-weight: 800; line-height: 16px; text-align: center; }

:root[data-filters="off"] .sidebar { display: none; }

/* ---------- Zahnrad: Ansicht ---------- */

.viewmenu { position: relative; flex: none; }
/* Drei Punkte statt eines zweiten Zahnrads: das Rad oben neben dem Namen
   gehoert dem Stapel (`gearbtn`), dieses hier nur dem Blick darauf. Eigene
   Klasse, damit die Pruefung „wo kein Stapel ist, ist auch keine
   Stapel-Einstellung" weiter greift — sie haengt genau an diesem Namen. */
.viewbtn.on { border-color: var(--accent-border); color: var(--text-strong); }
/* Nach unten und nach links: das Zahnrad ist das letzte Element der Leiste, ein
   Menue, das an seiner linken Kante beginnt, haengt aus dem Fenster. */
.viewpop { position: absolute; top: calc(100% + 8px); right: 0; z-index: 40;
  width: 258px; padding: 8px; border: 1px solid var(--border); border-radius: 12px;
  background: var(--surface); box-shadow: 0 18px 40px -18px rgba(0,0,0,.6); }
.vp-head { font-size: 11px; font-weight: 700; letter-spacing: .8px; text-transform: uppercase;
  color: var(--text-faint); padding: 6px 8px 8px; }
.vp-opt.sep, .vp-field.sep { border-top: 1px solid var(--border-soft);
  margin-top: 7px; padding-top: 13px; }
.vp-opt.sep { border-radius: 0 0 9px 9px; }
/* Kein Klickziel, sondern ein Feld: also kein Zeiger, kein Aufleuchten beim
   Ueberfahren, und das Eingabefeld rechts an der Kante — wie die Zahl, die es
   ersetzt, in der Kopfzeile stand. */
.vp-field { display: flex; align-items: center; gap: 10px; padding: 7px 8px; }
.vp-field .vp-name { flex: 1; min-width: 0; }
.vp-field .moneyin { width: auto; flex: none; }
/* Einzeilig, seit die Kurzbeschreibungen weg sind: das Kaestchen sitzt neben
   dem Wort und nicht mehr an der Oberkante eines zweizeiligen Blocks. */
.vp-opt { display: flex; align-items: center; gap: 9px; padding: 7px 8px;
  border-radius: 9px; cursor: pointer; }
.vp-opt:hover { background: var(--surface-2); }
.vp-opt input { flex: none; accent-color: var(--accent); }
.vp-name { font-size: 13px; font-weight: 600; color: var(--text-strong); }
.vp-opt:has(input:checked) .vp-name { color: var(--accent); }

/* ---------- Kartenansicht: das ganze Blatt ---------- */

/* Breiter gerastert als die Kachel, weil eine Karte hochkant steht: bei 158
   Punkten Breite waere sie 220 hoch, und auf dem Blatt stuende nichts mehr, was
   man lesen koennte — dann kann man auch beim Ausschnitt bleiben. */
.tilegrid.as-card { grid-template-columns: repeat(auto-fill, minmax(178px, 1fr)); }
.tile.as-card { contain-intrinsic-size: auto 300px; }
.tile.as-card .tile-art { aspect-ratio: 63 / 88; }
/* Das Blatt hat seinen eigenen Rand und seine eigene Abdunklung unten; die
   Vignette der Kachel legte eine zweite darueber. */
.tile.as-card .art-vignette { box-shadow: inset 0 0 0 1px rgba(0,0,0,.25); }
/* Alles, was ueber dem Ausschnitt einer Kachel auf Artwork liegt, laege hier auf
   dem Kartennamen und dem Regeltext. Es steht deshalb unter dem Bild
   (`.tile-meta`) — bis auf die rechte obere Ecke, in der das Vormerk-Kaestchen
   und die Commander-Marke sitzen: eines ist ein Klickziel, das andere haelt
   seine Ecke in allen Ansichten. */
.tile.as-card .tag-tl, .tile.as-card .tag-bl, .tile.as-card .tag-br { display: none; }
.tile-meta { display: flex; align-items: center; gap: 8px; margin-top: 5px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .3px; color: var(--text-faint);
  text-transform: uppercase; }
.tm-cond, .tm-lang { color: var(--text-dim); }
.tm-foil { color: var(--accent); }
.tm-proxy { color: var(--text-dim); }
.tm-qty, .tm-pos { font-variant-numeric: tabular-nums; }
.tm-pos { margin-left: auto; text-transform: none; letter-spacing: 0; font-weight: 600; }

/* ---------- Textansicht ---------- */

.cardlist { display: flex; flex-direction: column; }
/* Kein Kaestchen je Zeile, nur eine Haarlinie dazwischen: sechzig gerahmte
   Zeilen sind sechzig Rahmen, und was man hier liest, sind Spalten. */
.cardrow { display: flex; align-items: center; gap: 12px; padding: 7px 10px;
  border-bottom: 1px solid var(--border-soft); cursor: pointer; }
/* Dasselbe in der Textansicht: die unterste Karte braucht keinen Strich unter
   sich. Bei den Abschnitten nach Kartentyp gilt es je Abschnitt — sonst stiesse
   der Strich der letzten Zeile auf die Ueberschrift des naechsten. */
.cardlist .cardrow:last-child { border-bottom: none; }
.cardrow:hover { background: var(--surface-2); }
.cardrow:focus { outline: none; background: var(--surface-2);
  box-shadow: inset 2px 0 0 var(--accent); }
.cr-pips { flex: none; display: flex; gap: 3px; width: 34px; }
/* Zahlen rechtsbuendig und auf gleicher Ziffernbreite — sonst tanzen Menge und
   Preis von Zeile zu Zeile, und genau das Untereinanderstehen ist der Grund,
   warum man diese Ansicht waehlt. */
.cr-qty { flex: none; width: 22px; text-align: right; font-size: 12.5px; font-weight: 700;
  color: var(--text-dim); font-variant-numeric: tabular-nums; }
.cr-name { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px;
  font-size: 13.5px; font-weight: 600; color: var(--text-strong);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.cr-flag { flex: none; padding: 1px 6px; border-radius: 5px; background: var(--surface-2);
  border: 1px solid var(--border-soft); font-size: 10px; font-weight: 700;
  letter-spacing: .3px; color: var(--text-dim); }
.cr-flag.foil { color: var(--accent); border-color: var(--accent-border); }
/* A start-page mover measured on the foil price (`home.movers`). */
.row-name .cr-flag { margin: 0 2px 0 6px; vertical-align: 1px; }
.cr-set { flex: none; width: 96px; display: flex; align-items: baseline; gap: 7px;
  font-size: 12px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.cr-code { font-weight: 700; letter-spacing: .5px; color: var(--text-dim); }
.cr-cond { flex: none; width: 26px; font-size: 11.5px; font-weight: 700; color: var(--text-faint); }
.cr-lang { flex: none; width: 24px; font-size: 11px; font-weight: 700; color: var(--text-faint);
  text-transform: uppercase; }
.cr-pos { flex: none; font-size: 11.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.cr-price { flex: none; width: 86px; text-align: right; font-size: 13px; font-weight: 700;
  color: var(--text-strong); font-variant-numeric: tabular-nums; }
.cr-price.noprice { font-weight: 500; color: var(--text-faint); }
.cr-trend { flex: none; width: 62px; text-align: right; }
.cr-mark { flex: none; width: 22px; display: flex; justify-content: flex-end; }
/* Dasselbe Kaestchen, aber nicht mehr auf einem Bild: das dunkle Feld der Kachel
   steht dort gegen das Artwork, auf hellem Pergament ist es ein schwarzer Fleck. */
.cr-mark .tile-box { background: var(--surface-2); border-color: var(--border); }
.cr-mark .tile-sale:hover .tile-box { border-color: var(--accent-border); background: var(--surface-2); }
.cr-mark .tile-sale input:checked + .tile-box,
.cr-mark .tile-sale.on .tile-box { background: var(--accent); border-color: var(--accent); }
.cr-mark .tile-box::after { border-color: var(--on-accent); }

/* Die Schwebevorschau: ein Bild, dem Zeiger nach. Ausserhalb des Bildlaufs
   verankert (`position: fixed`), damit es nicht mit der Liste wegscrollt, und
   ohne Mausereignisse, sonst faehrt der Zeiger auf sein eigenes Bild und die
   Zeile darunter verliert ihn. */
.cardpeek { position: fixed; z-index: 90; pointer-events: none;
  width: 250px; border-radius: 12px; overflow: hidden;
  box-shadow: 0 24px 50px -16px rgba(0,0,0,.75); }
.cardpeek img { display: block; width: 100%; height: auto; }

/* ---------- Preistrend ---------- */

/* Klein und neben der Zahl, auf die er sich bezieht. Waagerecht und grau, wenn
   sich unter einem halben Prozent nichts getan hat: ein gruener Pfeil fuer drei
   Cent Bewegung meldet ein Ereignis, wo keines war. */
.trend { display: inline-flex; align-items: center; margin-left: 7px;
  font-size: 11px; font-weight: 700; font-variant-numeric: tabular-nums;
  white-space: nowrap; }
.trend.up { color: var(--up); }
.trend.down { color: var(--down); }
.trend.flat { color: var(--text-faint); font-weight: 600; }

/* 720, wie der Rest der Seite: dort dreht `.col-layout` auf Spalten, und ab da
   liegt die Filterleiste oben quer statt daneben. */
@media (max-width: 720px) {
  /* Am Telefon liegt die Leiste oben quer statt daneben. Eingeklappt ist sie
     hier eher noch nuetzlicher als am grossen Schirm: offen nimmt sie fast die
     halbe Hoehe. Der Knopf sitzt unveraendert in der Werkzeugleiste. */
  .viewpop { width: 232px; }
  /* Spalten fallen in der Reihenfolge, in der man sie am wenigsten vermisst.
     Am Ende bleiben Name, Preis und Trend — wofuer man die Liste aufmacht. Der
     Name ist die einzige Spalte ohne feste Breite: was die anderen sparen,
     bekommt er, und bei 390 Punkten waren das vorher vierzig. */
  .cr-set, .cr-lang, .cr-pos, .cr-pips { display: none; }
  .cardrow { gap: 8px; padding: 8px 6px; }
  .cr-qty { width: 16px; }
  .cr-cond { width: 22px; }
  .cr-price { width: 68px; }
  .cr-price.paid { min-width: 68px; }
  .cr-trend { width: 52px; }
  /* Ein Finger hat keinen Zeiger, an dem ein Bild haengen koennte. */
  .cardpeek { display: none; }
}

/* Warum an dieser Karte nichts mehr geht. Eine Zeile unter dem Kartentyp, in der
   Farbe der Warnung und ohne Kasten: sie erklaert etwas, das man schon sieht
   (das Grau), und soll deshalb nicht lauter sein als das Erklaerte. */
.lockline { margin: 8px 0 4px; font-size: 12px; font-weight: 600; color: var(--warn); }

/* Die Grenze fuer den ersten Lauf und der CSV-Ausweg darunter. Beides klein:
   das eine ist eine Vorsicht, das andere der zweite Weg. */
/* Der zweite Weg, gleich breit wie der erste: ein Ausweg ist kein Kleingedrucktes. */
.exp-fallback { margin-top: 8px; }
.exp-csv { display: block; width: 100%; text-align: center; }

/* ---------- Stapel aus einer Liste anlegen ---------- */

/* Ein Knopf, der wie einer aussieht, um ein Dateifeld, das keiner ist. Das
   native Feld traegt in jedem Browser eine andere Beschriftung in einer anderen
   Sprache — hier steht deutsch, was passiert. */
.filepick { display: inline-flex; align-items: center; padding: 4px 11px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--surface-2);
  color: var(--text-dim); font-size: 12px; font-weight: 600; cursor: pointer; }
.filepick:hover { border-color: var(--accent-border); color: var(--text-strong); }

/* Die eingeworfene Liste. Feste Schriftbreite, weil hier Mengen untereinander
   stehen und man sofort sieht, ob eine Zeile aus der Reihe faellt. */
.listin { width: 100%; resize: vertical; line-height: 1.5;
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px; }

/* Was ein Listenimport nicht auflösen konnte. Eine Zeile über dem Raster, in
   der Farbe der Warnung — sie meldet einen Vorgang und beschreibt keinen
   Zustand, also bekommt sie keinen Kasten und keine dauerhafte Stelle. */
.importmiss { flex: none; padding: 9px 22px; border-bottom: 1px solid var(--border-soft);
  background: color-mix(in srgb, var(--warn) 9%, transparent);
  font-size: 12.5px; font-weight: 600; color: var(--warn); }

/* ---------- Auswertung: die Verkaufshistorie ---------- */

/* Verkauft und versendet, als eigene Zeile unter der Ueberschrift.

   Sie standen inline daneben — „NOCH HIER € 116,81 3 VERSENDET € 344,31 8",
   vier Zahlen und zwei Woerter in einem Zug, den man von links nach rechts
   entziffern musste. Zwei Bloecke mit der Beschriftung ueber dem Betrag liest
   man dagegen in einem Blick.

   Der Balken darunter traegt das Verhaeltnis: zwei Betraege nebeneinander muss
   man erst teilen, um zu sehen, wieviel noch zu tun ist. Ohne Gesamtsumme —
   die steht als Kachel „Umsatz" oben. */
/* Luft nach oben, weil die Ueberschriftzeile rechts ein Auswahlfeld traegt:
   „VERSENDET" stand zwei Pixel unter dessen Unterkante und las sich als dessen
   Beschriftung statt als Anfang eines eigenen Blocks. */
.splitbar { display: grid; grid-template-columns: 1fr 1fr; gap: 4px 16px;
  margin: 16px 0 18px; }
.sp { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.sp.right { text-align: right; }
.sp-lab { font-size: 10px; font-weight: 700; letter-spacing: .5px;
  text-transform: uppercase; color: var(--text-faint); }
.sp-val { font-size: 17px; font-weight: 700; color: var(--text-strong);
  font-variant-numeric: tabular-nums; }
.sp-n { font-size: 11.5px; color: var(--text-faint); }
/* Ein Streifen ueber beide Spalten: links, was noch hier liegt, rechts, was raus
   ist. Die Warnfarbe fuer das, was Arbeit macht — versendet ist Geschichte und
   braucht keine Aufmerksamkeit mehr. */
.sp-track { grid-column: 1 / -1; height: 6px; border-radius: 999px;
  background: color-mix(in srgb, var(--up) 26%, transparent); overflow: hidden;
  margin-top: 8px; }
.sp-fill { height: 100%; border-radius: 999px; background: var(--warn); }


/* Eine Zeile je verkauftem Exemplar. Dieselbe Bauart wie die Textansicht des
   Rasters — feste Spalten, Zahlen rechtsbuendig, damit man untereinander liest
   statt zu suchen. */
.soldlist { display: flex; flex-direction: column; }
.soldrow { display: flex; align-items: center; gap: 12px; padding: 7px 2px;
  font-size: 13px; }
.soldrow + .soldrow { border-top: 1px solid var(--border-soft); }
.soldrow .art { flex: none; width: 34px; height: 25px; border-radius: 5px;
  overflow: hidden; }
.soldrow .art img { width: 100%; height: 100%; object-fit: cover; display: block; }
.sc-name { flex: 1; min-width: 0; display: flex; align-items: center; gap: 8px;
  font-weight: 600; color: var(--text-strong);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sc-set { flex: none; width: 46px; font-size: 11.5px; font-weight: 700;
  letter-spacing: .5px; color: var(--text-dim); }
.sc-cond { flex: none; width: 24px; font-size: 11.5px; font-weight: 700; color: var(--text-faint); }
.sc-day { flex: none; width: 46px; font-size: 12px; color: var(--text-faint);
  font-variant-numeric: tabular-nums; }
.sc-order { flex: none; width: 84px; font-size: 11.5px; color: var(--text-faint);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* Ueber oder unter dem Marktpreis des Verkaufstags — die einzige Zahl hier, an
   der man ablesen kann, ob der Preis gut war. */
.sc-over { flex: none; width: 52px; text-align: right; font-size: 11.5px;
  font-weight: 700; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.sc-price { flex: none; width: 76px; text-align: right; font-weight: 700;
  color: var(--text-strong); font-variant-numeric: tabular-nums; }

/* Gleiche Hoehe, weil sie nebeneinander stehen. `align-items: start` liess das
   kuerzere Panel oben haengen, und zwei Kaesten nebeneinander, von denen einer
   frueher aufhoert, sehen aus wie ein Fehler im Aufbau. */
.reports .grid { align-items: stretch; }
.reports .grid > .panel { display: flex; flex-direction: column; }
/* Der Verlauf traegt die Hoehe; die Klassen darunter verteilen sich auf das,
   was daneben tatsaechlich gebraucht wird. */
.reports .grid .segments { flex: 1; display: flex; flex-direction: column;
  justify-content: space-between; }
.reports .cut { margin-top: 36px; padding-top: 28px;
  border-top: 1px solid var(--border-soft); }
.reports .gap { margin-top: 16px; }

@media (max-width: 900px) {
  .sc-order, .sc-cond, .sc-over { display: none; }
  .splitbar { gap: 4px 10px; }
}

/* „+ 10" hinter einer gekuerzten Liste: dass da noch mehr ist, ohne dass alles
   dastehen muss. Kein Knopf — es gibt nichts zu druecken, die Liste ist eine
   Auswahl der dringendsten. */
.more { font-size: 11.5px; font-weight: 700; color: var(--text-faint); }

/* Bestellung von Hand: an wen, und dann was. Eine Zeile fuer den Kopf, damit
   Kaeufer, Versand und Zahlung nicht drei Zeilen fuer drei Felder brauchen. */
.ord-new-head { display: flex; gap: 8px; margin: 12px 0 4px; }
.ord-new-buyer { flex: 1 1 auto; min-width: 0; }
.ord-new-ship { flex: none; width: 96px; }
.ord-new-pay { flex: none; width: 116px; }
.ord-new-head input, .ord-new-head select { padding: 6px 9px; border: 1px solid var(--border);
  border-radius: 8px; background: var(--surface); color: var(--text);
  font: inherit; font-size: 12.5px; }
.ord-new-search { margin: 8px 0; }


/* --- Umsortieren -----------------------------------------------------------
   Nur sichtbar, solange der Modus laeuft: Ein Raster, das immer nach „fass mich
   an" aussieht, laedt zum Verschieben ein, wo man eigentlich oeffnen will. */
.sortbtn.on { border-color: var(--accent); background: var(--accent-soft); color: var(--chip-a-text); }

[data-sortable].sorting .tile,
[data-sortable].sorting .cardrow { cursor: grab; }
[data-sortable].sorting .tile:active,
[data-sortable].sorting .cardrow:active { cursor: grabbing; }
/* Der Platz, an dem die Karte gerade weg ist — kein Schatten, keine Bewegung:
   Der Browser zieht schon ein Abbild mit, und zwei Sachen in Bewegung sind eine
   zu viel. */
[data-sortable] .dragging { opacity: .35; }
/* Die Platznummer traegt in dieser Sortierung die Aussage, also darf sie hier
   nicht die kleinste Schrift auf der Kachel sein. */
[data-sortable].sorting .tag-pos { background: var(--accent-soft); color: var(--chip-a-text); font-weight: 700; }

/* Die Ueberschrift einer Bestellung in der Verkaufshistorie: Nummer, Tag,
   Stueckzahl, Summe. Einmal darueber statt siebenmal daneben — eine Bestellung
   ist ein Vorgang und kein Merkmal einer Zeile. */
.ordhead { display: flex; align-items: baseline; gap: 10px; padding: 12px 2px 6px;
  border-bottom: 1px solid var(--border); }
.soldlist.grouped .ordhead:first-child { padding-top: 2px; }
.oh-no { font-size: 12.5px; font-weight: 800; letter-spacing: .3px; color: var(--text-strong); }
.oh-day { font-size: 11.5px; color: var(--text-faint); font-variant-numeric: tabular-nums; }
.oh-n { font-size: 11px; font-weight: 700; padding: 1px 7px; border-radius: 20px;
  background: var(--surface-2); color: var(--text-faint); }
.oh-sum { font-size: 13px; font-weight: 700; color: var(--text-strong);
  font-variant-numeric: tabular-nums; }
/* Eingerueckt, damit man ohne Nachdenken sieht, was unter welcher Bestellung
   haengt — die Ueberschrift traegt keine Klammer, also traegt sie die Kante. */
.soldrow.in-group { padding-left: 14px; }
.soldlist.grouped .ordhead + .soldrow { border-top: none; }


/* ---------- Blättern: Pfeile und Zähler ---------- */
/* Stammen aus der entfernten Prüfansicht und tragen jetzt `pager.js` — dieselbe
   Form für dasselbe Tun, in Gewinner, Verlierer und Liegengeblieben. */
.rvnav { display: flex; align-items: center; gap: 4px; }
.rvbtn { width: 26px; height: 26px; border-radius: 7px; cursor: pointer;
  border: 1px solid var(--border); background: var(--surface-2);
  color: var(--text-dim); font-size: 15px; line-height: 1; }
.rvbtn:hover { border-color: var(--accent-border); color: var(--text-strong); }
.rvcount { font-size: 12px; color: var(--text-faint); padding: 0 6px;
  font-variant-numeric: tabular-nums; }

/* Blättern in einer Liste: dieselben Pfeile wie überall. Versteckt, solange es
   nur eine Seite gibt — `pager.js` blendet sie ein, denn Pfeile, die nichts tun,
   sind schlimmer als keine. */
.pgnav { display: flex; align-items: center; gap: 4px; margin-left: 8px; }
.pgnav[hidden] { display: none; }
/* Unter der Liste rechts ausgerichtet, mit einer Haarlinie darüber: sie gehören
   zu den Zeilen darüber und nicht zum nächsten Kasten. In der Kopfzeile eines
   Panels (Liegengeblieben, ohne Chips) bleiben sie, wo sie sind. */
.panel > .pgnav { justify-content: flex-end; margin: 10px 0 0;
  padding-top: 10px; border-top: 1px solid var(--border-soft); }

/* Bestellung von Hand: oben was gewaehlt ist, unten was man sucht.

   Gebaut aus dem, was der Rest des Systems schon hat: Felder wie in der
   Filterleiste (`--surface-2`, 8px Radius, derselbe Fokusring), Zeilen wie im
   Export-Dialog (`.exp-item`), Ueberschriften wie in der aufgeklappten
   Bestellung (`.ordbox-title`). Ein Dialog mit eigener Formsprache faellt auf,
   und zwar als Fremdkoerper. */
/* Der Dialog hoert auf zu wachsen, statt aus dem Bild zu laufen: es sind die
   beiden Listen, die laenger werden, also rollen sie — der Kopf, die Felder und
   der Knopf bleiben, wo sie sind. Der Deckel am Kasten selbst ist der Notnagel
   fuer kleine Fenster.

   Die Deckel sind enger als der Platz erlaubt, und das ist Absicht: ein Dialog,
   der fast den ganzen Schirm fuellt, laesst einen nicht mehr sehen, worauf er
   liegt. Lieber rollen die Listen frueher. */
.askbox.ordnew { width: min(820px, 100%); max-height: 82vh; overflow-y: auto;
  padding: 20px 22px 18px; }
.ordnew .chosen-list { max-height: 28vh; overflow-y: auto; }
.ordnew .exp-list { max-height: 26vh; }
/* Und der Knopf bleibt am Boden stehen, wenn der Rest rollt. Sonst kostet der
   Deckel genau das, wofuer er da war: man sucht die Handlung, die man gerade
   vorbereitet hat. Der Ring in der Kastenfarbe ist die fehlende Polsterung —
   ohne ihn schiebt sich der Inhalt an seinen Ecken vorbei. */
.ordnew .exp-go { position: sticky; bottom: -16px; z-index: 2;
  box-shadow: 0 0 0 9px var(--surface), 0 20px 0 9px var(--surface); }
.ordnew-sec { margin-top: 22px; }
.ordnew-sec:first-of-type { margin-top: 16px; }
.ordnew-lab { display: flex; align-items: baseline; gap: 8px; margin-bottom: 12px;
  padding-bottom: 8px; border-bottom: 1px solid var(--border-soft);
  font-size: 12px; font-weight: 700; color: var(--text-dim); }
.ordnew .chosen-lab { margin-left: auto; font-size: 12.5px; font-weight: 700;
  color: var(--text-strong); font-variant-numeric: tabular-nums; }

/* Ein Feld sieht ueberall gleich aus — dieselben Werte wie `.sidebar input`. */
.ordnew-sec input, .ordnew-sec select, .ordnew-row input, .ordnew-row select,
.ord-new-filter .search input, .chosen-price {
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px;
  color: var(--text); font: inherit; font-size: 12.5px; padding: 9px 11px;
  outline: none; }
.ordnew-sec input:focus, .ordnew-sec select:focus, .ordnew-row input:focus,
.ordnew-row select:focus, .ord-new-filter .search input:focus, .chosen-price:focus {
  border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.ordnew-sec > input, .ordnew-sec > select,
.ordnew-blk > input { display: block; width: 100%; margin-top: 10px; }
.ordnew-row { display: flex; gap: 10px; margin-top: 10px; }
.ordnew-row input, .ordnew-row select { flex: 1 1 auto; min-width: 0; }
.ordnew-row .w-plz { flex: none; width: 92px; }
.ordnew-row .w-land { flex: none; width: 72px; }
.ordnew-row .w-geld { flex: none; width: 122px; }
.ordnew-row .w-zahl { flex: none; width: 132px; }
/* Keine Pfeilchen an den Geldfeldern: einen Preis tippt man, man klickt ihn
   nicht in Ein-Cent-Schritten zusammen — und die zwei Pfeile nehmen genau dort
   Platz weg, wo die Ziffern stehen. */
.ordnew input[type=number] { appearance: textfield; -moz-appearance: textfield; }
.ordnew input[type=number]::-webkit-outer-spin-button,
.ordnew input[type=number]::-webkit-inner-spin-button {
  -webkit-appearance: none; margin: 0; }

.ord-new-filter { display: flex; gap: 10px; align-items: center; margin: 14px 0 8px; }
.ord-new-filter .search { flex: 1 1 auto; min-width: 0; margin: 0; }
.ord-new-filter .search input { width: 100%; padding-left: 30px; }
.ord-new-filter .search-i { left: 10px; }
#stackPick { flex: none; width: 176px; }
/* Eine Hoehe fuer beide, gesagt statt gerechnet: `select` und `input` bringen
   bei gleicher Polsterung verschiedene Hoehen mit (35 gegen 33), und zwei
   Pixel Versatz nebeneinander sieht man. */
.ord-new-filter #stackPick, .ord-new-filter .search input { height: 36px; }

/* Zeilen wie im Export-Dialog: dieselben Abstaende, dieselbe Zeichnung. */
/* Zwei Spalten, seit der Dialog breit genug ist: acht gewaehlte Karten sind vier
   Zeilen statt acht, und damit ist das Rollen halb so weit. Bei schmalem Fenster
   faellt es von selbst auf eine Spalte zurueck. */
.chosen-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(330px, 1fr));
  gap: 6px 10px; align-content: start; }
.chosen-list:empty { display: none; }
/* Das Bild ist der Grund fuer die Breite: der Name allein unterscheidet die
   „Insel" aus vier Sets nicht, das Artwork schon. Kartenformat quer wie in den
   Bestellzeilen, damit dasselbe Bild ueberall gleich aussieht. */
.chosen-art { flex: none; width: 42px; height: 31px; border-radius: 5px;
  overflow: hidden; display: block; background: var(--surface-2); }
.chosen-art img, .chosen-art .art-fill { width: 100%; height: 100%;
  object-fit: cover; display: block; }
.chosen-main { display: flex; flex-direction: column; min-width: 0; gap: 1px; }
.chosen-main .exp-name { flex: none; }
.chosen-main .exp-meta { white-space: nowrap; overflow: hidden;
  text-overflow: ellipsis; }
.pick-row { display: flex; align-items: baseline; gap: 12px;
  padding: 8px 7px; border-radius: 8px; }
/* `center`, nicht `baseline`: in dieser Zeile steht ein Eingabefeld, und ein
   Feld hat keine Schriftlinie, an der sich Text ausrichten koennte — der Name
   stand deshalb hoeher als der Preis daneben. */
.chosen-row { display: flex; align-items: center; gap: 10px;
  padding: 6px 8px; border-radius: 9px; background: var(--surface-2); }
.pick-row .exp-name { flex: 1; min-width: 0; }
.chosen-row .chosen-main { flex: 1; }
.chosen-price { flex: none; width: 82px; text-align: right; padding: 5px 8px;
  background: var(--surface); font-variant-numeric: tabular-nums; }
/* Derselbe Platz, aber kein Feld: in einer bestehenden Bestellung steht der
   Preis fest, den der Käufer bezahlt. Ein Eingabefeld daneben verspräche, dass
   man ihn hier ändern kann. */
.chosen-price.flat { border: 0; background: none; padding: 5px 0;
  font-weight: 700; color: var(--text-strong); }
.chosen-row form { flex: none; display: flex; }
.chosen-x { flex: none; width: 20px; height: 20px; border: 0;
  border-radius: 6px; background: none; color: var(--text-faint); cursor: pointer;
  font-size: 12px; line-height: 1; }
.chosen-x:hover { color: var(--down); background: var(--surface-2); }

/* Ein Treffer ist ein Knopf, keine Zeile mit Kaestchen: ein Klick legt ihn oben
   ab. Er verhaelt sich trotzdem wie `.exp-item`, damit die zwei Listen im selben
   Dialog nicht wie zwei Bauteile aussehen. */
.pick-row { width: 100%; border: 0; background: none; color: var(--text);
  font: inherit; text-align: left; cursor: pointer; }
.pick-row:hover:not(:disabled) { background: var(--surface-2); }
.pick-plus { flex: none; width: 16px; align-self: center; text-align: center;
  font-weight: 800; color: var(--accent); }
.pick-row.taken { opacity: .45; cursor: default; }
.pick-row.taken .pick-plus { color: var(--up); }


/* --- Meldezeile ------------------------------------------------------------
   Unten links, ueber allem, und sie geht von selbst wieder. Vier Sekunden: lang
   genug, um sie im Vorbeigehen zu lesen, kurz genug, um nicht im Weg zu stehen.
   `:empty` blendet sie aus, statt einen leeren Kasten liegen zu lassen.

   Eigener Name und nicht `.toast`: Den gibt es schon, oben mittig, fuer den
   Scanner. Zwei Regeln mit demselben Namen mischen sich — meine setzte `bottom`,
   seine `top` und ein `transform`, und heraus kam eine Meldung, die oben links
   ueber dem Schriftzug lag. */
.notebar { position: fixed; left: 22px; bottom: 22px; z-index: 90;
  padding: 10px 15px; border: 1px solid var(--accent-border); border-radius: 10px;
  background: var(--accent-soft); color: var(--chip-a-text);
  font-size: 13px; font-weight: 600; box-shadow: 0 12px 30px -12px rgba(0,0,0,.5);
  animation: notebar-weg 4s ease forwards; }
.notebar:empty { display: none; }
/* Mit Rueckgaengig (ein Wunsch wurde abgehakt, Migration 55): dieselbe Zeile,
   aber neun Sekunden, damit der Knopf zu erreichen ist. */
.notebar-undo { display: flex; align-items: center; gap: 12px;
  animation-duration: 9s; }
.notebar-form { display: contents; }
.notebar-btn { border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; color: var(--accent); text-decoration: underline;
  text-underline-offset: 3px; }
@keyframes notebar-weg {
  0%   { opacity: 0; transform: translateY(6px); }
  8%   { opacity: 1; transform: none; }
  80%  { opacity: 1; transform: none; }
  100% { opacity: 0; transform: translateY(6px); visibility: hidden; }
}


/* Der Verlauf, waehrend er geholt wird. Ein Kreisel und ein Satz statt einer
   leeren Flaeche — die sieht aus wie ein Fehler, und hier ist keiner. */
.ph-empty.holt { display: flex; align-items: center; gap: 9px; }
.holt-kreisel { flex: none; width: 13px; height: 13px; border-radius: 50%;
  border: 2px solid var(--border); border-top-color: var(--accent);
  animation: holt-dreht 900ms linear infinite; }
@keyframes holt-dreht { to { transform: rotate(360deg); } }

/* ---------- Wunschliste ---------- */
/* Das Regal sieht aus wie das der Stapel, nur ohne Bild: eine Wunschliste hat
   kein Gesicht, weil die Karten noch nicht da sind. Also Text, und der Name
   gross genug, um ihn beim Ueberfliegen zu treffen. */
.wl-grid { display: grid; gap: 14px;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr)); }
.wl-card { display: block; padding: 16px 18px; text-decoration: none;
  transition: transform .18s ease, border-color .18s ease; }
.wl-card:hover { transform: translateY(-2px); border-color: var(--accent-border); }
.wl-name { font-size: 17px; font-weight: 650; color: var(--text-strong); }
.wl-meta { margin-top: 4px; font-size: 13px; color: var(--text-dim);
  font-variant-numeric: tabular-nums; }
.wl-note { margin-top: 8px; font-size: 13px; color: var(--text-dim); }

/* Dieselbe Zeilenform wie die Sammelliste, eine Spalte weniger: statt des
   Platzes steht vorn die Menge, denn was fehlt, liegt nirgendwo. */
.wl-rows { display: block; }
.wl-main { min-width: 0; display: flex; flex-direction: column; gap: 2px; }
.wl-row { display: grid; align-items: center; gap: 14px;
  grid-template-columns: 44px 1fr auto 28px;
  padding: 10px 2px; border-top: 1px solid var(--border-soft); }
.wl-row:first-child { border-top: 0; }
.wl-qty { font-size: 17px; font-weight: 700; text-align: right;
  font-variant-numeric: tabular-nums; color: var(--text-strong); }
.wl-row form { margin: 0; }
.wl-x { background: none; border: 0; cursor: pointer; color: var(--text-dim);
  font-size: 15px; line-height: 1; padding: 4px; opacity: 0; transition: opacity .14s; }
.wl-row:hover .wl-x, .wl-x:focus { opacity: 1; }
.wl-x:hover { color: var(--text-strong); }
/* Abhaken (Migration 55): ein Kreis vor dem Kreuz. Leer = offen, Glut =
   abgehakt; der Haken erscheint erst, wenn er gilt. */
.wl-tickable { grid-template-columns: 44px 1fr auto 28px 28px; }
.wl-tick { width: 22px; height: 22px; border-radius: 50%; padding: 0;
  border: 1.5px solid var(--border); background: none; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--on-accent); font-size: 13px; line-height: 1; }
.wl-tick:hover { border-color: var(--accent); }
.wl-tick.on { border-color: var(--accent); background: var(--accent); }
.wl-tick.on::after { content: "✓"; }
.wl-done > summary { cursor: pointer; list-style: none; }
.wl-done > summary::-webkit-details-marker { display: none; }

/* Die Fundzeile hat keinen Haken und keine Bestellmarke — beides gehoert zum
   Verkauf. Bleiben vier Spalten statt sechs. */
.wl-pick { grid-template-columns: 44px 40px 1fr auto; }

/* Angebote (Migration 56): auf der Wunschliste eines Freundes je Zeile am Ende
   ein Haken „geben" und, wenn er mehr als eine braucht, wie viele. */
.wl-offerable { grid-template-columns: 44px 1fr auto auto; }
.wl-give { display: inline-flex; align-items: center; justify-content: flex-end;
  gap: 8px; min-width: 22px; }
.wl-give input { width: 17px; height: 17px; margin: 0; accent-color: var(--accent); }
.wl-give-qty { padding: 4px 6px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--surface); color: var(--text); font-size: 12.5px; }

/* ---------- Leere Sammlung: der Startschirm ---------- */

/* Vier leere Abschnitte mit ihren Reglern darüber berichten über nichts. Also
   steht dort dann, was zu tun ist — fünf Wörter und zwei Knöpfe. */
.startup { display: flex; flex-direction: column; align-items: center;
  gap: 6px; padding: 74px 20px 90px; text-align: center; }
.su-mark { opacity: .9; margin-bottom: 10px; }
.su-mark img { display: block; }
.su-title { font-size: 21px; font-weight: 800; color: var(--text-strong);
  letter-spacing: -.2px; }
.su-line { font-size: 14px; color: var(--text-dim); }
.su-stores { display: flex; flex-wrap: wrap; justify-content: center; gap: 12px;
  margin-top: 20px; }

/* Die Abzeichen der beiden Läden. Nachgebaut und nicht die amtlichen Bilder:
   die sind geschützt, und hier steht ohnehin ein Platzhalter — die App gibt es
   noch nicht. Zwei Zeilen wie im Original, die kleine oben. */
.storebadge { display: inline-flex; align-items: center; gap: 10px;
  padding: 9px 16px; border-radius: 11px; text-decoration: none;
  border: 1px solid var(--border); background: var(--surface-2);
  color: var(--text-strong); }
.storebadge:hover { border-color: var(--accent-border); }
.sb-text { display: flex; flex-direction: column; line-height: 1.15;
  font-size: 14px; font-weight: 700; text-align: left; }
.sb-top { font-size: 9.5px; font-weight: 600; letter-spacing: .4px;
  text-transform: uppercase; color: var(--text-faint); }

/* ---------- Marktplätze im Kartendetail ---------- */
/* Eine Zeile je Anbieter: Name links, Datum und Betrag rechts. Kein Raster mit
   Spaltenköpfen — vier Angaben tragen keine Tabelle, und der Name des
   Marktplatzes sagt schon, was die Zahl bedeutet. */
.quotes { display: flex; flex-direction: column; }
.quote { display: flex; align-items: baseline; gap: 10px;
  padding: 7px 2px; border-top: 1px solid var(--border-soft);
  font-variant-numeric: tabular-nums; }
.quote:first-child { border-top: 0; }
.quote-where { color: var(--text-strong); }
.quote-when { font-size: 12px; color: var(--text-dim); }
.quote-amount { font-weight: 650; color: var(--text-strong); }
/* Der gewählte Marktplatz ist der, dessen Zahl oben auf der Kachel steht. */
.quote.on .quote-where, .quote.on .quote-amount { color: var(--accent); }

/* ---------- Kontomenü in der Kopfzeile ---------- */

/* Es hängt an der rechten Kante, also klappt es nach links auf — nach rechts
   hinge es aus dem Fenster. */
.acctmenu .acctpop { right: 0; width: 200px; }
/* Der Name ist keine Sache, die man anklickt, sondern die Auskunft, wem das hier
   gehört: also der Kopf des Menüs und keine Zeile darin. Die Adresse stand
   darunter und ist wieder weg — sie gehört ins Profil, wo man sie ändern kann. */
.acct-id { padding: 8px 9px 10px; border-bottom: 1px solid var(--border-soft);
  margin-bottom: 4px; }
.acct-who { font-size: 13px; font-weight: 700; color: var(--text-strong); }
/* Ein Knopf, der wie eine Menüzeile aussieht: Abmelden muss ein Formular
   abschicken, kann also kein Link sein. Eigener Name und nicht `.linkbtn`: den
   gibt es schon, mit eigener Schriftgröße — die Zeile fiel damit aus der Reihe.
   Nur die Knopf-Eigenheiten fallen weg, alles andere kommt von `.vp-opt`, und
   genau deshalb trägt der Knopf diese Klasse jetzt selbst: vorher lag sie am
   Formular darum, und damit färbte sich beim Darüberfahren eine andere Fläche
   als die, die klickt — die Zeile fühlte sich anders an als „Profil“ darüber. */
.vp-btn { border: 0; background: none; cursor: pointer;
  font-family: inherit; text-align: left; width: 100%; }
.acct-form { display: block; }

/* Ein offenes Menü gehört nach oben. Eine Kachel bekommt beim Darüberfahren ein
   `transform` (`.tile:hover`), und das macht sie zu einem eigenen Malgrund, der
   über allem liegt, was keine Schichtnummer trägt — sie schob sich vor das
   offene Menü. Nur solange offen, damit die Kopfzeile sonst nichts überdeckt. */
.viewmenu:has(> .viewpop:not([hidden])) { z-index: 70; }

/* Profil und Hell/Dunkel gehören zusammen. Die Kopfzeile setzt 28px zwischen
   ihre Teile — richtig zwischen Name, Navigation und dem Rest, viel zu viel
   zwischen zwei Knöpfen, die dasselbe Eck bilden. */
.headtools { display: flex; align-items: center; gap: 8px; flex: none; }

/* Der Spielumschalter. Ein nacktes Auswahlfeld, kein Knopf mit Menue: Es ist
   eine Wahl aus einer kurzen Liste und keine Aktion, und ein Auswahlfeld sagt
   das von selbst — am Telefon zeichnet das System sogar sein eigenes.

   Zurueckhaltend gehalten, weil er nicht das Wichtigste in der Kopfzeile ist:
   Wer eine Sammlung ansieht, weiss, welches Spiel er sammelt. Er muss sich nur
   finden lassen, wenn man ihn sucht. Erscheint ohnehin erst ab dem zweiten
   Spiel (base.html). */
.gameswitch { flex: none; margin-right: 4px; }
.gameswitch select { appearance: none; border: 1px solid var(--border-soft);
  border-radius: 8px; padding: 5px 26px 5px 10px; font: inherit; font-size: 13px;
  color: var(--text-dim); background: transparent;
  /* Das Dreieck als Hintergrundbild statt als Zeichen daneben: ein zweites
     Element haette sich beim Zoomen vom Feld geloest. */
  background-image: linear-gradient(45deg, transparent 50%, currentColor 50%),
                    linear-gradient(135deg, currentColor 50%, transparent 50%);
  background-position: right 12px center, right 7px center;
  background-size: 5px 5px, 5px 5px; background-repeat: no-repeat;
  cursor: pointer; }
.gameswitch select:hover { color: var(--text); border-color: var(--border); }

/* So breit wie die Passwortfelder darunter. Über die ganze Panelbreite gezogen
   sah das eine Feld nach mehr Eingabe aus als es ist. */
.rowform .textin { max-width: 320px; }

/* Der Stand einer Adresse. Bestätigt ist eine Feststellung, unbestätigt ein
   Zustand, der etwas von einem will — deshalb trägt nur der zweite Farbe. */
.mailstate { font-size: 12.5px; font-weight: 700; }
.mailstate.ok { color: var(--up); }
.mailstate.warn { color: var(--warn); }
/* Feld und Knopf in einer Zeile, wie sonst auch. */
.rowform { display: flex; gap: 8px; flex: 1; min-width: 0; }

/* Die Marken unter einem Passwortfeld. Drei kurze Wörter in einer Zeile, keine
   Leiste und kein Prozentwert: was zu tun ist, sind drei Dinge, und ein Balken
   sagte davon keines.

   Nur das Erfüllte trägt Farbe. Ein rotes Kreuz an einem Feld, in dem gerade
   der dritte Buchstabe steht, schimpft über etwas, das noch niemand falsch
   gemacht hat — der Haken ist grau, bis er verdient ist. */
.pwrules { list-style: none; margin: 8px 0 0; padding: 0; display: flex;
  flex-wrap: wrap; gap: 4px 14px; }
.pwrules:empty { display: none; }
.pwrule { font-size: 11.5px; color: var(--text-faint); padding-left: 15px;
  position: relative; }
.pwrule::before { content: '\2713'; position: absolute; left: 0;
  font-weight: 700; color: var(--text-faint); opacity: .45; }
.pwrule.ok { color: var(--text-dim); }
.pwrule.ok::before { color: var(--up); opacity: 1; }


/* ---------- Profil: Reiter statt eines langen Schirms ---------- */

/* Dieselbe Form wie die Zeitfenster in der Auswertung (`.chips`), nur als Wege:
   ein Reiter ist eine Adresse und kein Umschalter — man kann ihn verlinken, und
   nach einem POST kommt man dorthin zurück, wo man war. */
.ktabs { display: flex; gap: 3px; margin-bottom: 16px; padding: 3px;
  background: var(--surface-2); border: 1px solid var(--border); border-radius: 9px;
  align-self: flex-start; width: fit-content; }
.ktabs a { padding: 6px 13px; border-radius: 6px; font-size: 12.5px; font-weight: 600;
  color: var(--chip-text); }
.ktabs a.on { background: var(--accent-soft); color: var(--chip-a-text); }

/* Was dasteht, ohne dass man es ändern kann: die Adresse, „An"/„Aus", die
   Währung neben einem Marktplatz. Kein Feld, also auch nicht wie eins. */
.kval { font-size: 13px; color: var(--text-strong); }

/* Der Fehler bei dem Feld, das er meint — und nur, wenn es einen gibt. Rot und
   eine Zeile: was hier steht, ist immer ein Satz, der eine Handlung nennt. */
/* Die ruhige Schwester von `.login-err`: „die Mail ist unterwegs" ist keine
   Fehlermeldung, steht aber an derselben Stelle. */
.login-note { margin-bottom: 12px; font-size: 12.5px; color: var(--text-dim); }
.ferr { margin-top: 8px; font-size: 12px; font-weight: 600; color: var(--danger-text); }
/* Was gleich geschieht, bevor jemand tippt. Grau, weil es keine Warnung ist —
   nur die Folge, die man kennen sollte. */
.fnote { margin-top: 8px; font-size: 11.5px; color: var(--text-faint); }

/* Der QR-Code zum Koppeln eines Telefons. Immer schwarz auf weiss, auch im
   Dunkelmodus — ein Scanner liest Kontrast, und ein heller Code auf dunklem
   Grund ist einer, den er verweigern darf. Der weisse Rand gehoert zum Code. */
.pairqr { display: flex; justify-content: center; margin-bottom: 16px; }
.pairqr svg { width: 232px; height: 232px; max-width: 100%; border-radius: 10px; }

/* „Merken" und „Beenden" gehören in eine Leiste. Als zwei Formulare nebeneinander
   trennte sie der Zeilenabstand weiter voneinander als das Feld von seinem
   eigenen Knopf. */
.rowbar { display: flex; align-items: center; gap: 8px; flex: none; }

/* "Exact" (Migration 59): je Wunsch ein leiser Knopf am Ende der Metazeile.
   Ungesetzt erscheint er erst beim Darueberfahren; gesetzt nennt er, was der
   Wunsch verlangt („Foil - DE - NM or better“), in Glut. */
.wl-exact { background: none; border: 0; padding: 0 0 0 6px; cursor: pointer;
  font: inherit; font-size: 12px; color: var(--text-dim); opacity: 0;
  transition: opacity .14s; }
.wl-row:hover .wl-exact, .wl-exact:focus, .wl-exact.on { opacity: 1; }
.wl-exact:hover { color: var(--text-strong); }
.wl-exact.on { color: var(--accent); font-weight: 600; }
.exact-sel { flex: 1; min-width: 0; padding: 8px 11px; border: 1px solid var(--border);
  border-radius: 8px; background: var(--surface-2); color: var(--text-strong); font: inherit; }
/* Why the wish asks for another language than the one chosen (it does not
   exist there, Leon 05.11.2026), under the picker. */
.exact-warn { margin: -4px 0 10px 104px; }

/* Mehrere Karten auf einmal (Leon, 05.11.2026: "select several cards to give
   them properties"). "Select" schaltet den Modus ein (`select.js`): ein Klick
   waehlt eine Karte, statt sie zu oeffnen. Gewaehlt heisst Glutrand und ein
   Haken oben rechts; eine Kachel, von deren Exemplaren nur ein Teil gewaehlt
   ist, traegt einen Strich. Der Modus haengt an <html>, weil das Raster beim
   Filtern getauscht wird. */
:root[data-selecting] .tile::after { content: ""; position: absolute; top: 8px; right: 8px;
  z-index: 3; box-sizing: border-box; width: 22px; height: 22px; border-radius: 50%;
  border: 2px solid rgba(255,255,255,.8); background: rgba(12,13,15,.45);
  display: flex; align-items: center; justify-content: center;
  font-size: 13px; font-weight: 800; line-height: 1; color: var(--on-accent); }
:root[data-selecting] .tile.picked { border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent); }
:root[data-selecting] .tile.picked::after { content: "\2713"; border-color: var(--accent);
  background: var(--accent); }
:root[data-selecting] .tile.part::after { content: "\2013"; border-color: var(--accent);
  background: var(--accent); }
/* the commander mark steps aside for the check */
:root[data-selecting] .tag-tr { right: 38px; }
:root[data-selecting] .cardrow.picked { background: var(--accent-soft);
  box-shadow: inset 3px 0 0 var(--accent); }
:root[data-selecting] .cardrow.part { box-shadow: inset 3px 0 0 var(--accent-border); }
/* room under the last row for the bar, and the note above it */
:root[data-selecting] .grid-scroll { padding-bottom: 110px; }
:root[data-selecting] .notebar { bottom: 92px; }

/* Die Leiste unten: wie viele, alle waehlen, und was mit ihnen geschieht.
   Schwebt ueber dem Raster, mittig, und liegt ausserhalb des Filterformulars
   (`collection.html`). */
.selbar { position: fixed; left: 50%; bottom: 18px; transform: translateX(-50%); z-index: 80;
  box-sizing: border-box; width: min(880px, calc(100vw - 32px));
  display: flex; align-items: center; flex-wrap: wrap; gap: 10px; padding: 10px 12px;
  border: 1px solid var(--border); border-radius: 14px; background: var(--surface);
  box-shadow: 0 18px 40px -18px rgba(0,0,0,.6); }
.sel-count { padding: 0 4px; font-size: 13px; font-weight: 700; white-space: nowrap;
  color: var(--chip-a-text); }
.selbar .primary-btn, .selbar .danger-btn { height: var(--ctl-h); padding-top: 0;
  padding-bottom: 0; }
.selbar [disabled] { opacity: .45; cursor: not-allowed; }
.sel-move { width: auto; max-width: 210px; height: var(--ctl-h); margin-bottom: 0; }
.selmenu { position: relative; }
.selpop { position: absolute; bottom: calc(100% + 10px); left: 0; z-index: 81;
  width: 340px; padding: 10px 12px; display: flex; flex-direction: column; gap: 7px;
  border: 1px solid var(--border); border-radius: 12px; background: var(--surface);
  box-shadow: 0 18px 40px -18px rgba(0,0,0,.6); }
.sel-row { display: flex; align-items: center; gap: 8px; }
.sel-label { width: 82px; flex: none; font-size: 12px; font-weight: 700; color: var(--text-dim); }
.sel-row .printsel { flex: 1; min-width: 0; margin-bottom: 0; }
.sel-price { max-width: 120px; }

/* A note that says something could not be done. */
.notebar-error { border-color: var(--danger-border); background: var(--danger-bg);
  color: var(--danger-text); }

/* Suggestions instead of automatic ticks (migration 63, Leon 05.11.2026):
   the copies in one's boxes that answer an open wish, in its row, each with
   "Tick off" and "Not for this list"; above the list the same two for all.
   Quiet: a line of meta text and two small text buttons, not a second row. */
.wl-suggestbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-bottom: 16px; padding: 10px 14px; border-radius: 10px;
  border: 1px solid var(--accent-border); background: var(--accent-soft);
  color: var(--chip-a-text); font-size: 13px; font-weight: 600; }
.wl-suggestbar form { margin: 0; }
.wl-sbar-btn { height: 30px; padding: 0 12px; font-size: 12.5px; }
.wl-suggest { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin-top: 4px; font-size: 11.5px; color: var(--text-dim); }
.wl-suggest form { margin: 0; }
.wl-suggest-where { min-width: 0; }
.wl-suggest-acts { display: inline-flex; align-items: center; gap: 10px; white-space: nowrap; }
.wl-suggest-acts form { margin: 0; }
@media (max-width: 640px) {
  .wl-suggest { flex-direction: column; align-items: flex-start; gap: 3px; }
  .wl-suggestbar .spacer { display: none; }
}
.wl-suggest-proxy { position: static; font-size: 9.5px; padding: 2px 6px; }
.wl-sbtn { border: 0; background: none; padding: 0; cursor: pointer;
  font: inherit; font-weight: 600; color: var(--accent);
  text-decoration: underline; text-underline-offset: 3px; }
.wl-sbtn-quiet { color: var(--text-dim); font-weight: 500; }
.wl-sbtn:hover { color: var(--chip-a-text); }
.home-suggest { margin-bottom: 18px; }
.home-suggest-link { color: var(--accent); text-decoration: underline;
  text-underline-offset: 3px; }
