/* ──────────────────────────────────────────────────────────────────────────
   Loading indicators.

   Two distinct things, because they answer different questions:

   1. .mk-progress  — a slim top bar for navigation. Non-blocking: the page
      stays visible and interactive. Used when the user clicks a link and we
      do not know how long the next page will take.

   2. .mk-overlay   — a blocking veil for an operation the user explicitly
      started and must wait for (generate, upload, merge). Only shown when the
      UI genuinely cannot continue.

   What this replaces: a full-screen opaque panel shown on every page load and
   every link click. That hid content which had already rendered, made the
   loader itself the Largest Contentful Paint, and had no failsafe — a failed
   navigation left the user staring at a veil with no way out.
   ────────────────────────────────────────────────────────────────────────── */

/* ---------- 1. Top progress bar ---------- */

.mk-progress {
    position: fixed;
    inset-block-start: 0;
    inset-inline-start: 0;
    width: 100%;
    height: 3px;
    z-index: 10050;
    pointer-events: none;
    opacity: 0;
    transition: opacity .2s ease;
}

.mk-progress[data-active="true"] {
    opacity: 1;
}

.mk-progress__bar {
    height: 100%;
    width: 100%;
    transform: scaleX(0);
    transform-origin: left center;
    background: linear-gradient(90deg, var(--bs-primary, #2563eb), #60a5fa);
    /* transform + opacity only, so the bar is composited and costs the main
       thread nothing while a page is busy doing real work. */
    transition: transform .25s cubic-bezier(.4, 0, .2, 1);
    will-change: transform;
}

/* RTL: grow from the right edge, matching reading direction. */
[dir="rtl"] .mk-progress__bar {
    transform-origin: right center;
}

/* ---------- 2. Blocking overlay ---------- */

.mk-overlay {
    position: fixed;
    inset: 0;
    z-index: 10040;
    display: flex;
    align-items: center;
    justify-content: center;
    background: color-mix(in srgb, var(--bg-primary, #111827) 72%, transparent);
    backdrop-filter: blur(3px);
    opacity: 0;
    visibility: hidden;
    transition: opacity .18s ease, visibility .18s ease;
}

.mk-overlay[data-active="true"] {
    opacity: 1;
    visibility: visible;
}

.mk-overlay__card {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
    padding: 26px 34px;
    border-radius: 14px;
    background: var(--bg-secondary, #1b2232);
    color: var(--text-primary, #e6ecff);
    box-shadow: 0 10px 40px rgba(0, 0, 0, .35);
    max-width: min(90vw, 340px);
    text-align: center;
    /* Slight rise as it appears — transform only. */
    transform: translateY(6px) scale(.98);
    transition: transform .18s ease;
}

.mk-overlay[data-active="true"] .mk-overlay__card {
    transform: none;
}

.mk-overlay__spinner {
    width: 40px;
    height: 40px;
    border-radius: 50%;
    border: 3px solid color-mix(in srgb, currentColor 22%, transparent);
    border-top-color: var(--bs-primary, #2563eb);
    animation: mk-spin .8s linear infinite;
}

.mk-overlay__text {
    margin: 0;
    font-size: .95rem;
    line-height: 1.5;
    color: var(--text-secondary, #b7bed5);
}

/* The "still working" note only appears once an operation runs long. */
.mk-overlay__note {
    margin: 0;
    font-size: .82rem;
    color: var(--text-secondary, #8892b0);
    opacity: 0;
    transition: opacity .3s ease;
}

.mk-overlay__note[data-visible="true"] {
    opacity: 1;
}

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

/* ---------- Accessibility ---------- */

/* Honour the OS setting: no spin, no slide — the indicator still appears, it
   just does not move. */
@media (prefers-reduced-motion: reduce) {
    .mk-overlay__spinner { animation: none; border-top-color: currentColor; }
    .mk-progress__bar,
    .mk-overlay,
    .mk-overlay__card { transition: none; }
}

/* ---------- 3. Page-to-page transition ---------- */
/*
   The progress bar says "something is loading", but the swap itself was still
   an abrupt cut: the old page vanished and the new one appeared fully formed.

   Two mechanisms, because browser support splits:

   a) Cross-document View Transitions (Chrome 126+, Safari 18.2+). The browser
      snapshots the outgoing page, waits for the new document, and cross-fades
      between them. Nothing to wire up in JS — both pages just opt in.

   b) Everywhere else, an enter animation on the content container, started by
      loading.js on DOMContentLoaded.

   Deliberately NOT animated:

   - opacity from 0. An element at opacity 0 is not a Largest Contentful Paint
     candidate, so fading in from nothing pushes LCP out by the full duration
     of the animation. Both paths start at 0.6, which paints and so counts.
   - transform on <body> or <html>. A transformed ancestor becomes the
     containing block for position:fixed descendants, which would drag the
     sticky nav and any open modal out of place mid-animation.

   Durations are short on purpose: this is feedback, not choreography.
*/

@view-transition {
    navigation: auto;
}

::view-transition-old(root),
::view-transition-new(root) {
    /* The default is a 250ms cross-fade of the whole snapshot. Shorter, and
       opacity-only, so it stays on the compositor. */
    animation-duration: .18s;
    animation-timing-function: cubic-bezier(.4, 0, .2, 1);
}

/* Fallback path. .mk-anim is put on <html> by the inline script in
   theme-boot.html, before the first paint — loading.js is deferred and so runs
   too late to start an entrance.

   No animation-fill-mode: the end keyframe is the element's normal state
   anyway, so letting the animation release the element afterwards avoids
   keeping a compositor layer alive for the life of the page. */
.mk-anim main.app-content,
.mk-anim #mk-page {
    animation: mk-page-in .22s cubic-bezier(.4, 0, .2, 1);
}

@keyframes mk-page-in {
    from {
        opacity: .6;
        transform: translateY(6px);
    }
    to {
        opacity: 1;
        transform: none;
    }
}

/* Outgoing cue for browsers without view transitions: dim the content the
   moment a link is clicked, so the click registers visually even if the server
   takes a beat. Reverted on pageshow (including bfcache restores). */
.mk-leaving .mk-page-target {
    opacity: .72;
    transition: opacity .15s ease-out;
}

@media (prefers-reduced-motion: reduce) {
    ::view-transition-old(root),
    ::view-transition-new(root) {
        animation: none;
    }

    .mk-anim main.app-content,
    .mk-anim #mk-page {
        animation: none;
    }

    .mk-leaving .mk-page-target {
        opacity: 1;
        transition: none;
    }
}

/* ---------- 4. Theme swap ---------- */
/*
   theme.js adds .theme-transition to <html> for 300ms around a light/dark
   swap. It had no matching rule, so the class did nothing: every themed
   element animated its own colour on its own timing and the switch smeared.

   Suppressing transitions outright is the right call here — the swap should
   read as instant, and transitioning hundreds of colour properties at once is
   what made it look slow in the first place.
*/
.theme-transition,
.theme-transition *,
.theme-transition *::before,
.theme-transition *::after {
    transition: none !important;
}
