/* =====================================================================
   Staff booking console (/admin/bookings): how its state changes arrive.
   Uses the tokens from ui_feel.css (loaded first) and adds nothing to the
   console's look: every colour below is an existing --ops-* token, already
   used for the same thing in admin_bookings.html / admin_ops_console.css.

   Rules: transform and opacity only (layout never moves), 120-240 ms, nothing
   loops, nothing delays a click, every transition is interruptible, and
   prefers-reduced-motion turns all of it off (bottom of file).
   Paired with static/js/admin_bookings_feel.js.
   ===================================================================== */

:root {
    --dur-arrive: 240ms;   /* a row or pane settling in */
}

/* ---- selected row: its fill and accent edge fade in ----------------------
   The fill moves from the row's own background (which cannot fade without
   animating a colour) to a layer under the content that fades by opacity.
   Same colours as .conv-row.selected in admin_bookings.html. */
.sidebar-list .conv-row {
    isolation: isolate;
}

.sidebar-list .conv-row::before {
    background: var(--ops-surface);
    bottom: 0;
    box-shadow: inset 2px 0 0 var(--ops-accent);
    content: '';
    left: -2px;   /* over the 2px left border, where the accent edge sits */
    opacity: 0;
    pointer-events: none;
    position: absolute;
    right: 0;
    top: 0;
    transition: opacity var(--dur-ui) var(--ease-ui);
    z-index: -1;
}

.sidebar-list .conv-row.selected {
    background: transparent;
    border-left-color: transparent;
}

.sidebar-list .conv-row.selected::before {
    opacity: 1;
}

/* Light theme: the selection reads as an accent wash (admin_bookings.html). */
#ops-app[data-theme="light"] .sidebar-list .conv-row::before {
    background: var(--ops-accent-soft);
}

#ops-app[data-theme="light"] .sidebar-list .conv-row.selected {
    background: transparent;
}

/* ---- a row the live stream just brought or changed ----------------------
   A soft accent wash that settles away; the row itself is already in place
   and clickable from the first frame. */
.sidebar-list .conv-row::after {
    background: var(--ops-accent);
    content: '';
    inset: 0;
    opacity: 0;
    pointer-events: none;
    position: absolute;
    z-index: -1;
}

.sidebar-list .conv-row.ffg-fresh::after {
    animation: ffg-console-settle var(--dur-arrive) var(--ease-out) both;
}

@keyframes ffg-console-settle {
    from { opacity: .16; }
    to { opacity: 0; }
}

/* ---- detail pane: a different booking swaps in --------------------------
   Fades up 8px; the pane keeps its box, so nothing below it jumps and the
   composer stays where the hand is. */
.booking-view.ffg-swap {
    animation: ffg-rise var(--dur-arrive) var(--ease-out) both;
}

/* ---- stage and Pulse badges cross-fade when their words change ---------- */
.stage-strip,
.stage-flag-row {
    position: relative;   /* the outgoing badge is laid over the incoming one */
}

.ffg-xfade-in {
    animation: ffg-fade var(--dur-ui) var(--ease-ui) both;
}

.ffg-xfade-out {
    animation: ffg-console-out var(--dur-ui) var(--ease-ui) both;
    box-sizing: border-box;
    margin: 0;
    pointer-events: none;
    position: absolute;
    white-space: nowrap;
}

@keyframes ffg-console-out {
    from { opacity: 1; }
    to { opacity: 0; }
}

/* ---- counts: digits share one width, so a count never jitters ----------- */
.cat-pill .fcount,
.conv-group .fcount,
#board-footer .count {
    font-variant-numeric: tabular-nums;
}

/* ---- buttons: press .97 and back ---------------------------------------- */
#ops-app .icon-btn,
#ops-app .tb-primary,
#ops-app .ops-toplink,
#ops-app .fpill,
#ops-app .cat-pill,
#ops-app .conv-details-btn,
#ops-app .send-btn,
#ops-app .tab,
#ops-app .seg button,
#ops-app .set-done,
#ops-app #detail-save,
#ops-app .bill-modal-card button,
#ops-app .ffg-sound-toggle {
    transition: transform var(--dur-press) var(--ease-ui);
}

#ops-app .icon-btn:active:not(:disabled),
#ops-app .tb-primary:active,
#ops-app .ops-toplink:active,
#ops-app .fpill:active,
#ops-app .cat-pill:active,
#ops-app .conv-details-btn:active:not(:disabled),
#ops-app .send-btn:active:not(:disabled),
#ops-app .tab:active,
#ops-app .seg button:active:not(:disabled),
#ops-app .set-done:active,
#ops-app #detail-save:active:not(:disabled),
#ops-app .bill-modal-card button:active:not(:disabled),
#ops-app .ffg-sound-toggle:active {
    transform: scale(.97);
}

/* ---- notices and inline messages arrive ---------------------------------
   .board-notice centres itself with an inline translateX(-50%), so its rise
   keeps that and lets the inline style take over once it lands. */
.board-notice {
    animation: ffg-console-notice var(--dur-arrive) var(--ease-out) backwards;
}

@keyframes ffg-console-notice {
    from { opacity: 0; transform: translate(-50%, 8px); }
    to { opacity: .97; transform: translate(-50%, 0); }
}

.composer-error:not(.hidden),
.sched-msg:not(.hidden),
.detail-saved:not(.hidden),
#detail-error:not(.hidden) {
    animation: ffg-rise-small var(--dur-ui) var(--ease-out) both;
}

/* ---- dialogs open: the backdrop fades, the card rises 8px --------------- */
.about-overlay.active,
.bill-modal {
    animation: ffg-fade var(--dur-ui) var(--ease-out) both;
}

.about-overlay.active .about-panel,
.bill-modal .bill-modal-card {
    animation: ffg-rise var(--dur-arrive) var(--ease-out) both;
}

.ops-app.settings-active .settings-pane {
    animation: ffg-fade var(--dur-ui) var(--ease-out) both;
}

/* ---- sound toggle: the console's icon button, in its colours ------------
   ui_feel.css gives the button only its layout; here it is one more topbar
   icon-btn (the box and colours of #theme-toggle beside it). */
.ops-topbar .ffg-sound-slot {
    display: inline-flex;
    flex-shrink: 0;
    height: var(--tb-h);   /* reserved before ui_feel.js mounts the button */
    width: var(--tb-h);
}

#ops-app .ffg-sound-toggle {
    background: none;
    border: none;
    border-radius: var(--radius-md);
    color: var(--ops-text-dim);
    height: var(--tb-h);
    width: var(--tb-h);
}

#ops-app .ffg-sound-toggle svg {
    height: 18px;
    width: 18px;
}

#ops-app .ffg-sound-toggle:hover {
    background: var(--ops-surface);
    color: var(--ops-text-dim);
}

#ops-app .ffg-sound-toggle[aria-pressed="true"] {
    color: var(--ops-accent);
}

#ops-app .ffg-sound-toggle:focus-visible {
    outline: 2px solid var(--ops-accent);
    outline-offset: 2px;
}

/* One-row topbar (80em and up): the extra button's width comes out of the
   search box, not the venue name, which would otherwise truncate first. */
@media (min-width: 80em) {
    .ops-topbar .topbar-right {
        flex-shrink: 8;
    }
}

/* ---- reduced motion: every state change is instant ---------------------- */
@media (prefers-reduced-motion: reduce) {
    .sidebar-list .conv-row::before,
    #ops-app .icon-btn,
    #ops-app .tb-primary,
    #ops-app .ops-toplink,
    #ops-app .fpill,
    #ops-app .cat-pill,
    #ops-app .conv-details-btn,
    #ops-app .send-btn,
    #ops-app .tab,
    #ops-app .seg button,
    #ops-app .set-done,
    #ops-app #detail-save,
    #ops-app .bill-modal-card button,
    #ops-app .ffg-sound-toggle {
        transition: none;
    }

    #ops-app .icon-btn:active:not(:disabled),
    #ops-app .tb-primary:active,
    #ops-app .ops-toplink:active,
    #ops-app .fpill:active,
    #ops-app .cat-pill:active,
    #ops-app .conv-details-btn:active:not(:disabled),
    #ops-app .send-btn:active:not(:disabled),
    #ops-app .tab:active,
    #ops-app .seg button:active:not(:disabled),
    #ops-app .set-done:active,
    #ops-app #detail-save:active:not(:disabled),
    #ops-app .bill-modal-card button:active:not(:disabled),
    #ops-app .ffg-sound-toggle:active {
        transform: none;
    }

    .sidebar-list .conv-row.ffg-fresh::after,
    .booking-view.ffg-swap,
    .ffg-xfade-in,
    .board-notice,
    .composer-error:not(.hidden),
    .sched-msg:not(.hidden),
    .detail-saved:not(.hidden),
    #detail-error:not(.hidden),
    .about-overlay.active,
    .bill-modal,
    .about-overlay.active .about-panel,
    .bill-modal .bill-modal-card,
    .ops-app.settings-active .settings-pane {
        animation: none;
    }

    .ffg-xfade-out {
        display: none;
    }
}

/* ---- "Reduce motion" chosen in Settings > My console ----------------------
   The console's own choice (class on <html>, set by admin_bookings_prefs.js),
   for whatever the OS says. One blanket rule rather than a second copy of the
   list above: this stylesheet loads on /admin/bookings only, so every
   transition and animation on the page goes instant. The JS hooks read the
   same class through FFG.reducedMotion() and never add their swap, settle or
   cross-fade classes. A pressed button still shrinks for as long as it is
   held, with no tween: a blanket transform: none on :active would also hit
   every ancestor of the pressed element (a centred modal would jump). */
html.ffg-reduce-motion *,
html.ffg-reduce-motion *::before,
html.ffg-reduce-motion *::after {
    transition: none !important;
    animation: none !important;
    scroll-behavior: auto !important;
}
