/* =============================================
   Playvora Arena — Global Styles
   Dark gaming theme: deep navy + electric purple
   ============================================= */

:root {
    color-scheme: dark;
    --pv-primary:       #6c63ff;
    --pv-primary-dark:  #4f46c8;
    --pv-primary-glow:  rgba(108, 99, 255, 0.25);
    --pv-accent:        #ff6b6b;
    --pv-success:       #51cf66;
    --pv-dark:          #0b0b1a;
    --pv-dark-2:        #10102a;
    --pv-card:          #16163a;
    --pv-card-border:   rgba(108, 99, 255, 0.2);
    --pv-text:          #e0e0f0;
    --pv-text-muted:    #7b7b9e;
    --pv-input-bg:      #0d0d24;
    --pv-input-border:  rgba(255, 255, 255, 0.12);
}

* { box-sizing: border-box; }

html, body {
    height: 100%;
}

body {
    background-color: var(--pv-dark);
    color: var(--pv-text);
    font-family: 'Segoe UI', system-ui, -apple-system, sans-serif;
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

/* ---- Navbar ---- */
.pv-nav {
    background: linear-gradient(135deg, #0d0d28 0%, #16163a 100%);
    border-bottom: 2px solid var(--pv-primary);
    padding: 0.75rem 0;
    --bs-navbar-active-color: var(--pv-text);
    --bs-navbar-color: var(--pv-text-muted);
}

.pv-nav .navbar-brand {
    color: var(--pv-primary);
    font-weight: 700;
    font-size: 1.3rem;
    letter-spacing: 0.5px;
}

.pv-nav .navbar-brand:hover { color: var(--pv-primary); }

.pv-nav .nav-link {
    color: var(--pv-text-muted);
    transition: color 0.2s;
}

.pv-nav .nav-link:hover,
.pv-nav .nav-link.active {
    color: var(--pv-primary);
}

/* Wo bin ich gerade?
   Der Farbwechsel allein reicht nicht - er sieht genauso aus wie beim
   Ueberfahren mit der Maus, und dann heisst Blau mal "hier bist du" und mal
   "hier koenntest du hin". Der Strich darunter gehoert nur der offenen Seite. */
.pv-nav .nav-link.active {
    font-weight: 600;
    position: relative;
}

.pv-nav .nav-link.active::after {
    content: "";
    position: absolute;
    left: 0.5rem;
    right: 0.5rem;
    bottom: 0.15rem;
    height: 2px;
    border-radius: 2px;
    background: var(--pv-primary);
}

/* Der Klick ist angekommen - sofort, noch bevor die neue Seite da ist.
   Ohne Neuladen gibt es keinen Ladebalken des Browsers mehr; ohne diese
   Rueckmeldung wirkt ein Klick auf eine langsame Seite wie ins Leere. Die
   Klasse setzt navigation-feedback.js beim Klick und nimmt sie zurueck,
   sobald die Seite steht. */
.pv-nav .nav-link.pv-nav-pending {
    opacity: 0.55;
}

/* Waehrend geladen wird: der Zeiger sagt es mit.
   `aria-busy` setzt Turbo von selbst auf <html>, wir muessen es nur
   sichtbar machen. */
html[aria-busy="true"] .pv-nav .nav-link,
html[aria-busy="true"] a[data-turbo="true"] {
    cursor: progress;
}

/* Turbos Fortschrittsbalken - er existiert schon, nur in Blau von der Stange.
   Er blendet erst nach einer halben Sekunde ein: Bei einem schnellen Wechsel
   waere ein aufblitzender Streifen unruhiger als gar keiner. */
.turbo-progress-bar {
    height: 3px;
    background: linear-gradient(90deg, var(--pv-primary), #a78bfa);
}

.pv-nav .dropdown-menu {
    background: var(--pv-card);
    border: 1px solid var(--pv-card-border);
}

.pv-nav .dropdown-item {
    color: var(--pv-text);
}

.pv-nav .dropdown-item:hover {
    background: var(--pv-primary-glow);
    color: var(--pv-primary);
}

.pv-nav .navbar-toggler {
    border-color: var(--pv-card-border);
}

/* ---- Footer ---- */
.pv-footer {
    background: var(--pv-dark-2);
    border-top: 1px solid var(--pv-card-border);
    padding: 1.25rem 0;
    color: var(--pv-text-muted);
    margin-top: auto;
}

/* ---- Buttons ---- */
/* btn-pv-primary is an alias for btn-pv — keep both selectors so existing
   templates (sandbox tabs, battleship attack/defend, dicewars publish)
   render with the gradient instead of falling back to the unstyled
   Bootstrap default that looks like a text link. */
.btn-pv,
.btn-pv-primary {
    background: linear-gradient(135deg, var(--pv-primary) 0%, var(--pv-primary-dark) 100%);
    color: #fff;
    border: none;
    border-radius: 8px;
    padding: 0.6rem 1.5rem;
    font-weight: 600;
    transition: transform 0.15s, box-shadow 0.15s;
    box-shadow: 0 4px 15px var(--pv-primary-glow);
}

.btn-pv:hover,
.btn-pv-primary:hover {
    color: #fff;
    transform: translateY(-2px);
    box-shadow: 0 6px 20px var(--pv-primary-glow);
}

.btn-pv:active,
.btn-pv-primary:active { transform: translateY(0); }

.btn-pv-outline {
    background: transparent;
    color: var(--pv-primary);
    border: 1px solid var(--pv-primary);
    border-radius: 8px;
    padding: 0.6rem 1.5rem;
    font-weight: 600;
    transition: all 0.2s;
}

.btn-pv-outline:hover {
    background: var(--pv-primary-glow);
    color: var(--pv-primary);
}

/* ---- Cards ---- */
.pv-card {
    background: var(--pv-card);
    border: 1px solid var(--pv-card-border);
    border-radius: 12px;
    padding: 1.5rem;
    transition: transform 0.2s, box-shadow 0.2s;
}

.pv-card.pv-card-hover:hover {
    transform: translateY(-3px);
    box-shadow: 0 8px 30px var(--pv-primary-glow);
}

/* ---- Form inputs ---- */
.form-control, .form-select {
    background-color: var(--pv-input-bg);
    border: 1px solid var(--pv-input-border);
    color: var(--pv-text);
    border-radius: 8px;
}

.form-control:focus, .form-select:focus {
    background-color: var(--pv-input-bg);
    border-color: var(--pv-primary);
    color: var(--pv-text);
    box-shadow: 0 0 0 0.25rem var(--pv-primary-glow);
}

.form-control::placeholder { color: var(--pv-text-muted); }

.form-label {
    color: var(--pv-text-muted);
    font-size: 0.875rem;
    font-weight: 500;
    margin-bottom: 0.4rem;
}

/* ---- Alerts ---- */
.alert-success {
    background: rgba(81, 207, 102, 0.1);
    border-color: rgba(81, 207, 102, 0.3);
    color: var(--pv-success);
}

.alert-danger {
    background: rgba(255, 107, 107, 0.1);
    border-color: rgba(255, 107, 107, 0.3);
    color: var(--pv-accent);
}

.alert-info {
    background: rgba(108, 99, 255, 0.1);
    border-color: var(--pv-card-border);
    color: var(--pv-primary);
}

/* ---- Utilities ---- */
.text-pv-primary { color: var(--pv-primary); }
.text-pv-muted   { color: var(--pv-text-muted); }
.bg-pv-card      { background: var(--pv-card); }
.bg-pv-primary   { background: var(--pv-primary); color: #fff; }

/* ---- Brand icon (navbar, lobby heading, footer) ---- */
.pv-brand-icon {
    border-radius: 6px;
    object-fit: cover;
    flex-shrink: 0;
}

/* ---- Ticket drop-zone (drag/drop/paste image upload) ---- */
.pv-drop-zone {
    border: 2px dashed var(--pv-card-border);
    border-radius: 10px;
    padding: 0;
    background: rgba(108, 99, 255, 0.04);
    transition: background 0.15s, border-color 0.15s;
}
.pv-drop-zone--hover {
    border-color: var(--pv-primary);
    background: rgba(108, 99, 255, 0.12);
}
.pv-drop-zone__body {
    padding: 1.25rem;
    text-align: center;
    color: var(--pv-text-muted);
    cursor: pointer;
    user-select: none;
}
.pv-drop-zone__body i {
    font-size: 1.4rem;
    color: var(--pv-primary);
    vertical-align: middle;
    margin-right: 0.35rem;
}
.pv-drop-zone__hint {
    font-size: 0.9rem;
}
.pv-drop-zone__hint kbd {
    background: var(--pv-input-bg);
    border: 1px solid var(--pv-input-border);
    border-radius: 4px;
    padding: 1px 5px;
    font-size: 0.78rem;
    color: var(--pv-text);
}
.pv-drop-zone__subhint {
    font-size: 0.75rem;
    opacity: 0.7;
    margin-top: 4px;
}
.pv-drop-zone__items {
    display: flex;
    flex-wrap: wrap;
    gap: 10px;
    padding: 0 1rem 1rem;
}
.pv-drop-zone__items:empty {
    display: none;
}
.pv-drop-zone__item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    background: var(--pv-card);
    border: 1px solid var(--pv-card-border);
    border-radius: 8px;
    width: calc(50% - 5px);
    min-width: 240px;
    position: relative;
}
.pv-drop-zone__item--done {
    border-color: rgba(81, 207, 102, 0.4);
}
.pv-drop-zone__item--error {
    border-color: rgba(255, 107, 107, 0.5);
    background: rgba(255, 107, 107, 0.08);
}
.pv-drop-zone__thumb {
    width: 48px;
    height: 48px;
    border-radius: 6px;
    background: var(--pv-input-bg);
    overflow: hidden;
    flex-shrink: 0;
    display: flex;
    align-items: center;
    justify-content: center;
}
.pv-drop-zone__thumb img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.pv-drop-zone__placeholder {
    color: var(--pv-text-muted);
    font-size: 1.2rem;
}
.pv-drop-zone__meta {
    flex: 1;
    min-width: 0;
    font-size: 0.78rem;
}
.pv-drop-zone__name {
    color: var(--pv-text);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}
.pv-drop-zone__size {
    color: var(--pv-text-muted);
    font-size: 0.7rem;
    margin-bottom: 4px;
}
.pv-drop-zone__progress {
    height: 4px;
    background: var(--pv-input-bg);
    border-radius: 2px;
    overflow: hidden;
}
.pv-drop-zone__progress-bar {
    height: 100%;
    background: linear-gradient(90deg, var(--pv-primary) 0%, var(--pv-primary-dark) 100%);
    transition: width 0.15s;
}
.pv-drop-zone__item--done .pv-drop-zone__progress-bar {
    background: var(--pv-success);
}
.pv-drop-zone__item--error .pv-drop-zone__progress-bar {
    background: var(--pv-accent);
}
.pv-drop-zone__item-error {
    color: var(--pv-accent);
    font-size: 0.7rem;
    margin-top: 2px;
}
.pv-drop-zone__remove {
    border: none;
    background: transparent;
    color: var(--pv-text-muted);
    font-size: 1.3rem;
    line-height: 1;
    cursor: pointer;
    padding: 0 4px;
    transition: color 0.15s;
}
.pv-drop-zone__remove:hover {
    color: var(--pv-accent);
}
.pv-drop-zone__error {
    padding: 8px 1rem;
    color: var(--pv-accent);
    font-size: 0.8rem;
    background: rgba(255, 107, 107, 0.08);
    border-top: 1px solid rgba(255, 107, 107, 0.2);
}

/* ---- Players list ---- */
.pv-player-row {
    color: var(--pv-text);
    transition: background 0.15s;
}
.pv-player-row:hover {
    background: rgba(108, 99, 255, 0.08);
    color: var(--pv-text);
}

/* ---- Pagination ---- */
.pv-page-link {
    background: var(--pv-card);
    border-color: var(--pv-card-border);
    color: var(--pv-text-muted);
}
.pv-page-link:hover,
.pv-page-link:focus {
    background: rgba(108, 99, 255, 0.15);
    border-color: var(--pv-primary);
    color: var(--pv-primary);
}
.page-item.active .pv-page-link {
    background: var(--pv-primary);
    border-color: var(--pv-primary);
    color: #fff;
}
.page-item.disabled .pv-page-link {
    background: var(--pv-card);
    border-color: var(--pv-card-border);
    color: var(--pv-text-muted);
    opacity: 0.5;
}

/* ---- Custom Tooltips — Playvora gaming style ---- */
/* Rendered via tooltip.js into <body> to bypass overflow:hidden containers. */
#pv-tooltip {
    position: fixed;
    z-index: 99999;
    pointer-events: none;
    padding: 5px 12px;
    background: #0d0d24;
    color: #e0e0f0;
    font-size: 0.72rem;
    font-weight: 500;
    letter-spacing: 0.3px;
    border: 1px solid rgba(108, 99, 255, 0.55);
    border-radius: 6px;
    box-shadow: 0 0 10px rgba(108, 99, 255, 0.2), 0 3px 10px rgba(0, 0, 0, 0.6);
    white-space: nowrap;
    opacity: 0;
    transition: opacity 0.15s ease, transform 0.1s ease;
    transform: translateY(4px);
}

#pv-tooltip.pv-tooltip--visible {
    opacity: 1;
    transform: translateY(0);
}

/* Der Pfeil folgt der Blase - beide Richtungen, und seitlich stufenlos.
   `--pv-tip-arrow` setzt tooltip.js beim Anzeigen auf die Stelle des
   Ausloesers, gemessen vom linken Rand der Blase. Ohne die Variable steht er
   mittig, wie vorher. */
#pv-tooltip::after {
    content: '';
    position: absolute;
    top: 100%;
    left: var(--pv-tip-arrow, 50%);
    transform: translateX(-50%);
    border: 5px solid transparent;
    border-top-color: rgba(108, 99, 255, 0.55);
}

/* Blase unter dem Element: Der Pfeil sitzt oben und zeigt nach oben. */
#pv-tooltip.pv-tooltip--below::after {
    top: auto;
    bottom: 100%;
    border-top-color: transparent;
    border-bottom-color: rgba(108, 99, 255, 0.55);
}

/* ---- Input group text (icon prefix) ---- */
.pv-input-group-text {
    background: var(--pv-input-bg);
    border-color: var(--pv-input-border);
    color: var(--pv-text-muted);
}

/* Lobby/modal variant — darker navy */
.pv-input-group-text--lobby {
    background: #16162a;
    border-color: #2d2b5a;
    color: #818cf8;
}

/* ---- Shared accent code/monospace ---- */
.pv-code-accent {
    color: #818cf8;
    font-size: .75rem;
}

/* ---- Shared empty-state icon ---- */
.pv-empty-icon {
    font-size: 2.5rem;
    opacity: 0.4;
}

/* ---- Room page ---- */
.pv-room-container {
    max-width: 640px;
    padding-bottom: 100px;
}

.pv-room-label {
    font-size: .7rem;
    letter-spacing: 1.2px;
}

/* Reconnect banner */
.pv-banner--warning {
    background: rgba(245, 158, 11, 0.1);
    border-color: rgba(245, 158, 11, 0.3);
}

.pv-banner--warning .spinner-border {
    color: #f59e0b;
    width: 14px;
    height: 14px;
    border-width: 2px;
}

.pv-banner-text--warning {
    color: #f59e0b;
    font-size: .875rem;
    font-weight: 600;
}

/* Error banner */
.pv-banner--error {
    background: rgba(239, 68, 68, 0.1);
    border-color: rgba(239, 68, 68, 0.3);
}

.pv-error-text {
    color: #ef4444;
    font-size: .875rem;
    margin: 0;
    text-align: center;
}

.pv-spinner-primary { color: #6366f1; }

/* ---- Action bar (fixed bottom) ---- */
.pv-action-bar {
    position: fixed;
    bottom: 0;
    left: 0;
    right: 0;
    background: #0f0f23;
    border-top: 1px solid #2d2b5a;
    padding: 12px 20px 20px;
    z-index: 100;
}

.pv-action-bar-inner { max-width: 600px; }

/* ---- Countdown overlay ---- */
.pv-countdown-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, .65);
    align-items: center;
    justify-content: center;
    flex-direction: column;
    z-index: 9999;
}

.pv-countdown-label {
    color: #818cf8;
    font-size: 1rem;
    font-weight: 600;
    margin-bottom: .75rem;
}

.pv-countdown-number {
    color: #fff;
    font-size: 5rem;
    font-weight: 900;
    line-height: 1;
}

/* ---- Game lobby ---- */
.pv-lobby-container { max-width: 640px; }

.pv-lobby-modal .modal-content {
    background: #16162a;
    border: 1px solid #2d2b5a;
}

.pv-lobby-modal .modal-header,
.pv-lobby-modal .modal-footer {
    border-color: #2d2b5a;
}

/* ---- Lobby index — game cards ---- */
.pv-lobby-game-icon { font-size: 3rem; }
.pv-lobby-game-cover { width: 80px; height: 80px; object-fit: cover; border-radius: 14px; }

/* ---- Rankings page ---- */
.pv-ranking-tab {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.45rem 1rem;
    border-radius: 8px;
    background: rgba(99, 102, 241, 0.08);
    border: 1px solid rgba(99, 102, 241, 0.2);
    color: #94a3b8;
    font-size: 0.85rem;
    font-weight: 600;
    cursor: pointer;
    transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.pv-ranking-tab:hover { background: rgba(99, 102, 241, 0.15); border-color: rgba(99, 102, 241, 0.4); color: #ccd6f6; }
.pv-ranking-tab--active { background: rgba(99, 102, 241, 0.2); border-color: #6366f1; color: #fff; }
.pv-ranking-tab-cover { width: 20px; height: 20px; border-radius: 4px; object-fit: cover; }

.pv-ranking-table { font-size: 0.88rem; --bs-table-bg: transparent; }
.pv-ranking-table th { font-size: 0.75rem; text-transform: uppercase; letter-spacing: 0.05em; padding: 0.5rem 0.75rem; }
.pv-ranking-table td { padding: 0.6rem 0.75rem; border-top: 1px solid rgba(255,255,255,0.04); }
/* Rank column: keep it narrow and never wrap the medal + number onto two lines */
.pv-ranking-table th:first-child,
.pv-ranking-table td:first-child { width: 50px; white-space: nowrap; }
/* Cell colours as classes instead of inline styles in the rendered rows */
.pv-ranking-rank { color: #818cf8; }
.pv-ranking-elo { color: #4ade80; }

.pv-lobby-card-disabled {
    opacity: 0.45;
    cursor: not-allowed;
}

/* ---- Room list status dot ---- */
.pv-status-dot {
    width: 10px;
    height: 10px;
    border-radius: 50%;
    flex-shrink: 0;
}

/* A full table, spotted before the number is read. The app's lobby has always
   marked it this way; the browser now agrees. */
.pv-room-count--full {
    color: #ef4444;
    font-weight: 600;
}

/* Everybody who took a seat has gone, and the seats are still theirs. Amber,
   not red: red already means "no room for you", and this is the milder
   "there is room, but nothing is happening here". */
.pv-room-orphaned {
    color: #f59e0b;
    font-weight: 500;
}

/* ---- Rule variant ---- */

/* Which rules a listed room plays. Quiet on purpose: it sits beside the room
   name and must not compete with the status badge on the other side. */
.pv-variant-badge {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: #a5b4fc;
    background: rgba(99, 102, 241, 0.14);
    border: 1px solid rgba(99, 102, 241, 0.32);
}

/* ---- Move hints ---- */

/* Whether a listed room lights up the legal moves. Same quiet shape as the
   variant badge beside it, a different hue so the two never read as one label.
   Teal for on, amber for off - the off rooms are what an experienced player
   scans the list for, and amber is the one that catches an eye without
   shouting. */
.pv-hints-badge {
    display: inline-block;
    padding: 1px 8px;
    border-radius: 999px;
    font-size: 0.72rem;
    font-weight: 600;
    letter-spacing: 0.02em;
    color: #5eead4;
    background: rgba(45, 212, 191, 0.12);
    border: 1px solid rgba(45, 212, 191, 0.3);
}

.pv-hints-badge--off {
    color: #fcd34d;
    background: rgba(245, 158, 11, 0.12);
    border-color: rgba(245, 158, 11, 0.32);
}

/* The picker in the create-room dialog. Cards rather than bare radios: the
   hint under each name is what actually explains the variant, so it needs
   room to breathe. */
.pv-variant-group {
    display: grid;
    gap: 8px;
}

.pv-variant {
    display: block;
    margin: 0;
    padding: 10px 12px;
    border-radius: 12px;
    border: 1px solid #2d2b5a;
    background: #12122a;
    cursor: pointer;
    transition: border-color 0.18s, background 0.18s;
}

.pv-variant:hover {
    border-color: #4f46e5;
}

/* Screen readers still get a real radio group - the input is only moved out
   of sight, never removed from the accessibility tree. */
.pv-variant__input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.pv-variant__body {
    display: block;
}

.pv-variant__name {
    display: block;
    color: #e2e8f0;
    font-weight: 600;
    font-size: 0.92rem;
}

.pv-variant__hint {
    display: block;
    margin-top: 2px;
    color: #64748b;
    font-size: 0.78rem;
    font-variant-numeric: tabular-nums;
}

.pv-variant:has(.pv-variant__input:checked) {
    border-color: #6366f1;
    background: linear-gradient(100deg, rgba(99, 102, 241, 0.18), rgba(99, 102, 241, 0.06));
}

.pv-variant:has(.pv-variant__input:checked) .pv-variant__name {
    color: #fff;
}

.pv-variant:has(.pv-variant__input:checked) .pv-variant__hint {
    color: #a5b4fc;
}

/* Keyboard focus has to stay visible even though the input itself is hidden. */
.pv-variant:has(.pv-variant__input:focus-visible) {
    outline: 2px solid #818cf8;
    outline-offset: 2px;
}

/* Bildkacheln statt gestapelter Karten. Ein Spiel darf das für seinen eigenen
   Dialog wählen: Namen wie Poddavki oder Ugolki sagen niemandem etwas, der sie
   nicht kennt - das Brett schon. Drei nebeneinander, darunter Name und Satz. */
.pv-variant-tiles {
    display: grid;
    grid-template-columns: repeat(3, 1fr);
    gap: 8px;
}

/* Unter 480px stehen drei Kacheln zu eng; dann untereinander wie im
   Standardformular, aber weiter mit Bild. */
@media (max-width: 480px) {
    .pv-variant-tiles {
        grid-template-columns: 1fr;
    }
}

.pv-variant--tile {
    padding: 6px;
    overflow: hidden;
}

.pv-variant__art {
    display: block;
    width: 100%;
    height: auto;
    /* Eigene, kleinere Rundung statt bündig an der Kachelkante: dort hätte der
       Radius des Rahmens die Bildecken abgeschnitten, und weil das Motiv seinen
       eigenen Rahmen hat, sah das aus wie ein beschnittenes Bild. */
    border-radius: 8px;
    /* Ohne die Sättigung wäre die nicht gewählte Kachel genauso laut wie die
       gewählte, und die Reihe hätte keinen Schwerpunkt. */
    filter: saturate(0.55) brightness(0.82);
    transition: filter 0.18s;
}

.pv-variant--tile:hover .pv-variant__art,
.pv-variant--tile:has(.pv-variant__input:checked) .pv-variant__art {
    filter: none;
}

/* Eine Kachel, die eine Zahl statt eines Bildes zeigt.
 *
 * Bei Schach IST die Variante die Uhr, und eine Uhr ist eine Zahl: "5+3" sagt
 * alles, ein gezeichnetes Zifferblatt nichts. Die Regeln stehen hier und nicht
 * in chess.css, weil die Lobby nur das gemeinsame Stylesheet laedt - dort
 * waeren sie toter Code gewesen. */
.pv-chess-clock-face {
    display: flex;
    align-items: baseline;
    justify-content: flex-start;
    gap: 3px;
    padding: 2px 4px 4px;
    line-height: 1;
}

.pv-chess-clock-minutes {
    font-size: 1.5rem;
    font-weight: 700;
    color: #cdd3e8;
}

.pv-chess-clock-increment {
    font-size: 0.85rem;
    font-weight: 600;
    color: #7c86a8;
}

.pv-chess-clock-tile:has(.pv-variant__input:checked) .pv-chess-clock-minutes {
    color: #a5b4fc;
}

.pv-variant--tile .pv-variant__name,
.pv-variant--tile .pv-variant__hint {
    padding: 0 4px;
}

.pv-variant--tile .pv-variant__name {
    padding-top: 7px;
}

.pv-variant--tile .pv-variant__hint {
    padding-bottom: 2px;
}

/* Segmentierte Einstellung: eine Reihe kurzer Antworten, darunter EIN Satz,
   der zur gedrückten Taste gehört. Bei zwei gestapelten Karten ist ein Hinweis
   je Auswahl richtig, bei drei Wörtern nebeneinander wird er zum Rauschen. */
.pv-seg {
    display: flex;
    gap: 6px;
    padding: 4px;
    border-radius: 12px;
    border: 1px solid #2d2b5a;
    background: #12122a;
}

.pv-seg__item {
    flex: 1;
    margin: 0;
    cursor: pointer;
}

.pv-seg__input {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

.pv-seg__label {
    display: block;
    padding: 7px 6px;
    border-radius: 9px;
    color: #94a3b8;
    font-size: 0.85rem;
    font-weight: 600;
    text-align: center;
    transition: background 0.18s, color 0.18s;
}

.pv-seg__item:hover .pv-seg__label {
    color: #c7d2fe;
}

.pv-seg__item:has(.pv-seg__input:checked) .pv-seg__label {
    background: linear-gradient(100deg, rgba(99, 102, 241, 0.32), rgba(99, 102, 241, 0.14));
    color: #fff;
}

.pv-seg__item:has(.pv-seg__input:focus-visible) .pv-seg__label {
    outline: 2px solid #818cf8;
    outline-offset: 2px;
}

.pv-seg__hint {
    margin: 6px 2px 0;
    color: #64748b;
    font-size: 0.78rem;
}

/* ---- Participant list (room.js generated HTML) ---- */
.pv-participant-row { border-radius: 12px; }

.pv-participant-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
    display: inline-block;
}

.pv-avatar {
    border-radius: 50%;
    object-fit: cover;
    flex-shrink: 0;
}

.pv-avatar--player { width: 40px; height: 40px; }
.pv-avatar--spectator { width: 28px; height: 28px; }

.pv-avatar-fallback {
    border-radius: 50%;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    flex-shrink: 0;
}

.pv-avatar-fallback--player {
    width: 40px;
    height: 40px;
    font-size: 1rem;
    background: rgba(45, 43, 90, 1);
    color: #94a3b8;
}

.pv-avatar-fallback--spectator {
    width: 28px;
    height: 28px;
    font-size: .7rem;
    background: rgba(45, 43, 90, 1);
    color: #94a3b8;
}

.pv-avatar-wrap {
    position: relative;
    flex-shrink: 0;
}

.pv-avatar-online-dot {
    position: absolute;
    bottom: -1px;
    right: -1px;
    width: 8px;
    height: 8px;
    border-radius: 50%;
    border: 1.5px solid #1a1a2e;
}

.pv-seat-badge {
    width: 24px;
    height: 24px;
    border-radius: 6px;
    background: rgba(99, 102, 241, .15);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    color: #818cf8;
    font-size: .75rem;
    font-weight: 700;
    flex-shrink: 0;
}

.pv-participant-name { font-size: .875rem; }

.pv-you-badge {
    padding: 1px 6px;
    border-radius: 4px;
    background: rgba(99, 102, 241, .15);
    color: #818cf8;
    font-size: .625rem;
    font-weight: 600;
}

.pv-ready-badge {
    padding: 2px 8px;
    border-radius: 8px;
    font-size: .6875rem;
    font-weight: 600;
}

.pv-kick-btn {
    padding: 2px 8px;
    font-size: .6875rem;
    border-radius: 8px;
    background: rgba(239, 68, 68, .1);
    color: #ef4444;
    border: 1px solid rgba(239, 68, 68, .3);
}

.pv-hand-raised-icon {
    color: #f59e0b;
    font-size: 1rem;
    line-height: 1;
    display: inline-flex;
    align-items: center;
}

/* ---- Reconnecting overlay (modal with backdrop blur) ---- */
.pv-reconnect-overlay {
    position: fixed;
    inset: 0;
    z-index: 9999;
    display: flex;
    align-items: center;
    justify-content: center;
    background: rgba(10, 10, 26, 0.6);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}
.pv-reconnect-modal {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 32px 48px;
    background: #1a1a2e;
    border: 1px solid #2d2b5a;
    border-radius: 20px;
    box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
}
.pv-reconnect-text {
    color: #e0e0f0;
    font-size: 15px;
    font-weight: 600;
}

/* Dicewars game-over banner — sits at the bottom of the board card,
   translucent so the final board state stays visible behind it. */
.pv-game-over-banner {
    position: absolute;
    left: 12px;
    right: 12px;
    bottom: 12px;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 10px 14px;
    background: rgba(26, 26, 46, 0.92);
    backdrop-filter: blur(4px);
    -webkit-backdrop-filter: blur(4px);
    border: 1px solid #2d2b5a;
    border-radius: 12px;
    box-shadow: 0 8px 24px rgba(0, 0, 0, 0.4);
}
.pv-game-over-trophy { font-size: 22px; }
.pv-game-over-text { flex: 1; color: #e0e0f0; font-weight: 600; font-size: 14px; }
.pv-game-over-btn { white-space: nowrap; }

/* Game result overlay — shared by the board games.
 *
 * A result card placed in the page flow lands below the fold on anything
 * shorter than a desktop window: on a 1568x699 browser the board fills the
 * viewport, so the player who just won had to scroll to find out. Measured on
 * Checkers, 10.08.2026. It floats now, over a backdrop light enough that the
 * final position stays readable behind it.
 *
 * Deliberately below .pv-afk-overlay (1400) and .pv-wait-overlay (1500):
 * "your opponent is away" and "reconnecting" outrank a finished game.
 *
 * Usage — the box carries the class from the start and the client toggles
 * `is-open`, rather than writing style.display. An inline display would beat
 * the flex centering here, which is exactly how this used to break:
 *
 *   <div id="result-box" class="pv-game-result">
 *       <div class="pv-game-result__panel text-center"> … </div>
 *   </div>
 *   resultBox.classList.add('is-open');
 *
 * TicTacToe, Nardy and Chess each brought their own float before this existed;
 * they can move over here whenever they are next touched. */
.pv-game-result {
    position: fixed;
    inset: 0;
    z-index: 1080;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: rgba(11, 11, 26, 0.6);
    backdrop-filter: blur(3px);
    -webkit-backdrop-filter: blur(3px);
}
.pv-game-result.is-open {
    display: flex;
    animation: pv-game-result-in 0.28s ease-out;
}
.pv-game-result__panel {
    width: 100%;
    max-width: 420px;
    /* Tall panels — a rematch countdown plus two buttons — must not grow past
       the window, or the buttons end up unreachable again. */
    max-height: 90vh;
    overflow-y: auto;
    padding: 26px 24px 22px;
    background: var(--pv-card);
    border: 1px solid var(--pv-card-border);
    border-radius: 18px;
    box-shadow: 0 30px 80px rgba(0, 0, 0, 0.7);
}
@keyframes pv-game-result-in {
    from { opacity: 0; transform: scale(0.96); }
    to   { opacity: 1; transform: scale(1); }
}
@media (prefers-reduced-motion: reduce) {
    .pv-game-result.is-open { animation: none; }
}

/* AFK overlay — sits on top of the board card while the server lists
   the viewer as away. Blurs and darkens the map so the user can't keep
   "playing" on autopilot, and surfaces a centered card with the
   "Ich bin wieder da"-CTA. Disappears the instant the server flips
   the status back to connected. */
.pv-afk-overlay {
    position: absolute;
    inset: 0;
    z-index: 1400;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 16px;
    background: rgba(10, 10, 26, 0.55);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
    border-radius: inherit;
}
.pv-afk-modal {
    max-width: 360px;
    width: 100%;
    padding: 22px 20px;
    background: #1a1a2e;
    border: 1.5px solid #f59e0b;
    border-radius: 16px;
    box-shadow: 0 10px 32px rgba(0, 0, 0, 0.55);
    text-align: center;
}
.pv-afk-modal__icon {
    font-size: 44px;
    line-height: 1;
    color: #f59e0b;
    margin-bottom: 10px;
}
.pv-afk-modal__text {
    color: #fde68a;
    font-weight: 600;
    font-size: 14px;
    line-height: 1.4;
    margin-bottom: 16px;
}
.pv-afk-modal__btn {
    width: 100%;
    font-weight: 700;
}

/* Shared 2-player "opponent unavailable" wait overlay (game-wait-dialog.js) */
.pv-wait-overlay {
    position: fixed;
    inset: 0;
    z-index: 1500;
    display: none;
    align-items: center;
    justify-content: center;
    padding: 20px;
    background: rgba(10, 10, 26, 0.85);
    backdrop-filter: blur(6px);
    -webkit-backdrop-filter: blur(6px);
}
.pv-wait-card {
    max-width: 380px;
    width: 100%;
    padding: 26px 24px;
    background: #1a1a2e;
    border: 1.5px solid #2d2b5a;
    border-radius: 18px;
    box-shadow: 0 10px 32px rgba(0, 0, 0, 0.55);
    text-align: center;
}
/* "Keep waiting" - the second of the two answers the dialog offers, and it has
   to look like one. As a link it read as a footnote next to the red button,
   which is the wrong weight for a choice that is often the right one. */
.pv-wait-keep {
    margin-top: 8px;
    color: #cbd5e1;
    border-color: #3f3d70;
    background: rgba(45, 43, 90, 0.35);
    font-weight: 600;
    text-decoration: none;
}

.pv-wait-keep:hover,
.pv-wait-keep:focus {
    color: #fff;
    border-color: #5b579e;
    background: rgba(60, 57, 115, 0.6);
}

.pv-wait-title {
    color: #fff;
    font-size: 17px;
    font-weight: 700;
    margin-bottom: 10px;
}
.pv-wait-text {
    color: #94a3b8;
    font-size: 13px;
    line-height: 1.45;
    margin-bottom: 16px;
}
.pv-wait-elapsed {
    color: #818cf8;
    font-size: 15px;
    font-weight: 600;
    margin-bottom: 22px;
}

/* Dicewars room preview — hug the map tightly, no inner padding, so
   the rounded card border sits directly around the canvas. */
.pv-dw-preview {
    padding: 0;
    overflow: hidden;
}
.pv-dw-preview canvas { display: block; }

/* ---- Vertical player cards ("trading-card" layout) ----
   Rendered in a flex-wrap container so many cards fit side-by-side.
   Fixed width keeps the grid tidy; leaves room below for the future
   turn-timer bar that will live at the bottom of each card. */
/* Rounded container around the player cards — mirrors the Flutter
   Container(borderRadius: 20, border: Color(0xFF2D2B5A)) */
.pv-players-frame {
    background: #0d0d1f;
    border: 1px solid #2d2b5a;
    border-radius: 20px;
    padding: 12px;
}
.pv-players-grid {
    align-items: stretch;
    gap: 8px;
}
/* 8-player compact mode — only triggers when the grid actually
   contains an 8th card. With ≤7 players the default 76/8/12 layout
   stays untouched; at 8 players the row would otherwise wrap, so
   we shrink card width, gap and frame padding just enough to fit
   8*70 + 7*6 + 12 = 614px on the typical lobby column. */
.pv-players-frame:has(.pv-players-grid > :nth-child(8)) {
    padding: 6px;
}
.pv-players-grid:has(> :nth-child(8)) {
    gap: 6px;
}
.pv-players-grid:has(> :nth-child(8)) > .pv-player-card {
    width: 70px;
}
.pv-player-card {
    width: 76px;
    padding: 0 0 10px;
    border-radius: 12px;
    background: #141428;
    border: 1.5px solid #2d2b5a;
    display: flex;
    flex-direction: column;
    align-items: center;
    cursor: pointer;
    position: relative;
    overflow: hidden;
}
.pv-player-color-bar {
    width: 100%;
    height: 5px;
    flex-shrink: 0;
}
.pv-player-card--me {
    background: #1e1b4b;
    border-color: #6366f1;
}
.pv-player-card--empty {
    background: #0a0a1a;
    border-color: #1e1b4b;
    cursor: default;
}
/* Seat held by a computer opponent — same footprint as a player card,
   but with no profile behind it, so no pointer cursor. */
.pv-player-card--bot {
    background: #101c28;
    border-color: #1f4b5a;
    cursor: default;
}
.pv-player-avatar--bot {
    border-color: #22d3ee;
    background: #0f2a33;
    color: #67e8f9;
}
/* Free seat the creator may hand to the computer. The cards are far too
   narrow for a labelled button, so the whole card is the target and the
   robot chip in the corner is the affordance; the title spells it out. */
.pv-player-card--seatable {
    cursor: pointer;
}
.pv-player-card--seatable:hover {
    border-color: #22d3ee;
    background: #101c28;
}
.pv-seat-bot-chip {
    position: absolute;
    right: 4px;
    /* Just below the colour bar — the same corner the kick button uses on an
       occupied card, which an empty seat never has. */
    top: 9px;
    display: flex;
    align-items: center;
    gap: 1px;
    padding: 1px 4px;
    border-radius: 8px;
    background: #0f2a33;
    border: 1px solid #1f4b5a;
    color: #67e8f9;
    font-size: 11px;
    line-height: 1;
    pointer-events: none;
}
.pv-seat-bot-chip__plus {
    font-size: 8px;
}
.pv-player-card--seatable:hover .pv-seat-bot-chip {
    background: #164e63;
    color: #a5f3fc;
}
.pv-player-avatar-wrap {
    position: relative;
    width: 52px;
    height: 52px;
    margin-top: 8px;
}
.pv-player-avatar {
    width: 52px;
    height: 52px;
    border-radius: 10px;
    border: 2px solid #475569;
    overflow: hidden;
    background: #2d2b5a;
    display: flex;
    align-items: center;
    justify-content: center;
    color: #94a3b8;
    font-size: 22px;
}
.pv-player-card--empty .pv-player-avatar {
    border-style: dashed;
    border-color: #2d2b5a;
    background: #1e1b4b;
    color: #4a4a6a;
}
.pv-player-avatar img {
    width: 100%;
    height: 100%;
    object-fit: cover;
}
.pv-player-avatar-fallback {
    width: 100%;
    height: 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    font-size: 22px;
}
.pv-player-conn-dot {
    position: absolute;
    right: -2px;
    bottom: -2px;
    width: 10px;
    height: 10px;
    border-radius: 50%;
    border: 1.5px solid #0d0d1f;
}
.pv-player-kick-btn {
    position: absolute;
    top: -4px;
    right: -4px;
    width: 18px;
    height: 18px;
    padding: 0;
    font-size: 14px;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 700;
    color: #fff;
    background: #ef4444;
    border: 1.5px solid #0d0d1f;
    border-radius: 50%;
    cursor: pointer;
}
.pv-player-name {
    margin-top: 6px;
    width: 100%;
    color: #fff;
    font-size: 11px;
    font-weight: 600;
    line-height: 1.2;
    text-align: center;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
/* Turn-timer bar: left-anchored so it shrinks right→left like the
   Flutter app. The parent card is a centered flex column, which would
   otherwise center the shrinking bar and make it collapse from both
   sides. align-self overrides that for this one child. */
.pv-player-timer {
    align-self: flex-start;
}
.pv-player-you {
    margin-top: 2px;
    color: #818cf8;
    font-size: 9px;
    font-weight: 700;
    text-align: center;
}
/* Spielstärke des Computers auf diesem Sitz, als Sterne unten links auf der
   Karte. Sterne statt des Stufennamens, weil auf 76px Kartenbreite jeder Name
   abgeschnitten wird und drei Sterne ohne Übersetzung sagen, wie stark der
   Gegner spielt. Sichtbar statt hinter einem Menü versteckt: an einem Tisch
   für acht will man mit einem Blick sehen, wen man vor sich hat. */
.pv-bot-stars {
    /* Auf der unteren Kante des Avatars, wie das Kreuz oben rechts und der
       Verbindungspunkt eines Menschen. In den Fluss gestellt wäre die Karte
       höher geworden - und Platz ist auf dem Handy das Knappe. */
    position: absolute;
    left: 50%;
    /* Innen auf der Kante, nicht darüber hinaus - genau wie in der App. Hing
       der Chip halb unter dem Avatar, sassen die Sterne in beiden Clients
       verschieden hoch. */
    bottom: 0;
    transform: translateX(-50%);
    display: flex;
    gap: 1px;
    padding: 1px 4px;
    border: 1px solid #1f4b5a;
    border-radius: 8px;
    /* Deckend, damit die Sterne nicht auf dem Roboter darunter liegen. */
    background: #101c28;
    color: #67e8f9;
    font-size: 9px;
    line-height: 1;
}
button.pv-bot-stars {
    cursor: pointer;
}
button.pv-bot-stars:hover {
    background: #123043;
    border-color: #22d3ee;
}
/* Der leere Stern muss als Platzhalter lesbar bleiben, ohne den vollen
   Konkurrenz zu machen. */
.pv-bot-stars .bi-star {
    color: #2c5866;
}
/* Nur Zuschauer und Mitspieler sehen die Stufe, ändern darf sie der Ersteller. */
.pv-bot-stars--static {
    color: #4d8b9b;
}
.pv-bot-stars--static .bi-star {
    color: #24414c;
}
/* Im Menü stehen die Sterne vor dem Namen, dort etwas grösser - und wieder im
   Fluss statt an einer Kante. */
.pv-bot-stars--menu {
    position: static;
    transform: none;
    display: inline-flex;
    margin: 0;
    padding: 0;
    gap: 1px;
    border: 0;
    background: transparent;
    font-size: 11px;
}
/* Das Menü hängt am body, nicht an der Karte: die Karten liegen in einem
   Raster mit overflow, in dem die Liste abgeschnitten würde. */
.pv-bot-level-menu {
    position: absolute;
    z-index: 1200;
    min-width: 120px;
    padding: 4px;
    border: 1px solid #1f4b5a;
    border-radius: 8px;
    background: #0d1922;
    box-shadow: 0 8px 24px rgba(0, 0, 0, .55);
}
.pv-bot-level-menu__item {
    display: flex;
    align-items: center;
    gap: 8px;
    width: 100%;
    padding: 6px 10px;
    border: 0;
    border-radius: 6px;
    background: transparent;
    color: #cbd5e1;
    font-size: 13px;
    text-align: left;
    cursor: pointer;
}
.pv-bot-level-menu__item:hover {
    background: #123043;
    color: #67e8f9;
}
.pv-player-name.pv-player-seat-label {
    color: #4a4a6a;
    font-style: italic;
}
.pv-player-card--forfeited .pv-player-name {
    color: #475569;
}
.pv-player-avatar-img--forfeited {
    filter: grayscale(1);
}
.pv-player-afk-badge {
    position: absolute;
    top: -4px;
    right: -4px;
    padding: 1px 4px;
    background: #f59e0b;
    color: #1a1a2e;
    font-size: 9px;
    font-weight: 700;
    border: 1.5px solid #0d0d1f;
    border-radius: 4px;
}
/* Finishing rank badge — pill-shaped, attached to the top-right of the
   whole card. Top-3 show a medal/trophy icon next to the rank number;
   rank 4+ shows only the number on a neutral background. */
.pv-player-rank-badge {
    position: absolute;
    top: 8px;
    right: 8px;
    display: inline-flex;
    align-items: center;
    gap: 3px;
    height: 22px;
    min-width: 22px;
    padding: 0 6px;
    z-index: 2;
    font-size: 12px;
    font-weight: 800;
    line-height: 1;
    color: #0a0a1a;
    border-radius: 11px;
    border: 2px solid #0d0d1f;
    box-shadow: 0 2px 6px rgba(0, 0, 0, 0.45);
}
.pv-player-rank-badge i {
    font-size: 12px;
    line-height: 1;
}
.pv-player-rank-num {
    font-variant-numeric: tabular-nums;
}
.pv-player-rank-badge--gold    { background: linear-gradient(135deg, #fde68a 0%, #f59e0b 100%); }
.pv-player-rank-badge--silver  { background: linear-gradient(135deg, #f1f5f9 0%, #94a3b8 100%); }
.pv-player-rank-badge--bronze  { background: linear-gradient(135deg, #fbbf9d 0%, #b45309 100%); color: #fff; }
.pv-player-rank-badge--other   { background: #64748b; color: #fff; padding: 0 8px; }

/* Live "in the lead" badge — shown while the game is being played
   on the player whose state metric (largest cluster, dice, sectors)
   is currently best. Reads "1st" so the meaning is unambiguous;
   sits TOP-LEFT so it never overlaps the rank pill in TOP-RIGHT. */
.pv-player-leader-badge {
    position: absolute;
    top: 8px;
    left: 8px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 22px;
    height: 22px;
    padding: 0 6px;
    z-index: 2;
    font-size: 11px;
    font-weight: 800;
    line-height: 1;
    color: #1f1300;
    background: linear-gradient(135deg, #fde68a 0%, #f59e0b 100%);
    border-radius: 11px;
    border: 2px solid #0d0d1f;
    box-shadow: 0 0 0 0 rgba(250, 204, 21, 0.7);
    animation: pvLeaderPulse 2.4s ease-in-out infinite;
}
@keyframes pvLeaderPulse {
    0%, 100% { box-shadow: 0 0 0 0 rgba(250, 204, 21, 0.6); }
    50%      { box-shadow: 0 0 0 6px rgba(250, 204, 21, 0); }
}

/* ---- Compact spectator card (flex-wrap layout) ---- */
.pv-spectator-card {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 10px;
    border-radius: 12px;
    min-height: 40px;
    cursor: pointer;
}
.pv-spectator-card .pv-spectator-name {
    font-size: .8125rem;
    font-weight: 600;
    color: #e0e7ff;
    max-width: 140px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.pv-spectator-card--raised {
    padding-right: 8px;
}
.pv-hand-queue-badge {
    position: absolute;
    top: -5px;
    right: -5px;
    width: 18px;
    height: 18px;
    padding: 0;
    border-radius: 50%;
    background: #f59e0b;
    color: #1a1a2e;
    font-size: .6875rem;
    font-weight: 700;
    line-height: 18px;
    text-align: center;
    border: 2px solid #1a1a2e;
    box-sizing: border-box;
    display: flex;
    align-items: center;
    justify-content: center;
}
.pv-spectator-card .pv-avatar-wrap {
    position: relative;
}

.pv-invite-seat-btn {
    padding: 2px 8px;
    font-size: .6875rem;
    border-radius: 8px;
    background: rgba(99, 102, 241, .1);
    color: #818cf8;
    border: 1px solid rgba(99, 102, 241, .3);
}

.pv-participant-empty {
    color: #475569;
    font-size: .875rem;
}

/* Ready-button active state (player is ready — button shows "Not ready") */
.pv-btn--ready {
    background: #1e1b4b;
    color: #818cf8;
    border: 1px solid #4338ca;
}
.pv-btn--ready:hover {
    background: #2e2754;
    border-color: #6366f1;
}

/* ---- Player profile (show page) ---- */
.pv-avatar-lg {
    width: 80px;
    height: 80px;
    object-fit: cover;
}

.pv-avatar-lg-placeholder {
    width: 80px;
    height: 80px;
}

.pv-avatar-lg-icon { font-size: 2.5rem; }

.pv-status-dot-icon {
    font-size: 0.55rem;
    vertical-align: middle;
}

/* ---- Players list (index page) ---- */
.pv-players-search { max-width: 420px; }
.pv-players-empty-icon { font-size: 3rem; }
.pv-player-list-wrap { overflow: hidden; }

.pv-player-avatar-sm {
    width: 42px;
    height: 42px;
    object-fit: cover;
}

.pv-player-avatar-sm-placeholder {
    width: 42px;
    height: 42px;
    background: var(--pv-dark-2);
}

.pv-player-avatar-sm-icon { font-size: 1.3rem; }

.pv-player-username {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.pv-player-divider {
    border-top: 1px solid var(--pv-card-border);
    margin: 0;
}

.pv-player-meta {
    font-size: 0.72rem;
    color: var(--pv-text-muted);
    margin-top: 2px;
}

.pv-stat-badge {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    background: rgba(108, 99, 255, 0.12);
    border: 1px solid rgba(108, 99, 255, 0.25);
    border-radius: 6px;
    padding: 1px 7px;
    font-size: 0.7rem;
    color: var(--pv-text-muted);
    white-space: nowrap;
}

.pv-stat-badge .pv-stat-elo {
    color: var(--pv-primary);
    font-weight: 600;
}

/* ---- Profile / Avatar ---- */
.pv-member-since { font-size: .75rem; }

.pv-avatar-pending-badge {
    background: rgba(99, 102, 241, .2);
    color: #818cf8;
    font-size: .7rem;
}

#avatarWrap {
    width: 80px;
    height: 80px;
    cursor: pointer;
}

.pv-avatar-img {
    width: 80px;
    height: 80px;
    object-fit: cover;
    border-radius: 16px;
    border: 3px solid var(--pv-primary);
    display: block;
}

.pv-avatar-initials {
    width: 80px;
    height: 80px;
    border-radius: 16px;
    border: 3px solid #3730A3;
    background: linear-gradient(135deg, #6366F1, #4338CA);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: 2rem;
    font-weight: 800;
    color: #fff;
}

.pv-avatar-overlay {
    position: absolute;
    bottom: 0;
    right: 0;
    width: 26px;
    height: 26px;
    border-radius: 50%;
    background: #6366F1;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: .8rem;
    color: #fff;
    box-shadow: 0 2px 6px rgba(0, 0, 0, .4);
    transition: background .15s;
}

.pv-avatar-wrap:hover .pv-avatar-overlay { background: #4338CA; }

.pv-lang-btn {
    background: var(--pv-card);
    border: 1px solid var(--pv-card-border);
    color: #fff;
    text-align: left;
    justify-content: flex-start;
}

.pv-lang-btn:hover,
.pv-lang-btn:focus,
.pv-lang-btn:active,
.pv-lang-btn.show {
    background: var(--pv-card) !important;
    border-color: var(--pv-primary) !important;
    color: #fff !important;
    box-shadow: none !important;
}

.pv-lang-btn::after { margin-left: auto; }

/* Dark-themed dropdown menus for profile settings dropdowns */
.pv-lang-btn + .dropdown-menu {
    background: var(--pv-card);
    border: 1px solid var(--pv-card-border);
}

.pv-lang-btn + .dropdown-menu .dropdown-item {
    color: var(--pv-text);
}

.pv-lang-btn + .dropdown-menu .dropdown-item:hover {
    background: var(--pv-primary-glow);
    color: var(--pv-primary);
}

.pv-lang-btn + .dropdown-menu .dropdown-item.active {
    background: var(--pv-primary);
    color: #fff;
}

.pv-lang-btn + .dropdown-menu .dropdown-divider {
    border-color: var(--pv-card-border);
}

.pv-crop-modal .modal-content {
    background: var(--pv-card);
    border: 1px solid var(--pv-card-border);
}

.pv-crop-modal .modal-header,
.pv-crop-modal .modal-footer {
    border-color: var(--pv-card-border);
}

/* ---- Generic dark modal (report dialog etc.) ---- */
/* Force explicit centering + width cap so the dialog never sticks to the
   left edge / stretches full-width, regardless of viewport or zoom level. */
.pv-modal .modal-dialog {
    max-width: min(500px, calc(100% - 1rem));
    margin-left: auto;
    margin-right: auto;
}

.pv-modal .modal-content {
    background: var(--pv-card);
    border: 1px solid var(--pv-card-border);
    color: var(--pv-text);
}

.pv-modal .modal-header,
.pv-modal .modal-footer {
    border-color: var(--pv-card-border);
}

.pv-modal .btn-close {
    filter: invert(1) grayscale(1) brightness(1.8);
}

.pv-modal .form-select,
.pv-modal .form-control {
    background: var(--pv-input-bg);
    border: 1px solid var(--pv-input-border);
    color: var(--pv-text);
}

.pv-modal .form-select:focus,
.pv-modal .form-control:focus {
    background: var(--pv-input-bg);
    border-color: var(--pv-primary);
    color: var(--pv-text);
    box-shadow: 0 0 0 0.2rem var(--pv-primary-glow);
}

.pv-modal .form-select option {
    color: initial;
}

.pv-crop-area {
    max-height: 360px;
    overflow: hidden;
}

.pv-crop-area img {
    max-width: 100%;
    display: block;
}

/* Marks the page as watch-only. Deliberately muted — it is a status note, not
   an action. */
.pv-spectator-badge {
    display: inline-flex;
    align-items: center;
    padding: 4px 10px;
    border: 1px solid rgba(148, 163, 184, 0.35);
    border-radius: 999px;
    color: #94a3b8;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    white-space: nowrap;
}

/* Result overlay — big emoji on its own line, like the app.
   Named pv-ttt-* for historical reasons: TicTacToe brought these in and has
   since moved to its own stylesheet. Hexaris still uses them, so they stay. */
.pv-ttt-result-emoji { font-size: 40px; line-height: 1; margin-bottom: 6px; }

.pv-ttt-progress {
    height: 6px;
    border-radius: 4px;
}

.pv-ttt-progress-bar {
    width: 100%;
    transition: width 0.1s linear;
}

.pv-ttt-countdown-text {
    color: #818cf8;
    opacity: .8;
}

.pv-game-you-label {
    font-size: .7rem;
    color: #818cf8;
}

.pv-game-dot {
    width: 8px;
    height: 8px;
    border-radius: 50%;
    display: inline-block;
    margin-right: 6px;
    vertical-align: middle;
    flex-shrink: 0;
}

/* Game-specific styles are in separate files (battleship.css, etc.) */

/* ── Player profile stat grid ─────────────────────────────────────────────── */
.pv-stat-value {
    font-size: 1.4rem;
    font-weight: 700;
    color: #fff;
    line-height: 1.2;
}

.pv-stat-label {
    font-size: .7rem;
    color: var(--pv-text-muted);
    text-transform: uppercase;
    letter-spacing: .04em;
    margin-top: 2px;
}

.pv-match-row-border {
    border-bottom: 1px solid #1e1b4b;
}

/* ── Achievement badges ───────────────────────────────────────────────────── */
.pv-achievement-badge {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 6px 12px;
    background: #1e1b4b;
    border: 1px solid #3730a3;
    border-radius: 20px;
    cursor: default;
    transition: background .15s;
}

.pv-achievement-badge:hover {
    background: #2d2b5a;
}

.pv-achievement-emoji {
    font-size: 1rem;
    line-height: 1;
}

.pv-achievement-label {
    font-size: .78rem;
    color: #a5b4fc;
    font-weight: 500;
    white-space: nowrap;
}

/* ---- Player Profile ---- */
.pv-profile-avatar {
    width: 88px;
    height: 88px;
    object-fit: cover;
    border: 3px solid #3730a3;
}

.pv-profile-avatar-placeholder {
    width: 88px;
    height: 88px;
    border: 3px solid #3730a3;
}

.pv-profile-avatar-icon {
    font-size: 2.5rem;
}

.pv-status-icon {
    font-size: .55rem;
}

.pv-text-subtle {
    color: #94a3b8;
}

.pv-section-heading {
    letter-spacing: .05em;
    font-size: .75rem;
}

.pv-profile-actions {
    border-top: 1px solid #2d2b5a;
}

.pv-btn-remove-friend {
    color: #ef4444;
    border: 1px solid rgba(239, 68, 68, 0.35);
    background: transparent;
    font-size: .78rem;
}

.pv-btn-remove-friend:hover {
    background: rgba(239, 68, 68, 0.1);
    color: #ef4444;
}

.pv-match-icon {
    width: 28px;
    height: 28px;
    border-radius: 6px;
    display: flex;
    align-items: center;
    justify-content: center;
    flex-shrink: 0;
}

.pv-match-icon i {
    font-size: .9rem;
}

.pv-match-meta {
    font-size: .75rem;
}

.pv-match-result-badge {
    font-size: .72rem;
}

.pv-card-overflow-hidden {
    overflow: hidden;
}

/* ---- Footer ---- */
.pv-footer-version {
    color: #64748b;
}

.pv-footer-link {
    color: #818cf8;
    text-decoration: none;
}

.pv-footer-link:hover {
    color: #a5b4fc;
    text-decoration: underline;
}

/* ---- Legal pages (privacy, imprint) ---- */
.pv-legal {
    max-width: 860px;
    color: #cbd5e1;
    line-height: 1.7;
}

.pv-legal h1 {
    color: #e2e8f0;
    font-weight: 700;
}

.pv-legal h2 {
    color: #a5b4fc;
    margin-top: 1.75rem;
    margin-bottom: 1rem;
    font-weight: 600;
}

.pv-legal a {
    color: #818cf8;
    text-decoration: none;
}

.pv-legal a:hover {
    text-decoration: underline;
}

/* While JS hasn't yet assembled the email, render the " [at] "
   placeholder slightly muted so the fallback reads naturally. */
.pv-email-at {
    color: #64748b;
    font-size: 0.95em;
}

.pv-legal address {
    font-style: normal;
    padding-left: 1rem;
    border-left: 3px solid #3730a3;
    color: #e2e8f0;
}

.pv-legal .text-muted {
    color: #94a3b8 !important;
}

.pv-legal code {
    color: #c7d2fe;
    background: rgba(99, 102, 241, 0.12);
    padding: 0.1em 0.35em;
    border-radius: 4px;
    font-size: 0.92em;
}

.pv-legal-table {
    --bs-table-bg: rgba(30, 27, 75, 0.5);
    --bs-table-color: #cbd5e1;
    --bs-table-border-color: rgba(55, 48, 163, 0.35);
    --bs-table-striped-bg: rgba(55, 48, 163, 0.18);
    --bs-table-striped-color: #cbd5e1;
    --bs-table-hover-bg: rgba(99, 102, 241, 0.18);
    --bs-table-hover-color: #e2e8f0;
    border-radius: 0.5rem;
    overflow: hidden;
}

.pv-legal-table > thead > tr > th {
    background: rgba(55, 48, 163, 0.55);
    color: #e2e8f0;
    border-color: rgba(55, 48, 163, 0.45);
}

.pv-legal-table > tbody > tr > td {
    vertical-align: top;
}

/* ---- Toast notifications ---- */
.pv-toast-container {
    position: fixed;
    bottom: 80px;
    right: 16px;
    z-index: 9999;
    display: flex;
    flex-direction: column-reverse;
    gap: 8px;
    pointer-events: none;
}

.pv-toast {
    background: #1a1a2e;
    border: 1px solid rgba(108, 99, 255, 0.3);
    border-radius: 8px;
    padding: 10px 16px;
    color: #ccd6f6;
    font-size: 0.82rem;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.5);
    pointer-events: auto;
    animation: pvToastIn 0.25s ease-out;
    max-width: 320px;
}

.pv-toast--success { border-color: rgba(74, 222, 128, 0.4); }
.pv-toast--error   { border-color: rgba(248, 113, 113, 0.4); color: #f87171; }

@keyframes pvToastIn {
    from { opacity: 0; transform: translateY(20px); }
    to   { opacity: 1; transform: translateY(0); }
}

/* ---- Dicewars configure-screen slider ----
   Compact range input with 7 tick marks drawn on the track (one per
   stop value between min=2 and max=8) AND a Material-style filled
   left half / unfilled right half. Mirrors what Material's Slider
   draws in the Flutter app.

   Track layering (top to bottom):
     1. radial-gradient — 7 light-indigo dots positioned exactly at
        each stop. `circle at 0% 50%` aligns the dot to the LEFT edge
        of every tile and `background-position: 0.5rem 50%` shifts the
        first dot inward by half a thumb-width, so the outermost dots
        line up with the thumb's centre at min/max.
     2. linear-gradient — splits the track at --pv-fill (set by JS on
        every slider input) so the part the thumb has passed is
        accent-coloured and the rest stays in the dark track colour.
   Track height is 4px to match the thinner Material look. */
/* Seat picker in the create-room dialog. Shares the Dicewars slider's
   colours, but without tick marks: a range of 50 cannot show them legibly. */
.pv-seat-slider {
    --pv-track: #2d2b5a;
    --pv-fill-color: #6366f1;
    --pv-fill: 0%;
}
.pv-seat-slider::-webkit-slider-runnable-track {
    height: 4px;
    border-radius: 999px;
    background: linear-gradient(to right, var(--pv-fill-color) var(--pv-fill), var(--pv-track) var(--pv-fill));
}
.pv-seat-slider::-moz-range-track {
    height: 4px;
    border-radius: 999px;
    background: linear-gradient(to right, var(--pv-fill-color) var(--pv-fill), var(--pv-track) var(--pv-fill));
}
.pv-seat-slider::-webkit-slider-thumb {
    margin-top: -6px;
}
.pv-seat-slider::-moz-range-thumb {
    transform: translateY(-2px);
}

.pv-dwc-slider {
    flex: 0 1 140px;
    min-width: 110px;
    max-width: 160px;
    --pv-track: #2d2b5a;
    --pv-fill-color: #6366f1;
    --pv-tick: #c7d2fe;
    --pv-fill: 0%;
}
.pv-dwc-slider::-webkit-slider-runnable-track {
    height: 4px;
    border-radius: 999px;
    background-image:
        radial-gradient(circle at 0% 50%, var(--pv-tick) 1.5px, transparent 1.7px),
        linear-gradient(to right, var(--pv-fill-color) var(--pv-fill), var(--pv-track) var(--pv-fill));
    background-position: 0.5rem 50%, 0 0;
    background-size: calc((100% - 1rem) / 6) 100%, 100% 100%;
    background-repeat: repeat-x, no-repeat;
}
.pv-dwc-slider::-moz-range-track {
    height: 4px;
    border-radius: 999px;
    background-image:
        radial-gradient(circle at 0% 50%, var(--pv-tick) 1.5px, transparent 1.7px),
        linear-gradient(to right, var(--pv-fill-color) var(--pv-fill), var(--pv-track) var(--pv-fill));
    background-position: 0.5rem 50%, 0 0;
    background-size: calc((100% - 1rem) / 6) 100%, 100% 100%;
    background-repeat: repeat-x, no-repeat;
}
/* Nudge the thumb 2px upward — visual fine-tuning per user feedback. */
.pv-dwc-slider::-webkit-slider-thumb {
    margin-top: -6px;
}
.pv-dwc-slider::-moz-range-thumb {
    transform: translateY(-2px);
}

/* ══ Hexaris ═══════════════════════════════════════════════════════════════ */

/* Gem score HUD above the board — ruby count vs pearl count (prototype HUD). */
.pv-hex-hud {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 22px;
}
.pv-hex-score {
    display: flex;
    align-items: center;
    gap: 8px;
    font-size: 20px;
    font-weight: 700;
    color: #e8eef5;
    background: rgba(14, 18, 28, 0.72);
    border: 1px solid rgba(255, 255, 255, 0.1);
    border-radius: 10px;
    padding: 4px 14px;
    min-width: 54px;
    justify-content: center;
    transition: box-shadow 0.25s ease, border-color 0.25s ease;
}
.pv-hex-score--active {
    border-color: rgba(255, 224, 102, 0.65);
    box-shadow: 0 0 14px rgba(255, 224, 102, 0.25);
}
.pv-hex-gem-dot {
    width: 15px;
    height: 15px;
    border-radius: 50%;
    display: inline-block;
}
.pv-hex-gem-dot--ruby {
    background: radial-gradient(circle at 34% 30%, #ff8fa5, #d61f45 55%, #7a0b22);
}
.pv-hex-gem-dot--pearl {
    background: radial-gradient(circle at 34% 30%, #ffffff, #c4d3e8 55%, #7f93b2);
}

/* Board wrapper — the canvas fills it; the aspect ratio matches the board
   geometry (width 14 hex sizes, height ~10.5 squashed), capped so the whole
   board stays visible above the fold on tall phones. */
.pv-hex-board-wrap {
    position: relative;
    width: 100%;
    aspect-ratio: 14 / 10.5;
    max-height: 62vh;
    margin: 0 auto;
}
.pv-hex-board-wrap canvas {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    display: block;
    cursor: pointer;
    touch-action: manipulation;
}

/* Seat nobody has taken yet. Muted on purpose - it says "somebody is still
   expected here" without competing with the players who have arrived. */
.pv-player-card--awaited {
    border-color: #2d2b5a;
    border-style: dashed;
    opacity: .55;
}

.pv-player-avatar--awaited {
    border-color: #475569;
    background: #1a1a35;
    color: #94a3b8;
}

.pv-player-card--awaited .pv-player-name {
    color: #94a3b8;
    font-style: italic;
}

/* ---- Online counter ---- */

/* Deliberately quiet: it sits between the navigation links and must not
   compete with them. The dot pulses only while somebody is online, so an
   empty platform does not blink at you. */
.pv-online .nav-link {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.pv-online .bi-broadcast {
    color: #34d399;
    font-size: 0.9rem;
}

.pv-online-menu {
    min-width: 260px;
    font-size: 0.9rem;
    max-height: 70vh;
    overflow-y: auto;
}

/* Group heading - "waiting for players" above "in progress". The order is the
   whole point of the feature, so the two need to read as separate blocks. */
.pv-online-group {
    padding: 8px 16px 4px;
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: #818cf8;
}

.pv-online-room {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
}

/* Name über Spiel, zweizeilig - der Raumname allein sagt nicht, worauf man
   sich einlässt. */
.pv-online-room-text {
    display: flex;
    flex-direction: column;
    min-width: 0;
    line-height: 1.25;
}

.pv-online-room-name {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.9rem;
}

.pv-online-room-game {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: 0.76rem;
    color: #64748b;
}

.pv-online-room-count {
    flex-shrink: 0;
    font-variant-numeric: tabular-nums;
    color: #94a3b8;
}

/* Players and watchers apart: "8 (2 + 6)" says something "8" cannot. */
.pv-online-detail {
    font-size: 0.78em;
    opacity: 0.75;
}

/* The ones online but in no room. No link - there is nowhere to send them. */
.pv-online-rest,
.pv-online-empty {
    padding: 8px 16px;
    font-size: 0.82rem;
    color: #64748b;
}

.pv-online-rest {
    border-top: 1px solid rgba(148, 163, 184, 0.18);
    margin-top: 4px;
}
