/* Gestaltung des Spieltisches.
 *
 * Sämtliche Farben kommen aus tokens.css. Hier stehen nur Anordnung und Form — wer die
 * Anmutung ändern will, fasst die Marken an und nicht diese Datei.
 */

* { box-sizing: border-box; }

/* Bildlaufleisten in der Farbe der Oberfläche. Ohne das zeichnet Windows einen breiten,
   hellen Balken mit Pfeilkästchen an beiden Enden — auf einem dunklen Tisch ist das der
   auffälligste Streifen im Bild, obwohl er das Unwichtigste zeigt. */
* {
    scrollbar-width: thin;
    scrollbar-color: var(--scroll-thumb) transparent;
}

::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }

/* Der durchsichtige Rahmen samt padding-box lässt den Balken schmaler wirken, als seine
   Spur breit ist — sonst klebt er an den Zeilen daneben. */
::-webkit-scrollbar-thumb {
    background: var(--scroll-thumb);
    background-clip: padding-box;
    border: 2px solid transparent;
    border-radius: var(--radius-pill);
}

::-webkit-scrollbar-thumb:hover { background: var(--scroll-thumb-hover); background-clip: padding-box; }
::-webkit-scrollbar-corner { background: transparent; }

html, body {
    margin: 0;
    background: var(--bg);
    color: var(--text);
    font-family: var(--font);
    font-size: 15px;
    -webkit-font-smoothing: antialiased;
}

/* Der Tisch soll den Bildschirm ausfüllen, nicht nur mindestens so hoch sein.
   Deshalb durchgehend feste Höhe statt min-height und überall min-height: 0 — ohne das
   weigern sich Grid- und Flex-Kinder zu schrumpfen und wachsen über den Rand hinaus. */
.app { height: 100vh; display: flex; flex-direction: column; overflow: hidden; }
.top-bar { flex: none; }
main { flex: 1; min-height: 0; padding: 0.9rem 1.1rem 1.1rem; overflow: auto; }

/* ================================================================
   Kopfleiste
   ================================================================ */

.top-bar {
    display: flex;
    align-items: center;
    gap: 1rem;
    padding: 0.85rem 1.25rem;
    background: var(--bg-bar);
    border-bottom: 1px solid var(--line-soft);
}

.brand {
    display: inline-flex;
    align-items: baseline;
    gap: 0.25rem;
    color: var(--text);
    text-decoration: none;
    font-size: 1.15rem;
    font-weight: 700;
    letter-spacing: -0.01em;
}

/* Der Punkt der Wortmarke trägt die Betonungsfarbe — das einzige farbige Element links. */
.brand::after {
    content: "";
    width: 0.3rem;
    height: 0.3rem;
    border-radius: 50%;
    background: var(--accent);
}

.top-bar .spacer { flex: 1; }

/* Der Raumcode wird am Telefon vorgelesen: einzeln stehende Zeichen, feste Breite. */
.room-code {
    font-family: var(--font-mono);
    font-size: 0.95rem;
    letter-spacing: 0.28em;
    color: var(--text-dim);
    text-transform: uppercase;
}

.room-code strong { color: var(--text); font-weight: 600; }

/* ================================================================
   Grundformen
   ================================================================ */

.chip {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    padding: 0.2rem 0.55rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--line);
    background: var(--panel);
    font-size: 0.7rem;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    color: var(--text-dim);
    white-space: nowrap;
}

.chip.accent { border-color: var(--accent-line); background: var(--accent-soft); color: var(--accent); }
.chip.danger { border-color: var(--danger-line); background: var(--danger-soft); color: var(--danger); }

.badge {
    display: inline-flex;
    align-items: center;
    padding: 0.05rem 0.4rem;
    margin-left: 0.35rem;
    border-radius: var(--radius-pill);
    background: var(--panel-raised);
    border: 1px solid var(--line);
    font-size: 0.72rem;
    color: var(--text-dim);
}

.badge.party { font-weight: 700; letter-spacing: 0.03em; }
.badge.party.re { background: var(--re-soft); border-color: var(--re-line); color: var(--re-ink); }
.badge.party.kontra { background: var(--kontra-soft); border-color: var(--kontra-line); color: var(--kontra-ink); }
.badge.bock { background: var(--danger-soft); border-color: var(--danger-line); color: var(--danger); }
.badge.away { background: rgba(224, 166, 60, 0.12); border-color: var(--accent-line); color: var(--accent); }

button {
    padding: 0.5rem 0.85rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--panel);
    color: var(--text);
    font: inherit;
    font-size: 0.88rem;
    cursor: pointer;
    transition: background 0.12s, border-color 0.12s, color 0.12s;
}

button:hover:not(:disabled) { background: var(--panel-raised); border-color: var(--accent-line); }
button:disabled { opacity: 0.4; cursor: not-allowed; }

button.primary {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-ink);
    font-weight: 600;
}

button.primary:hover:not(:disabled) { background: #efb84e; border-color: #efb84e; }

button.ghost { background: transparent; border-color: transparent; color: var(--text-dim); }
button.ghost:hover:not(:disabled) { color: var(--text); background: var(--panel); }

button.danger { background: var(--danger-soft); border-color: var(--danger-line); color: var(--danger); }
button.danger:hover:not(:disabled) { background: rgba(200, 84, 74, 0.22); }

.text {
    padding: 0.5rem 0.7rem;
    border: 1px solid var(--line);
    border-radius: var(--radius-sm);
    background: var(--bg);
    color: var(--text);
    font: inherit;
    font-size: 0.9rem;
}

.text:focus { outline: none; border-color: var(--accent-line); }
.text::placeholder { color: var(--text-faint); }
.text.code { width: 7rem; text-transform: uppercase; letter-spacing: 0.2em; font-family: var(--font-mono); }
.text.tiny { width: 4.5rem; }

.row { display: flex; gap: 0.5rem; align-items: center; flex-wrap: wrap; }
.muted { color: var(--text-dim); font-size: 0.85rem; }
.error { color: var(--danger); }
.prompt { font-weight: 600; font-size: 0.85rem; }
.number { text-align: right; font-variant-numeric: tabular-nums; }
.negative { color: var(--danger); }

.panel {
    background: var(--panel);
    border: 1px solid var(--line-soft);
    border-radius: var(--radius);
    padding: 1rem;
    margin-bottom: 0.85rem;
}

.panel h2, .panel h3 {
    margin: 0 0 0.7rem;
    font-size: 0.72rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--text-faint);
    font-weight: 600;
}

/* ================================================================
   Sitzkennzeichen
   ================================================================ */

/* Jeder Platz hat eine feste Farbe. Sie taucht am Tisch, im Punktestand und in der
   Sprachrunde identisch auf — dadurch findet man einen Mitspieler überall wieder. */
.avatar {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.6rem;
    height: 2.6rem;
    border-radius: var(--radius-sm);
    font-size: 0.98rem;
    font-weight: 700;
    letter-spacing: 0.01em;
    color: #fff;
    flex: none;
}

.seat-0 { background: var(--seat-0); color: var(--accent-ink); }
.seat-1 { background: var(--seat-1); }
.seat-2 { background: var(--seat-2); }
.seat-3 { background: var(--seat-3); }

/* ================================================================
   Lobby
   ================================================================ */

/* Die Startseite hat ihren eigenen Untergrund: ein Filzgrün, das nach unten ins Dunkle
   ausläuft. Der Spieltisch bleibt beim ruhigen Fast-Schwarz — dort soll nichts vom Blatt
   ablenken. */
.lobby-page {
    flex: 1;
    min-height: 0;
    display: flex;
    flex-direction: column;
    background:
        radial-gradient(ellipse 90% 55% at 50% -8%, #1e5c3d 0%, #164a32 30%, #0d2c1d 60%, #070d0a 100%);
}

.lobby-bar {
    display: flex;
    align-items: center;
    width: min(58rem, 100% - 3rem);
    margin: 0 auto;
    padding: 1.4rem 0 0.5rem;
}

.live-pill {
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.25rem 0.7rem;
    border-radius: var(--radius-pill);
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid rgba(255, 255, 255, 0.09);
    font-size: 0.8rem;
    color: var(--text-dim);
}

.live-pill .dot {
    width: 0.4rem;
    height: 0.4rem;
    border-radius: 50%;
    background: #4ec27d;
    box-shadow: 0 0 0 3px rgba(78, 194, 125, 0.18);
}

.live-pill.small { font-size: 0.72rem; padding: 0.15rem 0.5rem; }

/* Die Lobby rollt nicht als Ganzes: Der Aufmacher, das Namensfeld und die beiden Kacheln
   stehen fest, gerollt wird allein die Tischliste — und zwar dort, wo sie steht. Zuvor war
   das ganze <main> der Rollbereich; dessen Bildlaufleiste saß am äußersten Fensterrand,
   auf einem breiten Schirm eine halbe Bildschirmbreite neben der 58-rem-Spalte, auf die
   sie sich bezog. Sie sah damit aus, als betreffe sie die ganze Seite. */
.lobby {
    width: min(58rem, 100% - 3rem);
    margin: 0 auto;
    padding-bottom: 1.5rem;
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.lobby > .hero,
.lobby > .name-block,
.lobby > .lobby-cards,
.lobby > .notice { flex: none; }

.open-tables { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.open-tables h3 { flex: none; }

/* Etwas Luft rechts, damit die Bildlaufleiste nicht auf den Zeilen klebt. */
.table-list { flex: 1; min-height: 0; overflow-y: auto; padding-right: 0.45rem; }

/* Auf einem niedrigen Fenster bliebe für die Liste nichts übrig. Dann rollt wieder die
   ganze Seite — eine Liste mit drei sichtbaren Pixeln wäre schlimmer als eine
   Bildlaufleiste am Rand. */
@media (max-height: 46rem) {
    .lobby { overflow: visible; display: block; padding-bottom: 4rem; }
    .open-tables, .table-list { display: block; overflow: visible; min-height: 0; padding-right: 0; }
}

/* ---- Aufmacher ---- */

.hero { text-align: center; padding: 1.5rem 0 2rem; }

.hero-cards { display: flex; justify-content: center; gap: 0.2rem; margin-bottom: 1.5rem; }

.hero-card {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 3.1rem;
    height: 4.3rem;
    border-radius: 8px;
    background: var(--card-face);
    color: var(--card-black);
    font-size: 1.15rem;
    box-shadow: 0 8px 18px rgba(0, 0, 0, 0.35);
    animation: hero-float 5s ease-in-out infinite;
}

.hero-card.red { color: var(--card-red); }

/* Jede Karte etwas anders gekippt und zeitversetzt — sonst wippen alle im Gleichschritt
   und es wirkt mechanisch. */
.hero-card:nth-child(1) { transform: rotate(-9deg); animation-delay: 0s; }
.hero-card:nth-child(2) { transform: rotate(-3deg); animation-delay: 0.4s; }
.hero-card:nth-child(3) { transform: rotate(3deg); animation-delay: 0.8s; }
.hero-card:nth-child(4) { transform: rotate(9deg); animation-delay: 1.2s; }

@keyframes hero-float {
    0%, 100% { translate: 0 0; }
    50% { translate: 0 -7px; }
}

.hero h1 {
    margin: 0 0 0.9rem;
    font-size: clamp(2rem, 4.5vw, 3rem);
    font-weight: 800;
    letter-spacing: -0.02em;
}

.lead { margin: 0 auto; max-width: 28rem; color: var(--text-dim); line-height: 1.55; }

/* ---- Name ---- */

.name-block { text-align: center; margin-bottom: 1.6rem; }

.field-label {
    display: block;
    margin-bottom: 0.6rem;
    font-size: 0.68rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--text-faint);
    font-weight: 600;
}

.name-field {
    width: min(25rem, 100%);
    padding: 0.85rem 1rem;
    text-align: center;
    font-size: 1.05rem;
    background: rgba(0, 0, 0, 0.25);
    border-radius: var(--radius);
}

/* ---- Die beiden Karten ---- */

.lobby-cards { display: grid; grid-template-columns: 1fr 1fr; gap: 1rem; margin-bottom: 2rem; }

@media (max-width: 46rem) {
    .lobby-cards { grid-template-columns: 1fr; }
}

.lobby-card {
    background: rgba(0, 0, 0, 0.28);
    border: 1px solid rgba(255, 255, 255, 0.07);
    border-radius: var(--radius-lg);
    padding: 1.2rem;
}

.lobby-card h2 {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    margin: 0 0 0.5rem;
    font-size: 1rem;
    font-weight: 700;
    text-transform: none;
    letter-spacing: 0;
    color: var(--text);
}

.card-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.75rem;
    height: 1.75rem;
    border-radius: var(--radius-sm);
    background: rgba(78, 194, 125, 0.16);
    color: #6fd396;
    font-size: 0.95rem;
}

.lobby-card p.muted { margin: 0 0 0.9rem; }
.lobby-card form.row { flex-wrap: nowrap; }
.lobby-card form.row .text { flex: 1; min-width: 0; background: rgba(0, 0, 0, 0.3); }

.lobby-card .code {
    width: auto;
    font-family: var(--font-mono);
    letter-spacing: 0.3em;
    text-transform: uppercase;
}

button.outline { background: transparent; border-color: var(--accent-line); color: var(--accent); font-weight: 600; }
button.outline:hover:not(:disabled) { background: var(--accent-soft); }

.hint { margin: 0.7rem 0 0; font-family: var(--font-mono); font-size: 0.72rem; color: var(--text-faint); }
.hint.error { color: var(--danger); font-family: var(--font); }

.preset-pills { display: flex; gap: 0.4rem; margin-top: 0.9rem; flex-wrap: wrap; }

.pill {
    flex: 1;
    padding: 0.45rem 0.6rem;
    border-radius: var(--radius-sm);
    background: transparent;
    border: 1px solid var(--line);
    font-size: 0.82rem;
    white-space: nowrap;
}

.pill.active { background: var(--accent-soft); border-color: var(--accent-line); color: var(--accent); font-weight: 600; }

.solo-button {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.1rem;
    width: 100%;
    margin-top: 0.6rem;
    padding: 0.6rem 0.8rem;
    text-align: left;
    border-style: dashed;
    background: transparent;
}

.solo-button:hover:not(:disabled) { border-style: solid; }
.solo-button .muted { font-size: 0.75rem; }

/* ---- Offene Tische ---- */

.open-tables h3 {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    margin: 0 0 0.7rem;
    font-size: 0.68rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--text-faint);
    font-weight: 600;
}

.open-tables .empty { padding: 1rem; text-align: center; }

.table-row {
    display: flex;
    align-items: center;
    gap: 0.9rem;
    padding: 0.8rem 1rem;
    margin-bottom: 0.5rem;
    border-radius: var(--radius);
    background: rgba(0, 0, 0, 0.28);
    border: 1px solid rgba(255, 255, 255, 0.07);
}

.table-row .room-code { color: var(--accent); flex: none; }
.table-ident { flex: 1; min-width: 0; display: flex; flex-direction: column; }
.table-name { font-weight: 600; font-size: 0.95rem; }

/* ================================================================
   Raum
   ================================================================ */

/* Keine Breitenbegrenzung: Ein Kartentisch darf den Bildschirm nutzen, den er bekommt. */
.room { height: 100%; display: flex; flex-direction: column; min-height: 0; }

.room-body {
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-columns: minmax(0, 1fr) 21rem;
    gap: 1rem;
    align-items: stretch;
}

.table-area { min-height: 0; display: flex; flex-direction: column; }
.sidebar { min-height: 0; overflow-y: auto; display: flex; flex-direction: column; }

@media (max-width: 74rem) {
    .room-body { grid-template-columns: minmax(0, 1fr); grid-template-rows: minmax(0, 1fr) auto; }
    .app { height: auto; min-height: 100vh; overflow: visible; }
}

.notice {
    background: var(--accent-soft);
    border: 1px solid var(--accent-line);
    border-radius: var(--radius-sm);
    padding: 0.55rem 0.8rem;
    margin-bottom: 0.8rem;
    font-size: 0.88rem;
    cursor: pointer;
}

.notice.error { background: var(--danger-soft); border-color: var(--danger-line); color: var(--danger); }

/* Meldungen am Tisch schweben, statt den Tisch nach unten zu drücken. Ohne das rutscht
   das eigene Blatt jedes Mal, wenn der Server etwas mitteilt. */
.toasts {
    position: fixed;
    right: 1.25rem;
    top: 4.5rem;
    z-index: 60;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    width: min(26rem, calc(100vw - 2.5rem));
    pointer-events: none;
}

.toasts .notice { margin: 0; pointer-events: auto; box-shadow: var(--shadow); background: var(--panel-raised); }
.toasts .notice.error { background: var(--panel-raised); }

/* ---- Modale Fenster ---- */

.modal-backdrop {
    position: fixed;
    inset: 0;
    z-index: 80;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: clamp(1rem, 5vh, 3rem);
    background: rgba(4, 8, 6, 0.66);
    outline: none;
}

.modal {
    display: flex;
    flex-direction: column;
    width: min(38rem, 100%);
    max-height: 100%;
    border-radius: var(--radius-lg);
    border: 1px solid var(--line);
    background: var(--panel);
    box-shadow: var(--shadow-lift);
}

.modal.wide { width: min(64rem, 100%); }

.modal-head {
    display: flex;
    align-items: baseline;
    gap: 0.7rem;
    padding: 0.9rem 1.1rem;
    border-bottom: 1px solid var(--line-soft);
}

.modal-head h2 { margin: 0; font-size: 1rem; font-weight: 600; letter-spacing: 0.01em; }
.modal-head .muted { font-size: 0.82rem; }

/* Der Schließer wird nach rechts geschoben, damit zwischen Titel und Untertitel kein
   künstlicher Abstand entsteht. */
.modal-head .close { margin-left: auto; align-self: center; padding: 0.2rem 0.55rem; font-size: 0.9rem; }

.modal-body { padding: 1.1rem; overflow: auto; }
.modal-body > :first-child { margin-top: 0; }

.modal-foot { padding: 0.8rem 1.1rem; border-top: 1px solid var(--line-soft); display: flex; gap: 0.5rem; }

/* ---- Platzverwaltung vor der Partie ---- */

.seat-list { display: flex; flex-direction: column; gap: 0.4rem; }

.seat-row {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.5rem 0.6rem;
    border-radius: var(--radius-sm);
    background: rgba(0, 0, 0, 0.18);
}

.seat-name { width: 3.5rem; font-weight: 600; font-size: 0.85rem; color: var(--text-dim); }
.occupant { font-size: 0.9rem; }
.occupant.bot { font-style: italic; }
.occupant.away { opacity: 0.6; }
.seat-row .spacer { flex: 1; }

/* ================================================================
   Der Tisch
   ================================================================ */

.table {
    position: relative;
    flex: 1;
    min-height: 0;
    display: grid;
    grid-template-columns: 16rem minmax(0, 1fr) 16rem;
    grid-template-rows: auto minmax(0, 1fr) auto;
    grid-template-areas:
        ".    top    ."
        "left trick right"
        "hand hand   hand";
    gap: 1rem;
    padding: 1.25rem;
    border-radius: var(--radius-lg);
    border: 1px solid var(--felt-line);
    background:
        radial-gradient(ellipse 70% 60% at 50% 42%, var(--felt-center), var(--felt-edge) 78%);
    box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.05), var(--shadow);
}

.opponent.top { grid-area: top; justify-self: center; }
.opponent.left { grid-area: left; align-self: center; }
.opponent.right { grid-area: right; align-self: center; }
.trick-area { grid-area: trick; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 0.8rem; }
.my-area { grid-area: hand; }

/* ---- Mitspieler ---- */

.opponent-box {
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.6rem 0.8rem;
    border-radius: var(--radius);
    background: rgba(0, 0, 0, 0.3);
    border: 1px solid transparent;
    transition: border-color 0.15s, background 0.15s;
}

/* Wer am Zug ist, bekommt den einzigen farbigen Rahmen am Tisch. */
.opponent-box.active {
    border-color: var(--accent-line);
    background: rgba(224, 166, 60, 0.1);
}

.opponent-ident { min-width: 0; flex: 1; }

/* Am Tisch liest man Namen im Vorbeischauen. Neben dem Kartenbild waren 0,88 rem dafür
   zu wenig — Platz ist an den Seiten reichlich. */
.opponent-name { font-size: 1.1rem; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.opponent-status {
    display: flex;
    align-items: center;
    gap: 0.35rem;
    font-size: 0.84rem;
    color: var(--text-faint);
}

.opponent-status .dot { width: 0.4rem; height: 0.4rem; border-radius: 50%; background: var(--text-faint); }
.opponent-box.active .opponent-status .dot { background: var(--accent); }

.hand-count {
    font-variant-numeric: tabular-nums;
    font-size: 0.95rem;
    font-weight: 600;
    color: var(--text-dim);
    background: rgba(0, 0, 0, 0.35);
    border-radius: var(--radius-sm);
    padding: 0.15rem 0.45rem;
}

/* ---- Partei und Ansagen je Platz ---- */

/* Feste Mindesthöhe: Sagt jemand mitten im Spiel an, sollen die Kacheln nicht springen. */
.seat-calls {
    display: flex;
    gap: 0.35rem;
    justify-content: center;
    align-items: center;
    min-height: 1.95rem;
    margin-top: 0.4rem;
    flex-wrap: wrap;
}

.opponent.left .seat-calls, .opponent.right .seat-calls { justify-content: flex-start; }

/* Partei und Absage sind die beiden Angaben, nach denen man während des Spiels am
   häufigsten sucht. Als 0,72-rem-Zeile gingen sie neben dem Kartenbild unter — sie
   tragen deshalb dieselbe Schriftgröße wie der Statuszusatz und deutlich mehr Gewicht. */
.call {
    display: inline-flex;
    align-items: center;
    padding: 0.2rem 0.7rem;
    border-radius: var(--radius-pill);
    font-size: 0.92rem;
    font-weight: 700;
    letter-spacing: 0.03em;
    line-height: 1.3;
    border: 1px solid transparent;
}

/* Laut: Der Platz hat selbst angesagt und sich damit offenbart — volle Farbe. */
.call.loud.re { background: var(--re); border-color: var(--re); color: #fff; }
.call.loud.kontra { background: var(--kontra); border-color: var(--kontra); color: #fff; }

/* Leise: Die Partei liegt offen, ohne dass jemand etwas gesagt hätte — ein Solo, eine
   geklärte Hochzeit oder eine ausgespielte Kreuz-Dame. Gedämpft, aber in derselben
   Parteifarbe: Der frühere graue Rahmen war so zurückhaltend, dass man ihn übersah. */
.call.quiet.re { background: var(--re-soft); border-color: var(--re-line); color: var(--re-ink); }
.call.quiet.kontra { background: var(--kontra-soft); border-color: var(--kontra-line); color: var(--kontra-ink); }

.call.absage {
    background: var(--accent);
    border-color: var(--accent);
    color: var(--accent-ink);
    font-size: 0.85rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
}

.badge.absage { background: var(--accent); border-color: var(--accent); color: var(--accent-ink); font-weight: 600; }

/* ---- Stich und Ablegezone ---- */

.game-status {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.3rem 0.75rem;
    border-radius: var(--radius-pill);
    background: rgba(0, 0, 0, 0.32);
    border: 1px solid var(--accent-line);
    font-size: 0.72rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-dim);
}

.game-status .dot { width: 0.4rem; height: 0.4rem; border-radius: 50%; background: var(--accent); }
.game-status strong { color: var(--text); font-weight: 600; }
.game-status .sub { text-transform: none; letter-spacing: 0; color: var(--text-faint); }

/* Steht unter der Statuszeile, solange eine Hochzeit läuft: worauf gewartet wird, und
   ab wann es entschieden ist. */
.hochzeit-note {
    font-size: 0.78rem;
    color: var(--text-dim);
    background: rgba(0, 0, 0, 0.28);
    border-left: 2px solid var(--accent);
    border-radius: var(--radius-sm);
    padding: 0.3rem 0.65rem;
    max-width: 30rem;
}

/* Die Zielfläche ist absichtlich größer als die vier Karten, die darin liegen. Sie war
   einmal knapp bemessen und während eines laufenden Stichs randlos — wer eine Karte
   dorthin zog, zielte auf etwas Unsichtbares und ließ sie oft daneben los. Zusammen mit
   der Toleranz im Zeigermodul trifft man jetzt auch ungenau. */
.drop-zone {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: clamp(11rem, 26vh, 17rem);
    width: min(42rem, 100%);
    padding: 1rem;
    border: 1.5px dashed rgba(255, 255, 255, 0.1);
    border-radius: var(--radius-lg);
    transition: border-color 0.12s, background 0.12s;
}

/* Man ist am Zug: Die Fläche gibt sich schon zu erkennen, bevor gezogen wird. */
.drop-zone.inviting { border-color: rgba(255, 255, 255, 0.16); }

/* Es wird gerade gezogen — jetzt zeigt der Tisch deutlich, wohin. */
.drop-zone.drop-armed { border-color: var(--accent-line); background: rgba(224, 166, 60, 0.05); }

.drop-zone.drop-active {
    border-color: var(--accent);
    border-style: solid;
    background: var(--accent-soft);
}

/* Liegt ein fertiger Stich, tritt der Rahmen zurück — außer es wird gerade gezogen. */
.drop-zone.collected:not(.drop-armed):not(.drop-active) { border-color: transparent; }

.drop-hint { display: flex; flex-direction: column; align-items: center; gap: 0.4rem; color: var(--text-faint); font-size: 0.9rem; }
.drop-hint::before { content: "+"; font-size: 1.3rem; opacity: 0.7; }

.trick { display: flex; gap: 0.8rem; }
.trick-card { display: flex; flex-direction: column; align-items: center; gap: 0.35rem; padding: 0.3rem; border-radius: var(--radius-sm); border: 1.5px solid transparent; }
.trick-card.winner { border-color: var(--accent-line); background: var(--accent-soft); }
.trick-card .muted { color: var(--text-faint); font-size: 0.82rem; }
.trick-card .card { height: clamp(6rem, 16vh, 11rem); width: auto; aspect-ratio: 0.658; }

/* Feste Höhe: Die Zeile erscheint erst, wenn Karten liegen — ohne festen Platz schöbe sie
   beim Auftauchen die Statuszeile nach unten. */
.trick-line { min-height: 1.2rem; color: var(--text-faint); font-size: 0.85rem; }

.card.dealt { animation: fly-in 0.22s cubic-bezier(0.22, 0.8, 0.3, 1) both; }
.card.from-south { --from-x: 0; --from-y: 90px; }
.card.from-west { --from-x: -120px; --from-y: 0; }
.card.from-north { --from-x: 0; --from-y: -90px; }
.card.from-east { --from-x: 120px; --from-y: 0; }

@keyframes fly-in {
    from { opacity: 0; transform: translate(var(--from-x), var(--from-y)) scale(0.9); }
    to { opacity: 1; transform: translate(0, 0) scale(1); }
}

.status { display: flex; flex-direction: column; align-items: center; gap: 0.45rem; text-align: center; }
.status .muted { color: var(--text-faint); }

/* ---- Der abgeräumte Stich ---- */

/* Liegt auf dem Filz statt im Raster: ein kleiner Stapel in der Ecke, wie am echten Tisch.
   Anklicken zeigt ihn groß. */
.last-trick {
    position: absolute;
    right: 1.25rem;
    bottom: 1.25rem;
    z-index: 3;
    display: flex;
    align-items: center;
    gap: 0.7rem;
    padding: 0.5rem 0.8rem 0.5rem 0.6rem;
    border-radius: var(--radius);
    border: 1px solid var(--felt-line);
    background: rgba(0, 0, 0, 0.34);
    text-align: left;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s, transform 0.15s;
}

.last-trick:hover { border-color: var(--accent-line); background: rgba(0, 0, 0, 0.5); transform: translateY(-2px); }

.pile { display: flex; }
.pile .card { margin-left: -1.55rem; filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.6)); }
.pile .card:first-child { margin-left: 0; }

.card.mini { height: 3.1rem; width: calc(3.1rem * 0.658); border-radius: 3px; }

.pile-text { display: flex; flex-direction: column; line-height: 1.3; }
.pile-text strong { font-size: 0.85rem; font-weight: 600; }
.pile-text .muted { font-size: 0.76rem; }

.result { background: rgba(0, 0, 0, 0.35); border-radius: var(--radius-sm); padding: 0.6rem 0.9rem; margin-top: 0.4rem; font-size: 0.88rem; }
.result .lines { margin: 0.35rem 0 0; padding-left: 1.1rem; font-size: 0.78rem; color: var(--text-dim); }

/* ---- Handlungsleiste ---- */

.actions {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    flex-wrap: wrap;
    padding: 0.6rem 0.75rem;
    margin-bottom: 0.5rem;
    border-radius: var(--radius);
    background: rgba(0, 0, 0, 0.28);
    min-height: 3rem;
}

.actions .prompt {
    font-size: 0.68rem;
    letter-spacing: 0.16em;
    text-transform: uppercase;
    color: var(--text-faint);
    margin-right: 0.3rem;
}

.actions button { border-radius: var(--radius-pill); background: transparent; }
.actions button:hover:not(:disabled) { background: var(--panel-raised); }
.actions button.primary { background: var(--accent); }

/* Die Hochzeit bekommt eine eigene Zeile. Sie ist kein weiterer Vorbehalt unter vielen,
   sondern legt fest, wodurch sich mitten in der Partie die Partei bestimmt — das gehört
   ausgeschrieben und nicht auf einen Knopf mit einem Wort darauf. */
.hochzeit-choice {
    flex-basis: 100%;
    display: flex;
    flex-wrap: wrap;
    gap: 0.4rem;
    margin-top: 0.5rem;
    padding-top: 0.55rem;
    border-top: 1px solid var(--line-soft);
}

.hochzeit-choice .prompt {
    flex-basis: 100%;
    margin: 0 0 0.1rem;
    text-transform: none;
    letter-spacing: 0;
    font-size: 0.78rem;
    color: var(--text-dim);
}

.hochzeit-choice button {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.1rem;
    text-align: left;
    padding: 0.4rem 0.8rem;
    border-radius: var(--radius-sm);
    border: 1px solid var(--line);
    font-weight: 600;
}

.hochzeit-choice button small { font-weight: 400; font-size: 0.72rem; color: var(--text-faint); }
.hochzeit-choice button:hover:not(:disabled) small { color: var(--text-dim); }

/* ================================================================
   Handfächer und Karten
   ================================================================ */

/* Die Kartengröße hängt an der Bildschirmhöhe: Auf einem großen Schirm werden die Karten
   größer, auf einem Laptop passen sie trotzdem unter den Tisch. Das Seitenverhältnis
   entspricht dem viewBox der Motivdatei (1635 : 2486 = 0,658) — nicht dem gerundeten
   100 : 140, das hier einmal stand: Unter object-fit: contain lässt jede Abweichung einen
   weißen Streifen neben dem gedruckten Rahmen stehen. */
/* Der untere Innenabstand ist kein Zierrat: Eine um 19° gedrehte Karte ist rund 25 px
   höher als ihr Kasten, und der Bogen senkt die Randkarten zusätzlich ab. Ohne diesen
   Platz ragen sie unter den Tisch und die Seite bekommt eine Bildlaufleiste.
   Oben steht Platz für das Anheben beim Überfahren. */
.hand {
    --card-h: clamp(6.5rem, 17vh, 10rem);
    display: flex;
    justify-content: center;
    align-items: flex-end;
    padding: 2.2rem 0 1.7rem;
    touch-action: none;
}

/* 0.658 ist das Verhältnis der Motivdatei (viewBox 1635 : 2486). Bewusst nicht das des
   gedruckten Rahmens — der weiße Kartenrand dazwischen gehört zur Karte. */
.hand .card { height: var(--card-h); width: calc(var(--card-h) * 0.658); }

.card-sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.card {
    --rot: 0deg;
    --lift: 0px;
    width: 3.6rem;
    height: 5rem;
    flex: none;
    /* Der Drehpunkt liegt unterhalb der Karte — dadurch fächert die Hand auf einem Bogen
       auf, statt nur an Ort und Stelle zu kippen. */
    transform-origin: 50% 145%;
    transform: rotate(var(--rot)) translateY(var(--lift)) translate(var(--dx, 0), var(--dy, 0));
    transition: transform 0.13s ease-out, filter 0.13s ease-out;
    filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.5));
    user-select: none;
    -webkit-user-select: none;
}

/* Die Motive zeichnen einen gedruckten Rahmen, der 4,49 % vom Rand der Datei eingerückt
   ist. Dieser Abstand ist kein Fehler, sondern der weiße Kartenrand — so sieht eine echte
   Spielkarte aus, und ohne ihn kleben die Eckzeichen an der Kante.

   Die Box behält deshalb das Verhältnis der Datei, und das Bild wird nicht beschnitten.
   Was einmal zu viel war, war ein *eigener* Rahmen ganz außen: Der stand dann als dritter
   Strich um den weißen Rand herum. Den gibt es nicht mehr.

   Trumpf und Auswahl markieren von außen, damit sie nicht mit dem gedruckten Rahmen
   verschmelzen. Innen liegen die Motive durchsichtig auf der weißen Fläche. */
.card {
    background: var(--card-face);
    border-radius: 6px;
    overflow: hidden;
}

.card img { display: block; width: 100%; height: 100%; object-fit: contain; pointer-events: none; }

.card.trump { box-shadow: 0 0 0 2px var(--accent); }
.card.selected { box-shadow: 0 0 0 3px var(--accent); }

/* Ausgegraut wird nur, wenn es überhaupt eine Wahl gibt.
   Während der Vorbehaltsphase oder wenn ein anderer am Zug ist, ist keine einzige Karte
   spielbar — die ganze Hand grau zu färben sieht dann aus, als sei etwas kaputt. Man
   schaut sich sein Blatt in dem Moment ja gerade an. */
.hand:not(.inspecting) .card:not(.playable) {
    filter: grayscale(0.5) brightness(0.82) drop-shadow(0 1px 2px rgba(0, 0, 0, 0.4));
}

.hand .card.playable { cursor: grab; }

/* Das :not(.dragging) ist kein Zierrat, sondern der Kern des Ziehens.

   Beim Zeigerfang behält die gefangene Karte den Hover-Zustand über den ganzen Zug hinweg —
   der Zeiger gilt weiter als über ihr, auch wenn er längst über der Tischmitte steht. Diese
   Regel hat mit vier Klassen eine höhere Spezifität als .hand .card.dragging mit dreien und
   gewann deshalb jedes Mal, unabhängig von der Reihenfolge im Blatt. Die Karte hob sich
   also brav um 1,6 rem an und blieb dann liegen: Ziehen sah aus, als sei es nicht gebaut.
   Wer hier den Ausschluss entfernt, schaltet Ziehen und Ablegen wieder ab. */
.hand .card.playable:hover:not(.dragging),
.hand .card.playable:focus-visible:not(.dragging) {
    transform: rotate(var(--rot)) translateY(calc(var(--lift) - 1.6rem)) scale(1.05);
    filter: drop-shadow(0 10px 16px rgba(0, 0, 0, 0.6));
    z-index: 5;
    outline: none;
}

/* Gezogen wird die Karte selbst, in voller Deckkraft und mit klarer Kante — kein
   halbdurchsichtiges Schattenbild. Der Drehpunkt wandert in die Mitte, damit sie
   ungekippt und unverzerrt am Zeiger hängt. */
.hand .card.dragging {
    cursor: grabbing;
    transition: none;
    z-index: 40;
    opacity: 1;
    transform-origin: 50% 50%;
    transform: translate(var(--dx, 0), var(--dy, 0)) scale(1.05) rotate(0deg);
    box-shadow: 0 0 0 2px var(--accent);
    filter: drop-shadow(0 18px 26px rgba(0, 0, 0, 0.7));
}

.card.selected { transform: rotate(var(--rot)) translateY(calc(var(--lift) - 1.3rem)); z-index: 5; }

@media (prefers-reduced-motion: reduce) {
    .card, .drop-zone, .opponent-box { transition: none; }
    .card.dealt { animation: none; }
}

/* ================================================================
   Sprache und Video
   ================================================================ */

.media-controls { display: inline-flex; gap: 0.35rem; align-items: center; }

.media-strip {
    display: flex;
    gap: 0.5rem;
    align-items: center;
    flex-wrap: wrap;
    padding: 0.6rem;
    margin-bottom: 0.8rem;
    border-radius: var(--radius);
    background: var(--panel);
    border: 1px solid var(--line-soft);
}

.video-tile {
    position: relative;
    width: 100%;
    aspect-ratio: 4 / 3;
    background: #0a1410;
    border-radius: var(--radius-sm);
    overflow: hidden;
    margin-bottom: 0.35rem;
}

.media-strip .video-tile { width: 9rem; margin-bottom: 0; }
.video-tile video { width: 100%; height: 100%; object-fit: cover; display: block; }
.video-tile.audio-only video { display: none; }

.video-placeholder {
    position: absolute;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 1.5rem;
    font-weight: 700;
    color: var(--text-faint);
    background: rgba(255, 255, 255, 0.03);
}

.mic-off, .link-state, .tile-name {
    position: absolute;
    padding: 0.05rem 0.3rem;
    border-radius: 3px;
    background: rgba(0, 0, 0, 0.6);
    font-size: 0.68rem;
    color: var(--text);
}

.mic-off { bottom: 0.2rem; right: 0.2rem; }
.link-state { bottom: 0.2rem; left: 0.2rem; color: var(--accent); }
.tile-name { top: 0.2rem; left: 0.2rem; }

.self-view {
    display: inline-block;
    width: 4.5rem;
    aspect-ratio: 4 / 3;
    border-radius: var(--radius-sm);
    overflow: hidden;
    background: #0a1410;
    vertical-align: middle;
}

.self-view.hidden { display: none; }
.self-view video { width: 100%; height: 100%; object-fit: cover; transform: scaleX(-1); }

.spectator-note { color: var(--text-dim); font-size: 0.88rem; text-align: center; margin-bottom: 0.5rem; }
.opponent.bottom { display: flex; justify-content: center; }
.opponent.bottom .opponent-box { min-width: 13rem; }

/* ================================================================
   Seitenleiste
   ================================================================ */

.sidebar .panel { margin-bottom: 0.85rem; }
.sidebar .panel h3 { display: flex; justify-content: space-between; align-items: center; }

.totals { display: flex; flex-direction: column; gap: 0.25rem; }

.total-row {
    display: flex;
    align-items: center;
    gap: 0.55rem;
    padding: 0.4rem 0.5rem;
    border-radius: var(--radius-sm);
    border: 1px solid transparent;
    font-size: 0.9rem;
}

/* Die eigene Zeile ist hervorgehoben — bei vier ähnlichen Namen findet man sich sonst nicht. */
.total-row.mine { background: var(--accent-soft); border-color: var(--accent-line); }
.total-row .name { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.total-row .name .you { color: var(--text-faint); font-size: 0.78rem; }
.total-row .score { font-variant-numeric: tabular-nums; font-weight: 600; }
.total-row .score.negative { color: var(--danger); }

.sheet { margin: 0; padding-left: 1.1rem; font-size: 0.82rem; color: var(--text-dim); }
.sheet li { margin-bottom: 0.2rem; }

.chat .messages {
    display: flex;
    flex-direction: column-reverse;
    max-height: 15rem;
    overflow-y: auto;
    gap: 0.3rem;
    margin-bottom: 0.6rem;
    font-size: 0.85rem;
}

.message { display: flex; gap: 0.4rem; align-items: baseline; }
.message .who {
    flex: none;
    font-size: 0.62rem;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text-faint);
    background: var(--panel-raised);
    border-radius: 3px;
    padding: 0.05rem 0.3rem;
}
.message.system .body { color: var(--text-dim); }
.chat form { display: flex; gap: 0.4rem; }
.chat .text { flex: 1; min-width: 0; }

/* ================================================================
   Regel-Editor
   ================================================================ */

.rule-presets { display: flex; gap: 0.4rem; align-items: center; flex-wrap: wrap; margin-bottom: 0.7rem; }

.rule-summary {
    background: var(--accent-soft);
    border: 1px solid var(--accent-line);
    border-radius: var(--radius-sm);
    padding: 0.5rem 0.75rem;
    margin-bottom: 0.8rem;
    font-size: 0.88rem;
}

.rule-summary summary { cursor: pointer; font-weight: 600; }
.rule-summary ul, .rule-diff { margin: 0.4rem 0 0; padding-left: 1.2rem; font-size: 0.83rem; color: var(--text-dim); }

.rule-group { border: 1px solid var(--line-soft); border-radius: var(--radius-sm); padding: 0.6rem 0.8rem 0.8rem; margin-bottom: 0.8rem; }
.rule-group legend { font-size: 0.68rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--text-faint); padding: 0 0.3rem; font-weight: 600; }

.rule-row { padding: 0.3rem 0.4rem; border-radius: 4px; }
.rule-row label { display: flex; align-items: center; gap: 0.5rem; font-size: 0.88rem; }
.rule-row label > span { flex: 1; }
.rule-row.changed { background: var(--accent-soft); box-shadow: inset 2px 0 0 var(--accent); }

.hint { margin: 0.1rem 0 0.3rem 1.6rem; font-size: 0.78rem; }
.rule-actions { margin-top: 0.5rem; }

/* ================================================================
   Ladeanzeige
   ================================================================ */

.loading-progress { position: relative; display: block; width: 6rem; margin: 22vh auto 1rem; }
.loading-progress circle { fill: none; stroke: var(--line); stroke-width: 0.4rem; transform-origin: 50% 50%; }
.loading-progress circle:last-child {
    stroke: var(--accent);
    stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 2), 500%;
    transition: stroke-dasharray 0.05s ease-in-out;
}

.loading-progress-text { text-align: center; color: var(--text-dim); font-size: 0.85rem; }
.loading-progress-text:after { content: var(--blazor-load-percentage-text, "Lade …"); }

#blazor-error-ui {
    background: var(--danger-soft);
    border-top: 1px solid var(--danger-line);
    color: var(--danger);
    display: none;
    position: fixed;
    bottom: 0;
    width: 100%;
    padding: 0.6rem 1.2rem;
    z-index: 1000;
}

#blazor-error-ui .dismiss { cursor: pointer; position: absolute; right: 0.75rem; top: 0.6rem; }
