/* ==========================================================================
   chataiopensource / Vibgyor polish layer
   --------------------------------------------------------------------------
   Loaded AFTER assets/style.css, so everything here wins on equal specificity.
   It is purely additive: no existing selector is redefined, only extended.

   Contents
     1. Registered custom properties (needed to animate conic gradients)
     2. Spectral hue tokens (violet -> indigo -> blue -> green -> yellow ->
        orange -> red), light and dark
     3. Motion tokens (easing + duration)
     4. Glass tokens (translucent surfaces + backdrop blur)
     5. Aurora backdrop
     6. Base: smooth scrolling, selection, focus rings
     7. Glass surfaces
     8. Spectrum ring treatment for cards
     9. Buttons, brand mark, nav
    10. Battle: panels, vote reveal flourish, winner glow
    11. Toasts, dialogs, tables
    12. Motion preferences, transparency, print
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Registered properties
   A registered <angle> is interpolatable, which is what makes a conic gradient
   spin smoothly instead of snapping. Browsers without @property simply ignore
   the animation and keep the static 0deg value from initial-value.
   -------------------------------------------------------------------------- */
@property --ring-angle {
    syntax: '<angle>';
    initial-value: 0deg;
    inherits: false;
}

@property --hue-shift {
    syntax: '<angle>';
    initial-value: 0deg;
    inherits: false;
}

/* --------------------------------------------------------------------------
   2. Spectral hues
   The orbs, rings and sweeps below all read from these, so the whole palette
   can be retuned from one block.
   -------------------------------------------------------------------------- */
:root {
    --sp-violet: #8b5cf6;
    --sp-indigo: #5b5bd6;
    --sp-blue: #3b82f6;
    --sp-green: #10b981;
    --sp-yellow: #f5c518;
    --sp-orange: #f97316;
    --sp-red: #ef4444;

    /* Full spectrum, in ROYGBIV-adjacent order. */
    --sp-spectrum: linear-gradient(
        90deg,
        var(--sp-violet),
        var(--sp-indigo),
        var(--sp-blue),
        var(--sp-green),
        var(--sp-yellow),
        var(--sp-orange),
        var(--sp-red)
    );

    /* The same wheel as a conic, for borders and the brand mark. */
    --sp-wheel: conic-gradient(
        from var(--ring-angle),
        var(--sp-violet),
        var(--sp-indigo),
        var(--sp-blue),
        var(--sp-green),
        var(--sp-yellow),
        var(--sp-orange),
        var(--sp-red),
        var(--sp-violet)
    );

    /* Ring strength. Kept low: a 1px spectral edge should read as a sheen, not
       as a rainbow outline competing with the content. */
    --sp-ring-alpha: 0.55;
    --sp-ring-width: 1px;
}

/* On dark backgrounds every hue needs more luminance to stay visible, and the
   orbs can carry more saturation because there is no white to wash out. */
:root[data-theme='dark'] {
    --sp-violet: #a78bfa;
    --sp-indigo: #818cf8;
    --sp-blue: #60a5fa;
    --sp-green: #34d399;
    --sp-yellow: #fcd34d;
    --sp-orange: #fb923c;
    --sp-red: #f87171;

    --sp-ring-alpha: 0.75;
}

/* --------------------------------------------------------------------------
   3. Motion tokens
   -------------------------------------------------------------------------- */
:root {
    --ease-out: cubic-bezier(0.22, 0.61, 0.36, 1);
    --ease-in-out: cubic-bezier(0.65, 0.05, 0.36, 1);
    --ease-smooth: cubic-bezier(0.4, 0, 0.2, 1);
    /* Slight overshoot, for things that appear (chips, toasts, buttons). */
    --ease-spring: cubic-bezier(0.34, 1.46, 0.64, 1);

    --dur-1: 140ms;
    --dur-2: 220ms;
    --dur-3: 340ms;
    --dur-4: 560ms;
    --dur-5: 900ms;
}

/* --------------------------------------------------------------------------
   4. Glass tokens
   -------------------------------------------------------------------------- */
:root {
    --glass-blur: 16px;
    --glass-saturate: 1.6;
    --glass-bg: rgba(255, 255, 255, 0.72);
    --glass-bg-strong: rgba(255, 255, 255, 0.85);
    --glass-border: rgba(255, 255, 255, 0.6);
    /* Tint borrowed from the spectrum so the frost is not dead neutral. */
    --glass-tint: rgba(139, 92, 246, 0.06);
}

:root[data-theme='dark'] {
    --glass-blur: 20px;
    --glass-saturate: 1.4;
    --glass-bg: rgba(24, 24, 22, 0.66);
    --glass-bg-strong: rgba(24, 24, 22, 0.82);
    --glass-border: rgba(255, 255, 255, 0.09);
    --glass-tint: rgba(167, 139, 250, 0.08);
}

/* --------------------------------------------------------------------------
   5. Aurora backdrop
   Three soft hue fields drifting behind the whole app. The softness comes from
   the gradients themselves rather than filter: blur(), which would force a
   full-viewport repaint every frame; animating only transform keeps the whole
   thing on the compositor.
   Markup is <div class="aurora"> in includes/shell.php, aria-hidden.
   -------------------------------------------------------------------------- */
.aurora {
    position: fixed;
    z-index: 0;
    inset: 0;
    overflow: hidden;
    pointer-events: none;
    /* The orbs are decoration; they must never intercept a click. */
    contain: strict;
}

.aurora-orb {
    position: absolute;
    top: 0;
    left: 0;
    display: block;
    width: 46vmax;
    height: 46vmax;
    border-radius: 50%;
    opacity: 0.5;
    will-change: transform;
}

:root[data-theme='dark'] .aurora-orb {
    opacity: 0.34;
}

/* violet + indigo, upper left */
.aurora-orb:nth-child(1) {
    margin: -26vmax 0 0 -18vmax;
    background: radial-gradient(
        circle at 50% 50%,
        color-mix(in srgb, var(--sp-violet) 55%, transparent) 0%,
        color-mix(in srgb, var(--sp-indigo) 26%, transparent) 42%,
        transparent 68%
    );
}

/* blue + green, lower right */
.aurora-orb:nth-child(2) {
    margin: 22vmax 0 0 44vmax;
    background: radial-gradient(
        circle at 50% 50%,
        color-mix(in srgb, var(--sp-blue) 48%, transparent) 0%,
        color-mix(in srgb, var(--sp-green) 22%, transparent) 44%,
        transparent 70%
    );
}

/* orange + red + yellow, centre, smallest and faintest */
.aurora-orb:nth-child(3) {
    width: 38vmax;
    height: 38vmax;
    margin: 26vmax 0 0 30vmax;
    opacity: 0.3;
    background: radial-gradient(
        circle at 50% 50%,
        color-mix(in srgb, var(--sp-orange) 46%, transparent) 0%,
        color-mix(in srgb, var(--sp-red) 20%, transparent) 44%,
        transparent 70%
    );
}

:root[data-theme='dark'] .aurora-orb:nth-child(3) {
    opacity: 0.22;
}

/* The drift is opt-in. Keeping the declaration inside no-preference means a
   visitor who has not asked for reduced motion is the only one who gets it,
   rather than every visitor having it taken away again afterwards.

   The reduce block at the end of this file also cancels the drift, which
   stays as a backstop for anything that overrides the media query. */
@media (prefers-reduced-motion: no-preference) {
    .aurora-orb:nth-child(1) {
        animation: aurora-drift-a 38s var(--ease-in-out) infinite alternate;
    }

    .aurora-orb:nth-child(2) {
        animation: aurora-drift-b 52s var(--ease-in-out) infinite alternate;
    }

    .aurora-orb:nth-child(3) {
        animation: aurora-drift-c 44s var(--ease-in-out) infinite alternate;
    }
}

@keyframes aurora-drift-a {
    0% {
        transform: translate3d(0, 0, 0) scale(1);
    }
    100% {
        transform: translate3d(14vw, 9vh, 0) scale(1.22);
    }
}

@keyframes aurora-drift-b {
    0% {
        transform: translate3d(0, 0, 0) scale(1.1);
    }
    100% {
        transform: translate3d(-12vw, -8vh, 0) scale(0.9);
    }
}

@keyframes aurora-drift-c {
    0% {
        transform: translate3d(0, 0, 0) scale(0.95);
    }
    100% {
        transform: translate3d(-9vw, 11vh, 0) scale(1.28);
    }
}

/* Content has to sit above the aurora for the glass to have anything to blur. */
.app-shell {
    position: relative;
    z-index: 1;
}

/* --------------------------------------------------------------------------
   6. Base: scrolling, selection, focus
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: no-preference) {
    html {
        scroll-behavior: smooth;
    }
}

::selection {
    background: color-mix(in srgb, var(--sp-violet) 26%, transparent);
    color: var(--text);
}

:root[data-theme='dark'] ::selection {
    background: color-mix(in srgb, var(--sp-violet) 42%, transparent);
    color: var(--text);
}

/* A single accessible focus treatment everywhere. The ring is the spectrum at
   low alpha plus a solid accent core, so it stays visible on any surface and
   still meets contrast requirements against the page. */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
    outline: 2px solid color-mix(in srgb, var(--sp-indigo) 78%, transparent);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
    transition: outline-color var(--dur-2) var(--ease-out),
        box-shadow var(--dur-2) var(--ease-out);
    box-shadow: 0 0 0 4px color-mix(in srgb, var(--sp-blue) 16%, transparent);
}

/* --------------------------------------------------------------------------
   7. Glass surfaces
   Only for surfaces that actually have page content passing underneath them.
   opacity 0.66-0.72 keeps text contrast on top essentially unchanged.
   -------------------------------------------------------------------------- */
.sidebar,
.mobile-header,
.site-header,
.toast,
.search-dialog-shell,
.style-panel,
.response-panel-header,
.battle-toolbar,
.leaderboard-toolbar,
.model-actions,
.composer-topline {
    background: var(--glass-bg);
    border-color: var(--glass-border);
    -webkit-backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
    backdrop-filter: blur(var(--glass-blur)) saturate(var(--glass-saturate));
    box-shadow: var(--shadow-sm), inset 0 1px 0 var(--glass-tint);
}

/* The sidebar is tall and fixed; it needs a touch more opacity or long lists of
   secondary text start to lose contrast against the aurora. */
.sidebar {
    background: var(--glass-bg-strong);
}

/* No backdrop-filter support: fall back to an opaque surface rather than
   leaving the aurora showing through bare text. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
    .sidebar,
    .mobile-header,
    .site-header,
    .toast,
    .search-dialog-shell,
    .style-panel,
    .response-panel-header,
    .battle-toolbar,
    .leaderboard-toolbar,
    .model-actions,
    .composer-topline {
        background: var(--surface);
    }

    .sidebar {
        background: var(--sidebar);
    }
}

/* --------------------------------------------------------------------------
   8. Spectrum rings
   A masked conic gradient gives a real gradient border (something a plain
   border-color cannot do) without adding a single layout box. Static by
   default; it rotates only while the card is hovered, so at most one ring per
   screen is animating.
   -------------------------------------------------------------------------- */
:is(.suggestion-card, .stat-card, .info-card, .model-card, .leaderboard-card, .setup-card, .post-card, .admin-card) {
    position: relative;
    transition: transform var(--dur-3) var(--ease-out),
        box-shadow var(--dur-3) var(--ease-out),
        border-color var(--dur-3) var(--ease-out);
}

:is(.suggestion-card, .stat-card, .info-card, .model-card, .leaderboard-card, .setup-card, .post-card, .admin-card)::after {
    content: '';
    position: absolute;
    z-index: 1;
    inset: 0;
    padding: var(--sp-ring-width);
    border-radius: inherit;
    background: var(--sp-wheel);
    opacity: 0;
    pointer-events: none;
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    mask-composite: exclude;
    transition: opacity var(--dur-3) var(--ease-out);
}

:is(.suggestion-card, .stat-card, .info-card, .model-card, .leaderboard-card, .setup-card, .post-card, .admin-card):hover {
    transform: translateY(-2px);
    box-shadow: var(--shadow-md),
        0 0 0 1px color-mix(in srgb, var(--sp-indigo) 10%, transparent),
        0 12px 40px color-mix(in srgb, var(--sp-violet) 12%, transparent);
}

:is(.suggestion-card, .stat-card, .info-card, .model-card, .leaderboard-card, .setup-card, .post-card, .admin-card):hover::after {
    opacity: var(--sp-ring-alpha);
}

@media (prefers-reduced-motion: no-preference) {
    :is(.suggestion-card, .stat-card, .info-card, .model-card, .leaderboard-card, .setup-card, .post-card, .admin-card):hover {
        animation: ring-spin 6s linear infinite;
    }
}

@keyframes ring-spin {
    from {
        --ring-angle: 0deg;
    }
    to {
        --ring-angle: 360deg;
    }
}

/* --------------------------------------------------------------------------
   9. Buttons, brand, nav
   -------------------------------------------------------------------------- */
.button,
.send-button,
.retry-button,
.attach-button,
.swap-button {
    transition: transform var(--dur-2) var(--ease-spring),
        box-shadow var(--dur-2) var(--ease-out),
        background-color var(--dur-2) var(--ease-out),
        border-color var(--dur-2) var(--ease-out),
        color var(--dur-2) var(--ease-out);
}

.button:not(:disabled):hover,
.send-button:not(:disabled):hover,
.retry-button:not(:disabled):hover,
.attach-button:not(:disabled):hover,
.swap-button:not(:disabled):hover {
    transform: translateY(-1px);
}

.button:not(:disabled):active,
.send-button:not(:disabled):active,
.retry-button:not(:disabled):active,
.attach-button:not(:disabled):active,
.swap-button:not(:disabled):active {
    transform: translateY(0) scale(0.985);
    transition-duration: var(--dur-1);
}

/* The primary action carries the spectrum. White text sits on the mid hues
   (indigo/blue) which is where the wheel is darkest, so contrast holds. */
.button-primary,
.send-button {
    position: relative;
    background-image: var(--sp-spectrum);
    background-size: 260% 100%;
    background-position: 12% 0;
    border-color: transparent;
    color: #fff;
    font-weight: 600;
    box-shadow: 0 4px 16px color-mix(in srgb, var(--sp-indigo) 26%, transparent);
    transition: background-position var(--dur-4) var(--ease-smooth),
        transform var(--dur-2) var(--ease-spring),
        box-shadow var(--dur-2) var(--ease-out);
}

@media (prefers-reduced-motion: no-preference) {
    .button-primary:not(:disabled):hover,
    .send-button:not(:disabled):hover {
        background-position: 88% 0;
        box-shadow: 0 8px 28px color-mix(in srgb, var(--sp-violet) 34%, transparent);
    }
}

/* Without motion the button must still look intentional. */
@media (prefers-reduced-motion: reduce) {
    .button-primary:not(:disabled):hover,
    .send-button:not(:disabled):hover {
        background-position: 50% 0;
    }
}

.brand-mark {
    position: relative;
    background-image: var(--sp-spectrum);
    background-size: 220% 100%;
    background-position: 0% 0;
    color: #fff;
    box-shadow: 0 3px 12px color-mix(in srgb, var(--sp-indigo) 30%, transparent);
    transition: background-position var(--dur-5) var(--ease-smooth),
        box-shadow var(--dur-3) var(--ease-out),
        transform var(--dur-2) var(--ease-spring);
}

@media (prefers-reduced-motion: no-preference) {
    .brand:hover .brand-mark,
    a:hover > .brand-mark,
    .mobile-brand:hover .brand-mark {
        background-position: 100% 0;
        transform: rotate(-6deg) scale(1.06);
    }
}

.brand-name {
    background: linear-gradient(
        96deg,
        var(--sp-violet),
        var(--sp-indigo),
        var(--sp-blue),
        var(--sp-green),
        var(--sp-orange)
    );
    background-size: 220% 100%;
    -webkit-background-clip: text;
    background-clip: text;
    -webkit-text-fill-color: transparent;
    color: var(--text);
    transition: background-position var(--dur-5) var(--ease-smooth);
}

@media (prefers-reduced-motion: no-preference) {
    .brand:hover .brand-name {
        background-position: 100% 0;
    }
}

.sidebar-link,
.nav-links a,
.header-nav a {
    position: relative;
    transition: color var(--dur-2) var(--ease-out),
        background-color var(--dur-2) var(--ease-out),
        transform var(--dur-2) var(--ease-out);
}

.sidebar-link:hover,
.nav-links a:hover,
.header-nav a:hover {
    transform: translateX(2px);
}

/* An underline that grows from the left, tinted by the spectrum. */
.sidebar-link::after,
.nav-links a::after {
    content: '';
    position: absolute;
    right: 0;
    bottom: 2px;
    left: 0;
    height: 1.5px;
    border-radius: 2px;
    background-image: var(--sp-spectrum);
    background-size: 200% 100%;
    transform: scaleX(0);
    transform-origin: left center;
    opacity: 0;
    transition: transform var(--dur-3) var(--ease-out),
        opacity var(--dur-2) var(--ease-out);
}

.sidebar-link:hover::after,
.nav-links a:hover::after {
    transform: scaleX(1);
    opacity: 0.85;
}

.pill,
.category-pill,
.rating-chip,
.status-pill,
.page-tab,
.suggestion-icon {
    transition: transform var(--dur-2) var(--ease-spring),
        box-shadow var(--dur-2) var(--ease-out),
        background-color var(--dur-2) var(--ease-out),
        border-color var(--dur-2) var(--ease-out),
        color var(--dur-2) var(--ease-out);
}

/* --------------------------------------------------------------------------
   10. Battle workspace
   -------------------------------------------------------------------------- */
.response-panel,
.vote-panel,
.style-panel,
.prompt-composer,
.setup-card,
.conversation-setup {
    transition: box-shadow var(--dur-3) var(--ease-out),
        border-color var(--dur-3) var(--ease-out);
}

.vote-button {
    position: relative;
    overflow: hidden;
    transition: transform var(--dur-2) var(--ease-spring),
        box-shadow var(--dur-3) var(--ease-out),
        border-color var(--dur-3) var(--ease-out),
        background-color var(--dur-3) var(--ease-out);
}

/* A spectral sheen crosses the button as it comes under the cursor. */
.vote-button::after {
    content: '';
    position: absolute;
    z-index: 0;
    top: 0;
    bottom: 0;
    left: -60%;
    width: 45%;
    background: linear-gradient(
        100deg,
        transparent,
        color-mix(in srgb, var(--sp-violet) 22%, transparent),
        color-mix(in srgb, var(--sp-blue) 22%, transparent),
        transparent
    );
    transform: skewX(-18deg);
    opacity: 0;
    pointer-events: none;
    transition: left var(--dur-5) var(--ease-smooth), opacity var(--dur-2) var(--ease-out);
}

.vote-button > * {
    position: relative;
    z-index: 1;
}

.vote-button:not(:disabled):hover {
    transform: translateY(-2px);
    border-color: color-mix(in srgb, var(--sp-indigo) 42%, var(--border));
    box-shadow: 0 10px 30px color-mix(in srgb, var(--sp-violet) 16%, transparent);
}

.vote-button:not(:disabled):hover::after {
    left: 120%;
    opacity: 1;
}

.vote-button:not(:disabled):active {
    transform: translateY(0) scale(0.99);
}

.vote-button:disabled {
    cursor: not-allowed;
}

/* The moment a vote resolves is the one place a full spectrum sweep is worth
   the paint. It runs once, on the element that just appeared. */
.vote-result {
    position: relative;
    overflow: hidden;
    animation: result-reveal var(--dur-5) var(--ease-out) both;
}

.vote-result::before {
    content: '';
    position: absolute;
    z-index: 0;
    top: 0;
    bottom: 0;
    left: -100%;
    width: 100%;
    background: linear-gradient(
        100deg,
        transparent,
        color-mix(in srgb, var(--sp-violet) 16%, transparent),
        color-mix(in srgb, var(--sp-blue) 16%, transparent),
        color-mix(in srgb, var(--sp-green) 12%, transparent),
        transparent
    );
    pointer-events: none;
    animation: result-sweep 1400ms var(--ease-smooth) 120ms both;
}

.vote-result > * {
    position: relative;
    z-index: 1;
}

@keyframes result-reveal {
    from {
        opacity: 0;
        transform: translateY(10px) scale(0.985);
        filter: blur(4px);
    }
    to {
        opacity: 1;
        transform: none;
        filter: none;
    }
}

@keyframes result-sweep {
    from {
        left: -100%;
    }
    to {
        left: 100%;
    }
}

/* The winning model earns a soft spectrum halo. The class comes from
   assets/script.js applyVoteOutcome(): the winning .response-panel gets
   is-selected, the loser gets is-muted. */
.response-panel.is-selected {
    border-color: color-mix(in srgb, var(--sp-indigo) 45%, var(--border));
    box-shadow: 0 0 0 1px color-mix(in srgb, var(--sp-violet) 22%, transparent),
        0 14px 44px color-mix(in srgb, var(--sp-violet) 18%, transparent);
}

/* The loser recedes rather than turning grey, which would fight the existing
   rule in style.css that already handles the dimming. */
.response-panel.is-muted {
    filter: saturate(0.82);
    transition: filter var(--dur-4) var(--ease-smooth);
}

/* .spinner / .status-spinner are the only loading rings; .loading is a text
   style and has no border to recolour. The border colours are static, so they
   apply unconditionally; only the spin is opted into. */
.spinner,
.status-spinner {
    border-top-color: var(--sp-blue);
    border-right-color: var(--sp-violet);
    border-bottom-color: var(--sp-green);
}

@media (prefers-reduced-motion: no-preference) {
    .spinner,
    .status-spinner {
        animation: spin-hue 2.4s linear infinite;
    }
}

@keyframes spin-hue {
    to {
        transform: rotate(360deg);
    }
}

.bar-track,
.feedback-bar-track {
    overflow: hidden;
    background: color-mix(in srgb, var(--sp-indigo) 8%, var(--bg-subtle));
}

.bar-fill,
.category-bar-fill,
.feedback-bar-fill {
    position: relative;
    background-image: linear-gradient(
        90deg,
        var(--sp-indigo),
        var(--sp-blue),
        var(--sp-green)
    );
    background-size: 200% 100%;
    transition: width var(--dur-5) var(--ease-smooth);
}

@media (prefers-reduced-motion: no-preference) {
    .bar-fill,
    .category-bar-fill,
    .feedback-bar-fill {
        animation: bar-flow 7s linear infinite;
    }
}

@keyframes bar-flow {
    to {
        background-position: 200% 0;
    }
}

/* --------------------------------------------------------------------------
   11. Toasts, dialogs, tables
   -------------------------------------------------------------------------- */
.toast {
    border-radius: var(--radius-lg);
    animation: toast-arrive var(--dur-4) var(--ease-spring) both;
}

@keyframes toast-arrive {
    from {
        opacity: 0;
        transform: translateY(14px) scale(0.96);
        filter: blur(6px);
    }
    to {
        opacity: 1;
        transform: none;
        filter: none;
    }
}

.search-dialog-shell {
    border-radius: var(--radius-xl);
    box-shadow: var(--shadow-md), 0 0 0 1px color-mix(in srgb, var(--sp-violet) 14%, transparent);
}

.search-result:hover,
.history-item:hover,
.feedback-option:hover {
    background: color-mix(in srgb, var(--sp-indigo) 7%, var(--surface-hover));
    transition: background-color var(--dur-2) var(--ease-out);
}

.data-table tbody tr,
.admin-table tbody tr,
.table-wrapper tbody tr {
    transition: background-color var(--dur-2) var(--ease-out);
}

.data-table tbody tr:hover,
.admin-table tbody tr:hover {
    background: color-mix(in srgb, var(--sp-indigo) 5%, transparent);
}

.copy-button:hover,
.icon-button:hover {
    color: var(--sp-indigo);
}

/* --------------------------------------------------------------------------
   12. Preferences
   -------------------------------------------------------------------------- */

/* Reduced motion: the palette stays, the movement goes. Orbs are frozen at a
   pleasant offset rather than removed, so the page does not lose its depth. */
@media (prefers-reduced-motion: reduce) {
    .aurora-orb {
        animation: none !important;
        will-change: auto;
    }

    .aurora-orb:nth-child(1) {
        transform: translate3d(6vw, 4vh, 0) scale(1.1);
    }

    .aurora-orb:nth-child(2) {
        transform: translate3d(-5vw, -3vh, 0) scale(1);
    }

    .aurora-orb:nth-child(3) {
        transform: translate3d(4vw, 5vh, 0) scale(1.05);
    }

    .bar-fill,
    .category-bar-fill,
    .feedback-bar-fill,
    .status-spinner,
    .spinner {
        animation: none !important;
    }

    .vote-button::after,
    .vote-result::before {
        display: none;
    }

    .vote-result {
        animation: none;
    }

    :is(.suggestion-card, .stat-card, .info-card, .model-card, .leaderboard-card, .setup-card, .post-card, .admin-card):hover {
        animation: none;
        transform: none;
    }
}

/* Some setups strip transparency for legibility; drop the frost and the orbs. */
@media (prefers-reduced-transparency: reduce) {
    .aurora {
        display: none;
    }

    .sidebar,
    .mobile-header,
    .site-header,
    .toast,
    .search-dialog-shell,
    .style-panel,
    .response-panel-header,
    .battle-toolbar,
    .leaderboard-toolbar,
    .model-actions,
    .composer-topline {
        background: var(--surface);
        -webkit-backdrop-filter: none;
        backdrop-filter: none;
    }

    .sidebar {
        background: var(--sidebar);
    }
}

@media print {
    .aurora,
    .sidebar,
    .mobile-header,
    .site-header,
    .toast-region {
        display: none !important;
    }

    .app-shell {
        display: block;
    }

    .app-main {
        width: 100%;
        margin-left: 0;
    }
}
