/* ═══════════════════════════════════════════════════════════════
   XES — activity-circle styles (lobby left panel)
   Avatar ring + VoicePulse halos + radar blip + partner-muted
   state + dice spinner (3D mechanics).
   ═══════════════════════════════════════════════════════════════ */

/* ── Avatar ── */
.avatar-wrap { display: flex; justify-content: center; position: relative; }


.avatar-ring {
    --talk-level: 0; /* partner's voice — purple pulse */
    --mic-level: 0;  /* own voice     — green tint */
    width: 160px;
    height: 160px;
    border-radius: 50%;
    background: #000; /* solid black disc (default theme) — blocks the animation under the speaker */
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow:
        /* PARTNER (purple) */
        0 0 calc(30px + var(--talk-level) * 120px) rgba(160, 130, 255, calc(0.3 + var(--talk-level) * 0.85)),
        0 0 calc(8px + var(--talk-level) * 40px)   rgba(220, 200, 255, calc(var(--talk-level) * 0.9)),
        inset 0 0 calc(var(--talk-level) * 50px)   rgba(255, 255, 255, calc(var(--talk-level) * 0.22)),
        inset 0 0 calc(var(--talk-level) * 25px)   rgba(200, 180, 255, calc(var(--talk-level) * 0.45)),
        /* OWN (green) — smaller, layered on top */
        0 0 calc(20px + var(--mic-level) * 70px) rgba(80, 230, 150, calc(0.25 + var(--mic-level) * 0.65)),
        inset 0 0 calc(var(--mic-level) * 50px)  rgba(120, 255, 180, calc(var(--mic-level) * 0.5)),
        /* ambient identity halo — pink to the left, cyan to the right (always on) */
        -30px 6px 50px -6px rgba(255, 45, 117, 0.5),
        30px 6px 50px -6px rgba(0, 200, 255, 0.5),
        /* drop shadow stays put */
        0 8px 32px rgba(0,0,0,0.5);
    filter: brightness(calc(1 + var(--talk-level) * 0.55 + var(--mic-level) * 0.3)) saturate(calc(1 + var(--talk-level) * 0.5));
    transition: box-shadow 60ms linear, filter 60ms linear;
    flex-shrink: 0;
    position: relative;
    /* It's a tap target now — kill the mobile tap-flash, selection box + long-press callout. */
    -webkit-tap-highlight-color: transparent;
    -webkit-user-select: none;
            user-select: none;
    -webkit-touch-callout: none;
}

/* Logo subtly swells with the voice level too. */
.avatar-ring .avatar-img {
    transform: scale(calc(1 + var(--talk-level) * 0.08));
    transition: transform 60ms linear;
    position: relative;
    z-index: 0; /* sits under the VU bars so they paint over the speaker cone */
}

/* Inner pulse — concentric rings expanding from the centre of the activity
 * circle, contained by overflow:hidden on the ring. Two staggered rings via
 * ::before / ::after, plus a soft centre bloom on the parent.
 */
/* Radial frequency bars — painted by VoicePulse._drawBars from the
 * partner's AnalyserNode. Sits between the gradient and the logo.
 */
.voice-bars {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    pointer-events: none;
    z-index: 1;
}

/* Partner muted — label sits fixed just below the call timer (top-right), so it
   never obscures the speaker face. Styled to MATCH the timer exactly (colour,
   size, weight) so the two read as one stacked readout. */
.muted-label {
    display: none;
    position: fixed;
    top: 80px;             /* flush under the timer (62px + ~16px line + 2px gap) */
    right: 30px;           /* right edge lines up with the profile button + the call timer */
    text-align: right;
    color: rgba(255, 255, 255, 0.72);
    font-weight: 400;
    font-size: 0.72rem;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    z-index: 99;
    pointer-events: none;
}
.avatar-wrap.partner-muted .muted-label { display: block; }
.avatar-wrap.partner-muted .voice-bars { display: none; }
.avatar-wrap.partner-muted .avatar-ring .avatar-img {
    opacity: 0.82;
    filter: sepia(0.85) saturate(5) hue-rotate(-18deg) brightness(0.95);
}
.avatar-wrap.partner-muted .avatar-ring {
    background: radial-gradient(circle, #3a1414 0%, #190707 100%) !important;
    box-shadow:
        0 0 30px rgba(200, 55, 55, 0.4),
        0 8px 32px rgba(0,0,0,0.5) !important;
    filter: none !important;
}
/* Also kill the outside radar blip while muted — nothing's happening. */
.avatar-wrap.partner-muted.in-call .avatar-ring::before,
.avatar-wrap.partner-muted.in-call .avatar-ring::after {
    animation: none !important;
    opacity: 0 !important;
}

/* Radar blip — two staggered rings emanate from the activity circle while in a call. */
.avatar-wrap.in-call .avatar-ring::before,
.avatar-wrap.in-call .avatar-ring::after {
    content: '';
    position: absolute;
    inset: 0;
    border-radius: 50%;
    border: 2px solid rgba(124, 117, 255, 0.55);
    pointer-events: none;
    animation: radar-blip 2.4s cubic-bezier(0.16, 1, 0.3, 1) infinite;
}
.avatar-wrap.in-call .avatar-ring::after {
    animation-delay: 1.2s;
}
@keyframes radar-blip {
    0%   { transform: scale(1);    opacity: 0.55; }
    80%  { opacity: 0; }
    100% { transform: scale(1.55); opacity: 0; }
}

.avatar-img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    border-radius: 50%;
    opacity: 0.6; /* default theme */
    outline: none;
    border: none;
    -webkit-tap-highlight-color: transparent;
    -webkit-user-select: none;
            user-select: none;
    -webkit-touch-callout: none;
}
.avatar-img:focus,
.avatar-img:focus-visible { outline: none; }

/* ── Speaker = mute button (during a call) ──────────────────────────────
   The disc is a press-to-mute control: a mic glyph hints it (a brief fade on
   call start + on hover), muted shows a red mic-off and dims the speaker, and a
   tap gives a quick press-bounce. */
.avatar-wrap.in-call .avatar-ring { cursor: pointer; }
@keyframes speakerPress {
    0%   { transform: scale(1); }
    28%  { transform: scale(0.92); }
    62%  { transform: scale(1.025); }
    100% { transform: scale(1); }
}
.avatar-ring.speaker-press { animation: speakerPress 0.4s ease; }

/* Mic-mute button (in the Call row) — flips its mic icon when muted.
   #mute-btn (.btn-mute) gets `.muted` from app.js updateMuteButton(). */
.btn-mute .self-mic-off { display: none; }
.btn-mute.muted .self-mic-on  { display: none; }
.btn-mute.muted .self-mic-off { display: block; color: #ff5a6a; }

/* Deafen indicator — the speaker doubles as a "mute incoming sound" button;
   app.js toggles `.deafened` on .avatar-wrap (and mutes the partner's audio). */
.deafen-ind {
    position: absolute; inset: 0;
    display: flex; align-items: center; justify-content: center;
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.25s ease;
    z-index: 2;
}
.deafen-ind svg { width: 38px; height: 38px; color: #fff; filter: drop-shadow(0 1px 3px rgba(0,0,0,0.65)); }
.deafen-off { display: none; }
.avatar-wrap.deafened .deafen-ind { opacity: 1; }
.avatar-wrap.deafened .deafen-on  { display: none; }
.avatar-wrap.deafened .deafen-off { display: block; color: #ff5a6a; }
.avatar-wrap.deafened .avatar-ring .avatar-img { opacity: 0.35; }
/* Call duration — fades in at the bottom of the ring while in a call.
   Stays visible during partner-muted (timer keeps ticking; the muted
   label owns the centre, the timer owns the bottom). */
.call-timer {
    display: none;
    position: fixed;
    top: 62px;              /* sits just below the 56px nav bar */
    right: 30px;            /* nav pad (20) + nav-right pad (10) → right edge lines up with the profile button */
    color: rgba(255, 255, 255, 0.72);
    font-size: 0.72rem;
    font-family: 'Space Mono', ui-monospace, monospace;
    font-weight: 400;
    font-variant-numeric: tabular-nums;
    letter-spacing: 0.06em;
    pointer-events: none;
    z-index: 99;            /* below nav (100) but above everything else */
}
/* Show during a call — the in-call class lives on .avatar-wrap but the
   timer is now a sibling at body level, so we key off body.in-call instead. */
body.in-call .call-timer { display: block; }

/* ── Matchmaking Die ─────────────────────────────────────────────── */
.dice {
    --size: 56px;
    width: var(--size);
    height: var(--size);
    position: relative;
    transform-style: preserve-3d;
    /* Rotation is driven from JS by HintDieSpinner in app.js (rAF-based).
       A CSS `animation: rotateX/Y/Z … infinite` causes Chrome to compute a
       maximal dirty rect across the whole keyframe range — a 3D-projected
       volume that leaks far outside the element's box and triggers the
       viewport-wide flash. rAF only changes the transform once per frame, so
       Chrome's dirty rect for each repaint is just .dice's current 2D screen
       area. Same approach as backgammon's startDiceAnimation. */
}
.dice-face {
    position: absolute;
    inset: 0;
    background: #fefefe;
    border-radius: 0;
    box-shadow: inset 0 0 0 1px rgba(0,0,0,0.08);
    display: grid;
    grid-template: repeat(3, 1fr) / repeat(3, 1fr);
    padding: 7px;
    gap: 2px;
    backface-visibility: hidden;
}
.dice-face .dot {
    width: 9px;
    height: 9px;
    background: radial-gradient(circle at 30% 30%, #4a4a4a, #111);
    border-radius: 50%;
    align-self: center;
    justify-self: center;
}
/* Position dots per face */
.face-1 .dot:nth-child(1) { grid-area: 2 / 2; }

.face-2 .dot:nth-child(1) { grid-area: 1 / 1; }
.face-2 .dot:nth-child(2) { grid-area: 3 / 3; }

.face-3 .dot:nth-child(1) { grid-area: 1 / 1; }
.face-3 .dot:nth-child(2) { grid-area: 2 / 2; }
.face-3 .dot:nth-child(3) { grid-area: 3 / 3; }

.face-4 .dot:nth-child(1) { grid-area: 1 / 1; }
.face-4 .dot:nth-child(2) { grid-area: 1 / 3; }
.face-4 .dot:nth-child(3) { grid-area: 3 / 1; }
.face-4 .dot:nth-child(4) { grid-area: 3 / 3; }

.face-5 .dot:nth-child(1) { grid-area: 1 / 1; }
.face-5 .dot:nth-child(2) { grid-area: 1 / 3; }
.face-5 .dot:nth-child(3) { grid-area: 2 / 2; }
.face-5 .dot:nth-child(4) { grid-area: 3 / 1; }
.face-5 .dot:nth-child(5) { grid-area: 3 / 3; }

.face-6 .dot:nth-child(1) { grid-area: 1 / 1; }
.face-6 .dot:nth-child(2) { grid-area: 1 / 3; }
.face-6 .dot:nth-child(3) { grid-area: 2 / 1; }
.face-6 .dot:nth-child(4) { grid-area: 2 / 3; }
.face-6 .dot:nth-child(5) { grid-area: 3 / 1; }
.face-6 .dot:nth-child(6) { grid-area: 3 / 3; }

/* Place the six faces in 3D space */
.face-1 { transform: translateZ(calc(var(--size) / 2)); }
.face-2 { transform: rotateY(180deg) translateZ(calc(var(--size) / 2)); }
.face-3 { transform: rotateY(90deg)  translateZ(calc(var(--size) / 2)); }
.face-4 { transform: rotateY(-90deg) translateZ(calc(var(--size) / 2)); }
.face-5 { transform: rotateX(90deg)  translateZ(calc(var(--size) / 2)); }
.face-6 { transform: rotateX(-90deg) translateZ(calc(var(--size) / 2)); }

/* ── Mobile sizing (matches lobby's 800px breakpoint) ────────────── */
@media (max-width: 800px) {
    .avatar-ring   { width: 80px;  height: 80px; }
    /* Both 20px in; the muted label sits flush under the (smaller) timer + matches its size. */
    .call-timer    { top: 58px; right: 20px; font-size: 0.65rem; }
    .muted-label   { top: 73px; right: 20px; font-size: 0.65rem; }
}
