/* Shared motion and polish layer — loaded by the dashboard, leaderboard and admin.
 *
 * Two rules govern everything here:
 *
 * 1. Only `opacity`, `transform`, `box-shadow` and `filter` are animated, so nothing
 *    triggers layout and every effect stays on the compositor.
 * 2. Panels that contain `position: sticky` table headers get opacity-only entrances.
 *    A transform on an ancestor changes the containing block and makes sticky headers
 *    jitter, which is a bad trade for a bit of movement.
 *
 * Everything is switched off under prefers-reduced-motion at the bottom of the file.
 */

:root {
    --mp-ease: cubic-bezier(.22, .68, .36, 1);
    --mp-ease-out: cubic-bezier(.16, 1, .3, 1);
    --mp-fast: .16s;
    --mp-base: .26s;
}

/* ------------------------------------------------------------------ keyframes
 *
 * Every entrance animation runs with the default `animation-fill-mode: none`, and
 * the stagger is a hold built into the keyframes rather than `animation-delay`.
 *
 * That combination is deliberate. With `fill-mode: both` plus a delay, an element's
 * resting state is the hidden first keyframe, so anything that stops the animation
 * from running — a throttled background tab, an extension, a rendering quirk —
 * leaves the content permanently invisible. Here the resting state is the normal,
 * visible one: if an animation never runs, the page simply appears without motion.
 */
@keyframes mp-rise {
    from { opacity: 0; transform: translate3d(0, 12px, 0); }
    to   { opacity: 1; transform: none; }
}
/* Staggered variants: the leading percentage holds the element hidden, which buys a
   delay without making "hidden" the resting state. */
@keyframes mp-rise-2 { 0%, 16% { opacity: 0; transform: translate3d(0, 12px, 0); } 100% { opacity: 1; transform: none; } }
@keyframes mp-rise-3 { 0%, 28% { opacity: 0; transform: translate3d(0, 12px, 0); } 100% { opacity: 1; transform: none; } }
@keyframes mp-rise-4 { 0%, 38% { opacity: 0; transform: translate3d(0, 12px, 0); } 100% { opacity: 1; transform: none; } }
@keyframes mp-rise-5 { 0%, 46% { opacity: 0; transform: translate3d(0, 12px, 0); } 100% { opacity: 1; transform: none; } }
@keyframes mp-rise-6 { 0%, 53% { opacity: 0; transform: translate3d(0, 12px, 0); } 100% { opacity: 1; transform: none; } }
@keyframes mp-fade {
    from { opacity: 0; }
    to   { opacity: 1; }
}
@keyframes mp-pop {
    0%   { opacity: 0; transform: scale(.96); }
    70%  { transform: scale(1.008); }
    100% { opacity: 1; transform: none; }
}
@keyframes mp-slide-left {
    from { opacity: 0; transform: translate3d(-10px, 0, 0); }
    to   { opacity: 1; transform: none; }
}
/* The dash pattern lives inside the keyframes, so a line that never animates is
   simply drawn rather than invisible. */
@keyframes mp-draw {
    from { stroke-dasharray: 4000; stroke-dashoffset: 4000; }
    to   { stroke-dasharray: 4000; stroke-dashoffset: 0; }
}
@keyframes mp-drift {
    0%   { transform: translate3d(0, 0, 0) scale(1); }
    50%  { transform: translate3d(28px, -22px, 0) scale(1.08); }
    100% { transform: translate3d(0, 0, 0) scale(1); }
}
@keyframes mp-halo {
    0%, 100% { box-shadow: 0 0 0 0 rgba(72, 213, 151, .35); }
    60%      { box-shadow: 0 0 0 7px rgba(72, 213, 151, 0); }
}
@keyframes mp-sheen {
    from { transform: translate3d(-120%, 0, 0); }
    to   { transform: translate3d(320%, 0, 0); }
}
@keyframes mp-spin { to { transform: rotate(360deg); } }

/* ------------------------------------------------------------------- ambience */
/* The background blobs drift slowly so a mostly-static page still feels alive. */
.ambient-one { animation: mp-drift 26s ease-in-out infinite; }
.ambient-two { animation: mp-drift 34s ease-in-out infinite reverse; }

/* ------------------------------------------------------------------ entrances */
.topbar > div,
.filter-bar,
.lb-toolbar,
.settings-head,
.admin-summary { animation: mp-rise .42s var(--mp-ease-out); }

.sidebar .nav-item { animation: mp-slide-left .34s var(--mp-ease-out); }

/* Cards that hold no sticky content get the full lift-in. */
.kpi-card,
.podium,
.client-card,
.admin-card,
.sync-card,
.kpi-mini,
.admin-summary .stat { animation: mp-rise .5s var(--mp-ease-out); }

/* Panels wrap sticky table headers, so they only fade. */
.panel:not(.client-card):not(.admin-card):not(.sync-card):not(.kpi-mini):not(.stat) {
    animation: mp-fade .5s var(--mp-ease);
}

/* Stagger grids by swapping in a keyframe variant that holds longer before moving. */
.kpi-grid > *:nth-child(2), .podium-grid > *:nth-child(2), .client-grid > *:nth-child(2),
.admin-grid > *:nth-child(2), .sync-grid > *:nth-child(2), .detail-kpis > *:nth-child(2),
.admin-summary > *:nth-child(2) { animation-name: mp-rise-2; }
.kpi-grid > *:nth-child(3), .podium-grid > *:nth-child(3), .client-grid > *:nth-child(3),
.admin-grid > *:nth-child(3), .sync-grid > *:nth-child(3), .detail-kpis > *:nth-child(3),
.admin-summary > *:nth-child(3) { animation-name: mp-rise-3; }
.kpi-grid > *:nth-child(4), .podium-grid > *:nth-child(4), .client-grid > *:nth-child(4),
.admin-grid > *:nth-child(4), .sync-grid > *:nth-child(4), .detail-kpis > *:nth-child(4),
.admin-summary > *:nth-child(4) { animation-name: mp-rise-4; }
.kpi-grid > *:nth-child(5), .client-grid > *:nth-child(5), .admin-grid > *:nth-child(5),
.sync-grid > *:nth-child(5), .detail-kpis > *:nth-child(5),
.admin-summary > *:nth-child(5) { animation-name: mp-rise-5; }
.kpi-grid > *:nth-child(n+6), .client-grid > *:nth-child(n+6), .admin-grid > *:nth-child(n+6),
.sync-grid > *:nth-child(n+6), .detail-kpis > *:nth-child(n+6) { animation-name: mp-rise-6; }

/* Board rows re-render on every filter keystroke, so their entrance is deliberately
   short — long enough to read as a refresh, too short to wait on. */
.lb-row { animation: mp-fade .22s linear; }

/* --------------------------------------------------------------- interactions */
.kpi-card, .podium, .client-card, .admin-card, .sync-card, .kpi-mini,
.admin-summary .stat {
    transition: transform var(--mp-base) var(--mp-ease),
                box-shadow var(--mp-base) var(--mp-ease),
                border-color var(--mp-base) var(--mp-ease);
}
.kpi-card:hover, .client-card:hover, .admin-card:hover, .sync-card:hover,
.kpi-mini:hover, .admin-summary .stat:hover {
    transform: translate3d(0, -3px, 0);
    border-color: rgba(72, 149, 255, .3);
    box-shadow: 0 22px 44px rgba(0, 0, 0, .34);
}
.podium:hover { box-shadow: 0 22px 44px rgba(0, 0, 0, .34); }

.primary-button, .secondary-button, .icon-button, .mini, .seg button, .nav-item {
    transition: transform var(--mp-fast) var(--mp-ease),
                background-color var(--mp-fast) var(--mp-ease),
                border-color var(--mp-fast) var(--mp-ease),
                color var(--mp-fast) var(--mp-ease),
                box-shadow var(--mp-fast) var(--mp-ease),
                filter var(--mp-fast) var(--mp-ease);
}
.primary-button:hover, .secondary-button:hover, .icon-button:hover, .mini:hover {
    transform: translate3d(0, -1px, 0);
}
.primary-button:active, .secondary-button:active, .icon-button:active,
.mini:active, .seg button:active { transform: translate3d(0, 1px, 0) scale(.985); }
.primary-button:disabled:hover, .secondary-button:disabled:hover,
button:disabled:active { transform: none; }
.nav-item:hover { transform: translate3d(2px, 0, 0); }

/* Table rows get a colour-only hover so nothing shifts under the cursor. */
.lb-table tbody tr, .pivot-table tbody tr, .monthly-table tbody tr, .campaign-table tbody tr {
    transition: background-color var(--mp-fast) linear;
}

/* Keyboard focus must stay obvious now that hover carries so much of the feedback. */
a:focus-visible, button:focus-visible, input:focus-visible,
select:focus-visible, textarea:focus-visible, [tabindex]:focus-visible {
    outline: 2px solid rgba(120, 175, 255, .85);
    outline-offset: 2px;
    border-radius: 8px;
}

/* --------------------------------------------------------------------- accents */
.live-pill span { animation: mp-halo 2.6s ease-out infinite; }
.status-dot.online { animation: mp-halo 3.2s ease-out infinite; }
.icon-button.spinning svg { animation: mp-spin .85s linear infinite; }

/* A slow sheen across the leaderboard header reads as "live" without being loud. */
.pivot-head, .live-pill { position: relative; overflow: hidden; }
.pivot-head::after, .live-pill::after {
    content: '';
    position: absolute;
    top: 0; bottom: 0; left: 0;
    width: 18%;
    pointer-events: none;
    background: linear-gradient(100deg, transparent, rgba(255, 255, 255, .1), transparent);
    animation: mp-sheen 6.5s var(--mp-ease) infinite;
}
.live-pill::after { animation-duration: 4.5s; }

/* Medals get a subtle shine so the top three read as a podium at a glance. */
.rank-badge.gold, .rank-badge.silver, .rank-badge.bronze { position: relative; overflow: hidden; }
.rank-badge.gold::after, .rank-badge.silver::after, .rank-badge.bronze::after {
    content: '';
    position: absolute; inset: 0;
    background: linear-gradient(100deg, transparent 35%, rgba(255, 255, 255, .55) 50%, transparent 65%);
    animation: mp-sheen 4.2s var(--mp-ease) infinite;
}
.streak.hot i { display: inline-block; animation: mp-flicker 1.9s ease-in-out infinite; }
@keyframes mp-flicker {
    0%, 100% { transform: scale(1) rotate(0); }
    35%      { transform: scale(1.16) rotate(-6deg); }
    70%      { transform: scale(1.06) rotate(4deg); }
}

/* Bars and gauges move to their value instead of snapping to it. */
.client-meter i, .mini-meter i, .creative-track i {
    transition: width .85s var(--mp-ease-out);
}
.gauge { transition: background .9s var(--mp-ease-out); }

/* Chart lines draw themselves in. The dash length lives in the keyframes and is
   deliberately larger than any real path, so one value covers every chart and a line
   that never animates renders normally instead of vanishing. */
.line-spend, .line-revenue, .roas-line { animation: mp-draw 1.15s var(--mp-ease-out); }
.area-spend, .roas-area { animation: mp-fade 1.2s var(--mp-ease); }
/* Held, not delayed — the dots must be visible if the animation does not run. The
   hold is inside its own keyframe set so the modal's quick pop is unaffected. */
@keyframes mp-pop-late { 0%, 55% { opacity: 0; transform: scale(.96); } 80% { transform: scale(1.008); } 100% { opacity: 1; transform: none; } }
.pt, .data-point { animation: mp-pop-late 1s var(--mp-ease-out); }
.target-line, .target-label { animation: mp-fade 1.1s var(--mp-ease); }

/* ---------------------------------------------------------- modals and toasts */
.modal-backdrop { animation: mp-fade .18s linear; }
.modal { animation: mp-pop .28s var(--mp-ease-out); }
dialog[open] { animation: mp-pop .26s var(--mp-ease-out); }
.test-result { animation: mp-rise .3s var(--mp-ease-out); }
.notice { animation: mp-rise .3s var(--mp-ease-out); }

/* Numbers being counted up should not reflow the row as digits are added. */
.mp-counting { font-variant-numeric: tabular-nums; }

/* ------------------------------------------------------------------ scrollbars */
* { scrollbar-width: thin; scrollbar-color: #2b3850 #0d1522; }
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: rgba(13, 21, 34, .6); }
::-webkit-scrollbar-thumb {
    background: #2b3850; border-radius: 99px; border: 2px solid rgba(13, 21, 34, .6);
}
::-webkit-scrollbar-thumb:hover { background: #3b4c6b; }

/* --------------------------------------------------------------- reduced motion */
@media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
        animation-duration: .001ms !important;
        animation-iteration-count: 1 !important;
        transition-duration: .001ms !important;
        scroll-behavior: auto !important;
    }
    .ambient-one, .ambient-two,
    .pivot-head::after, .live-pill::after,
    .rank-badge.gold::after, .rank-badge.silver::after, .rank-badge.bronze::after {
        animation: none !important;
    }
    .line-spend, .line-revenue, .roas-line { stroke-dasharray: none; }
    .kpi-card:hover, .client-card:hover, .admin-card:hover, .sync-card:hover,
    .kpi-mini:hover, .admin-summary .stat:hover, .nav-item:hover,
    .primary-button:hover, .secondary-button:hover, .icon-button:hover, .mini:hover {
        transform: none;
    }
}
