/* =====================================================================
 * bay-grid-v2 — opt-in visual polish for the bay grid. All rules are
 * scoped under the `.bay-grid-v2` wrapper class so this file is inert
 * until that class is applied. To revert the entire pass: remove the
 * `bay-grid-v2` class from the container in Pages/Bays.razor. The file
 * can stay in place. 2026-06-17.
 *
 * Targets the existing `.bay-card-container` element (which carries the
 * `data-bay-state` and `data-bay-type` attributes) but applies the
 * visual effects to the INNER `.card` element so the halo, equipment
 * chip, and pulse all sit on the square icon tile — NOT around the bay
 * number label that lives below the tile inside the same container.
 *
 * Visual additions:
 *  - Soft ambient halo per state (on .card, not the outer container)
 *  - Breathing animation on in-use tiles
 *  - Equipment-type micro-chip in the top-right corner OF THE TILE
 *  - Brief "pulse" highlight when the state attribute changes (added
 *    by wwwroot/js/bayGridV2.js via MutationObserver — class lives on
 *    the outer container, CSS reaches the inner .card)
 * ===================================================================== */

/* Make the .card relative + add a transition so the halo + chip have
   an anchor and animations feel smooth. The card already has
   position:relative inline (BayCard.razor:102) but harmless to reassert. */
.bay-grid-v2 .bay-card-container .card {
    position: relative !important;
    border-radius: 8px;
    transition: transform 220ms ease, box-shadow 220ms ease;
}

/* Ambient halos by state. ALL colors are now sourced from the
   canonical bay-color map defaults defined in AdminDashboard.razor:860-877,
   so the halo always matches the tile background the operator sees
   (and the colors they can edit on the Bay Colors settings page).
   2026-06-18. */

/* Available — #198754 (Bootstrap green) */
.bay-grid-v2 .bay-card-container[data-bay-state="2"] .card {
    box-shadow: 0 0 0 1px rgba(25, 135, 84, 0.55), 0 0 12px rgba(25, 135, 84, 0.40);
}
/* In Use / BallRental — #0d6efd (Bootstrap blue) */
.bay-grid-v2 .bay-card-container[data-bay-state="1"] .card {
    animation: bayGridV2_breathe_active 3.4s ease-in-out infinite;
}
/* Time Rental — #6f42c1 (Bootstrap purple) */
.bay-grid-v2 .bay-card-container[data-bay-state="4"] .card {
    animation: bayGridV2_breathe_time 3.4s ease-in-out infinite;
}
/* FreePlay — color sourced from BayCard's BuildBayColorVars() so it
   matches the venue's own BayColors["Freeplay"] (purple at Ras, teal
   elsewhere by default). Falls back to Bootstrap purple if the var
   isn't set on the container. color-mix lets us still apply a soft
   halo alpha without baking the color into the rgba(). 2026-06-18. */
.bay-grid-v2 .bay-card-container[data-bay-state="3"] .card {
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--bg-freeplay, #6f42c1) 55%, transparent),
                0 0 12px color-mix(in srgb, var(--bg-freeplay, #6f42c1) 40%, transparent);
}
/* Checked In — #ffc107 (amber). ModeType 5. */
.bay-grid-v2 .bay-card-container[data-bay-state="5"] .card {
    box-shadow: 0 0 0 1px rgba(255, 193, 7, 0.65), 0 0 12px rgba(255, 193, 7, 0.45);
}
/* Checked Out — #fd7e14 (orange). ModeType 6. */
.bay-grid-v2 .bay-card-container[data-bay-state="6"] .card {
    box-shadow: 0 0 0 1px rgba(253, 126, 20, 0.65), 0 0 12px rgba(253, 126, 20, 0.45);
}
/* Error / warning pulses — split by severity:
     RED   = hard error: ModeType==7, OR any ErrorId != 0 except the
             low-balls-only warning (ErrorId=6). Codes 7 & 8 are
             "LowBalls + sensor fault" — the sensor fault wins so they
             pulse red. Codes 1-5, 9-12 are sensor / motor faults.
     YELLOW = warning only: ErrorId==6 (LowBalls).
   Both combine a scale throb with a strong glow so operators catch
   them from across the room. 2026-06-17. */

/* RED — hard error. Suppressed when the bay is offline (state 9/10),
   because ErrorId is a last-known cached value and shouldn't pulse
   while we know we can't trust it. 2026-06-18. */
.bay-grid-v2 .bay-card-container[data-bay-state="7"] .card,
.bay-grid-v2 .bay-card-container[data-bay-error]:not([data-bay-error="0"]):not([data-bay-error=""]):not([data-bay-error="6"]):not([data-bay-state="9"]):not([data-bay-state="10"]) .card {
    animation: bayGridV2_error_pulse 1.2s ease-in-out infinite;
    transform-origin: center;
    z-index: 6;
}
@keyframes bayGridV2_error_pulse {
    0%, 100% {
        transform: scale(1);
        box-shadow: 0 0 0 1px rgba(220, 53, 69, 0.55),
                    0 0 10px rgba(220, 53, 69, 0.35);
    }
    50% {
        transform: scale(1.05);
        box-shadow: 0 0 0 3px rgba(220, 53, 69, 1),
                    0 0 18px rgba(220, 53, 69, 0.80),
                    0 0 36px rgba(220, 53, 69, 0.45);
    }
}

/* YELLOW — low-balls warning (ErrorId==6). Suppressed when offline —
   the cached ErrorId from before Spectrum went down shouldn't keep
   pulsing on a tile we can't actually see. 2026-06-18. */
.bay-grid-v2 .bay-card-container[data-bay-error="6"]:not([data-bay-state="9"]):not([data-bay-state="10"]) .card {
    animation: bayGridV2_warning_pulse 1.6s ease-in-out infinite;
    transform-origin: center;
    z-index: 6;
}
/* LowBalls warning yellow — #ffc107 (Bootstrap warning, same hex as
   CheckedIn in the bay-color map). 2026-06-18. */
@keyframes bayGridV2_warning_pulse {
    0%, 100% {
        transform: scale(1);
        box-shadow: 0 0 0 1px rgba(255, 193, 7, 0.65),
                    0 0 10px rgba(255, 193, 7, 0.40);
    }
    50% {
        transform: scale(1.04);
        box-shadow: 0 0 0 3px rgba(255, 193, 7, 1),
                    0 0 18px rgba(255, 193, 7, 0.85),
                    0 0 32px rgba(255, 193, 7, 0.45);
    }
}

.bay-grid-v2 .bay-card-container[data-bay-state="9"] .card,
.bay-grid-v2 .bay-card-container[data-bay-state="10"] .card {
    /* Offline / NoResponse — NotFound #adb5bd, desaturated, no glow. */
    filter: saturate(0.55) brightness(0.92);
    box-shadow: 0 0 0 1px rgba(173, 181, 189, 0.55);
}

/* BallRental — #0d6efd (Bootstrap blue) */
@keyframes bayGridV2_breathe_active {
    0%, 100% { box-shadow: 0 0 0 1px rgba(13, 110, 253, 0.55), 0 0 10px rgba(13, 110, 253, 0.30); }
    50%      { box-shadow: 0 0 0 2px rgba(13, 110, 253, 0.85), 0 0 22px rgba(13, 110, 253, 0.55); }
}
/* TimeRental — #6f42c1 (Bootstrap purple) */
@keyframes bayGridV2_breathe_time {
    0%, 100% { box-shadow: 0 0 0 1px rgba(111, 66, 193, 0.55), 0 0 10px rgba(111, 66, 193, 0.30); }
    50%      { box-shadow: 0 0 0 2px rgba(111, 66, 193, 0.85), 0 0 22px rgba(111, 66, 193, 0.55); }
}
@keyframes bayGridV2_breathe_error {
    0%, 100% { box-shadow: 0 0 0 1px rgba(220, 53, 69, 0.65), 0 0 12px rgba(220, 53, 69, 0.40); }
    50%      { box-shadow: 0 0 0 2px rgba(220, 53, 69, 0.95), 0 0 26px rgba(220, 53, 69, 0.75); }
}

/* Equipment-type chip removed 2026-06-17 per user — the halos +
   pulse already convey enough state; the chip added visual noise. The
   data-bay-type mirror in bayGridV2.js is now unused but harmless and
   left in place so re-enabling the chip is a CSS-only change. */

/* Pulse class — added by bayGridV2.js to the outer container for ~1.6s
   whenever data-bay-state changes. CSS reaches down to the inner .card
   so the pulse animates the tile, not the container. */
.bay-grid-v2 .bay-card-container.bay-pulse .card {
    animation: bayGridV2_pulse 1.6s ease-out 1;
}
@keyframes bayGridV2_pulse {
    0%   { transform: scale(1);    box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.85); }
    25%  { transform: scale(1.06); box-shadow: 0 0 0 6px rgba(255, 255, 255, 0.55); }
    100% { transform: scale(1);    box-shadow: 0 0 0 12px rgba(255, 255, 255, 0); }
}

/* =====================================================================
 * Hover halos — when an operator hovers any bay tile, a state-colored
 * glow lights up around it. The error / low-balls pulses are left
 * running (those are urgent — never silence them); for the other
 * states the breathing animation is paused while hovered so the static
 * halo + slight lift read cleanly. 2026-06-17.
 * ===================================================================== */
.bay-grid-v2 .bay-card-container:hover .card {
    transform: translateY(-2px);
    z-index: 10;
}

/* Universal state hover halo — reads --bg-current, the resolved icon
   color BayCard.BuildBayColorVars() computed from GetBackground(). That
   handles integration overrides (TopTracer, InRange, Trackman, YGB,
   Trainer) which otherwise wouldn't get the right glow because the
   data-bay-state attribute still reads as FreePlay (3) or InUse (1)
   even though the body color is e.g. TopTracer cyan. Single rule wins
   over the per-state ones it replaces. Fallback is the InUse blue when
   the var hasn't propagated yet (rare race on first paint). 2026-06-18. */
.bay-grid-v2 .bay-card-container:hover .card {
    box-shadow: 0 0 0 3px var(--bg-current, #0d6efd),
                0 0 22px color-mix(in srgb, var(--bg-current, #0d6efd) 75%, transparent),
                0 0 44px color-mix(in srgb, var(--bg-current, #0d6efd) 40%, transparent);
}

/* Fully stop the breathing keyframe on hover for InUse + TimeRental
   (not just pause). animation-play-state: paused freezes the keyframe
   on its last computed box-shadow, which still wins over the hover
   rule's box-shadow → user saw no halo on hovered InUse bays.
   animation: none releases the box-shadow back to the hover rule.
   2026-06-18. */
.bay-grid-v2 .bay-card-container[data-bay-state="1"]:hover .card,
.bay-grid-v2 .bay-card-container[data-bay-state="4"]:hover .card {
    animation: none !important;
}

/* Offline — soft grey halo, lift only (no urgent color). */
.bay-grid-v2 .bay-card-container[data-bay-state="9"]:hover .card,
.bay-grid-v2 .bay-card-container[data-bay-state="10"]:hover .card {
    box-shadow: 0 0 0 2px rgba(156, 163, 175, 0.8),
                0 0 18px rgba(0, 0, 0, 0.30);
}

/* Shutdown (state=0) — #212529 halo matching BayColors["Shutdown"]
   from the bay-color map. 2026-06-18. */
.bay-grid-v2 .bay-card-container[data-bay-state="0"]:hover .card {
    box-shadow: 0 0 0 3px rgba(33, 37, 41, 1),
                0 0 22px rgba(33, 37, 41, 0.70),
                0 0 44px rgba(33, 37, 41, 0.35);
}

/* Error — red halo. Pause the pulse on hover so the static halo reads
   cleanly; resumes on mouse-leave. Same color family as the pulse so
   the transition feels continuous. 2026-06-17. */
.bay-grid-v2 .bay-card-container[data-bay-state="7"]:hover .card,
.bay-grid-v2 .bay-card-container[data-bay-error]:not([data-bay-error="0"]):not([data-bay-error=""]):not([data-bay-error="6"]):hover .card {
    animation-play-state: paused;
    box-shadow: 0 0 0 3px rgba(220, 53, 69, 1),
                0 0 22px rgba(220, 53, 69, 0.80),
                0 0 44px rgba(220, 53, 69, 0.45);
}

/* LowBalls warning — yellow halo. */
.bay-grid-v2 .bay-card-container[data-bay-error="6"]:hover .card {
    animation-play-state: paused;
    box-shadow: 0 0 0 3px rgba(245, 158, 11, 1),
                0 0 22px rgba(245, 158, 11, 0.80),
                0 0 44px rgba(245, 158, 11, 0.45);
}

/* ---------------------------------------------------------------------
   Venue-offline override — when Spectrum at the venue has gone dark,
   IsBayOffline() returns true on the C# side and BayCard stamps
   data-bay-offline="true" on the container. The bay's ModeType /
   ErrorId are last-known stale values (could be 3+6 = FreePlay with
   LowBalls), so they'd otherwise keep pulsing yellow / red / etc.
   This rule kills every halo, pulse, transform, and hover effect for
   those tiles and replaces the visual with the desaturated grey of an
   offline tile — same treatment as bay-level state 9/10. Wins via
   higher specificity (two attributes) + !important on the animation.
   2026-06-18.
   --------------------------------------------------------------------- */
.bay-grid-v2 .bay-card-container[data-bay-offline="true"] .card {
    animation: none !important;
    box-shadow: 0 0 0 1px rgba(173, 181, 189, 0.55) !important;
    filter: saturate(0.55) brightness(0.92);
    transform: none !important;
}
.bay-grid-v2 .bay-card-container[data-bay-offline="true"]:hover .card {
    transform: translateY(-2px) !important;
    box-shadow: 0 0 0 2px rgba(156, 163, 175, 0.8),
                0 0 18px rgba(0, 0, 0, 0.30) !important;
    animation: none !important;
}

/* Reduce motion accessibility — respect the OS-level preference. */
@media (prefers-reduced-motion: reduce) {
    .bay-grid-v2 .bay-card-container .card,
    .bay-grid-v2 .bay-card-container.bay-pulse .card,
    .bay-grid-v2 .bay-card-container[data-bay-state="1"] .card,
    .bay-grid-v2 .bay-card-container[data-bay-state="3"] .card,
    .bay-grid-v2 .bay-card-container[data-bay-state="4"] .card,
    .bay-grid-v2 .bay-card-container[data-bay-state="7"] .card,
    .bay-grid-v2 .bay-card-container[data-bay-error]:not([data-bay-error="0"]) .card,
    .bay-grid-v2 .bay-card-container[data-bay-error="6"] .card {
        animation: none !important;
        transition: none !important;
    }
}
