/* =============================================================================
   motion.css — transitions, loading feedback and lazy-media behaviour
   -----------------------------------------------------------------------------
   Loaded by BOTH the public site (after theme.css) and the admin (after
   admin.css), so the two share one motion vocabulary. Deliberately uses its own
   --mo-* tokens and literal brand colours: theme.css and admin.css keep separate
   variable namespaces, and this file must render correctly under either.

   Everything here degrades. No rule hides content that JavaScript is required to
   reveal — the fade-in classes are added BY script, so a page with no JS simply
   shows its images immediately.
   ========================================================================== */

:root {
    --mo-fast:    140ms;
    --mo-base:    240ms;
    --mo-slow:    420ms;
    --mo-reveal:  640ms;

    --mo-ease:        cubic-bezier(.22, .61, .36, 1);
    --mo-ease-out:    cubic-bezier(.16, 1, .3, 1);
    --mo-ease-spring: cubic-bezier(.34, 1.4, .64, 1);

    --mo-saffron: #E2761B;
    --mo-gold:    #C99A2E;
    --mo-maroon:  #7B2D26;
    --mo-line:    #E6DACA;
    --mo-sand:    #F1E5D3;
    --mo-ink:     #2A211B;
    --mo-ink-mut: #8A7A6C;
}

/* -----------------------------------------------------------------------------
   1. Route progress bar
   -----------------------------------------------------------------------------
   A hairline at the top of the viewport. For a file upload its width is the
   real number of bytes sent; for an ordinary page load it eases toward 90% and
   completes when the browser leaves the page.
   -------------------------------------------------------------------------- */
.route-progress {
    position: fixed;
    top: 0;
    left: 0;
    z-index: 9999;
    width: 100%;
    height: 3px;
    transform: scaleX(0);
    transform-origin: 0 50%;
    background: linear-gradient(90deg, var(--mo-saffron), var(--mo-gold) 55%, var(--mo-saffron));
    box-shadow: 0 0 8px rgba(226, 118, 27, .55), 0 1px 3px rgba(226, 118, 27, .35);
    opacity: 0;
    pointer-events: none;
    transition: transform var(--mo-base) var(--mo-ease-out), opacity 180ms linear;
}
.route-progress.is-active { opacity: 1; }
.route-progress.is-done   { opacity: 0; transition: opacity 240ms linear 100ms; }

/* -----------------------------------------------------------------------------
   2. Busy controls
   -------------------------------------------------------------------------- */
.spinner {
    display: inline-block;
    flex: none;
    width: 1em;
    height: 1em;
    vertical-align: -.15em;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    animation: mo-spin .62s linear infinite;
}
@keyframes mo-spin { to { transform: rotate(360deg); } }

.is-busy {
    pointer-events: none;
    cursor: progress;
}
.is-busy > .spinner { margin-right: .1em; }

/* The form beneath a running submission: readable, but plainly not accepting
   further input. */
.form-busy { position: relative; }
.form-busy > *:not(.upload-progress) { opacity: .55; transition: opacity var(--mo-base) var(--mo-ease); }
.form-busy input, .form-busy textarea, .form-busy select { pointer-events: none; }

/* -----------------------------------------------------------------------------
   3. Upload progress — driven by real bytes transferred
   -------------------------------------------------------------------------- */
.upload-progress {
    margin: 1.25rem 0 0;
    padding: 1rem 1.1rem;
    border: 1px solid var(--mo-line);
    border-radius: 12px;
    background: #fff;
    box-shadow: 0 1px 2px rgba(42, 33, 27, .05), 0 6px 20px rgba(42, 33, 27, .06);
    animation: mo-rise var(--mo-base) var(--mo-ease-out) both;
}
.upload-progress__head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 1rem;
    margin-bottom: .6rem;
    font-family: "Inter", system-ui, sans-serif;
    font-size: .86rem;
}
.upload-progress__name {
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-weight: 600;
    color: var(--mo-ink);
}
.upload-progress__pct {
    flex: none;
    font-variant-numeric: tabular-nums;
    font-weight: 700;
    color: var(--mo-saffron);
}
.upload-progress__track {
    height: 7px;
    border-radius: 99px;
    background: var(--mo-sand);
    overflow: hidden;
}
.upload-progress__fill {
    height: 100%;
    width: 0;
    border-radius: inherit;
    background: linear-gradient(90deg, var(--mo-saffron), var(--mo-gold));
    transition: width 180ms linear;
}
.upload-progress__note {
    margin: .6rem 0 0;
    font-family: "Inter", system-ui, sans-serif;
    font-size: .78rem;
    color: var(--mo-ink-mut);
}
/* Bytes are all sent, the server is still working. Nothing measurable is left
   to report, so the bar stops claiming a number and simply breathes. */
.upload-progress.is-processing .upload-progress__fill {
    background-size: 200% 100%;
    background-image: linear-gradient(90deg, var(--mo-saffron), var(--mo-gold), var(--mo-saffron));
    animation: mo-sweep 1.1s linear infinite;
}
@keyframes mo-sweep { from { background-position: 0 0; } to { background-position: -200% 0; } }

/* -----------------------------------------------------------------------------
   4. Scroll reveal
   -------------------------------------------------------------------------- */
.reveal {
    opacity: 0;
    transform: translateY(24px);
    transition: opacity var(--mo-reveal) var(--mo-ease-out),
                transform var(--mo-reveal) var(--mo-ease-out);
    will-change: opacity, transform;
}
.reveal.is-visible { opacity: 1; transform: none; will-change: auto; }

.reveal--left  { transform: translateX(-28px); }
.reveal--right { transform: translateX(28px); }
.reveal--zoom  { transform: scale(.94); }
.reveal--fade  { transform: none; }

/* -----------------------------------------------------------------------------
   5. Lazy media
   -----------------------------------------------------------------------------
   .media-fade is applied by script only, and only to images that had not
   already decoded. Without JS nothing is hidden.
   -------------------------------------------------------------------------- */
.media-fade {
    opacity: 0;
    transition: opacity var(--mo-slow) var(--mo-ease), filter var(--mo-slow) var(--mo-ease);
    filter: blur(8px);
}
.media-fade.is-loaded { opacity: 1; filter: none; }

/* Reserve the space an uploaded image will occupy, so nothing below it jumps
   when the bytes arrive. */
.media-frame {
    display: block;
    position: relative;
    overflow: hidden;
    background: linear-gradient(110deg, #EFE7DA 30%, #F8F2E8 50%, #EFE7DA 70%);
    background-size: 300% 100%;
}
.media-frame.is-waiting { animation: mo-shimmer 1.5s ease-in-out infinite; }

.skeleton {
    border-radius: 8px;
    background: linear-gradient(110deg, #EFE7DA 30%, #F8F2E8 50%, #EFE7DA 70%);
    background-size: 300% 100%;
    animation: mo-shimmer 1.5s ease-in-out infinite;
}
@keyframes mo-shimmer { from { background-position: 150% 0; } to { background-position: -150% 0; } }

/* Skip layout and paint for sections still far below the fold. */
.cv-auto { content-visibility: auto; contain-intrinsic-size: auto 560px; }

/* -----------------------------------------------------------------------------
   6. Entrances
   -------------------------------------------------------------------------- */
@keyframes mo-rise { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes mo-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes mo-pop  { from { opacity: 0; transform: translateY(14px) scale(.97); } to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: no-preference) {
    .page-enter { animation: mo-rise var(--mo-slow) var(--mo-ease-out) both; }

    /* Admin confirmation dialog and row-action popover */
    .confirm-modal:not([hidden]) .confirm-modal__backdrop { animation: mo-fade var(--mo-fast) ease both; }
    .confirm-modal:not([hidden]) .confirm-modal__dialog   { animation: mo-pop var(--mo-base) var(--mo-ease-spring) both; }
    .action-menu__popover:not([hidden])                   { animation: mo-pop 170ms var(--mo-ease-out) both; }

    /* Flash messages arrive rather than appear */
    .alert, .alert-success, .alert-info, .alert-error, .alert-warning { animation: mo-rise var(--mo-base) var(--mo-ease-out) both; }

    /* Table rows settle in sequence on first paint */
    .table-wrap tbody tr { animation: mo-fade var(--mo-base) ease both; }
    .table-wrap tbody tr:nth-child(1)  { animation-delay: 20ms; }
    .table-wrap tbody tr:nth-child(2)  { animation-delay: 40ms; }
    .table-wrap tbody tr:nth-child(3)  { animation-delay: 60ms; }
    .table-wrap tbody tr:nth-child(4)  { animation-delay: 80ms; }
    .table-wrap tbody tr:nth-child(5)  { animation-delay: 100ms; }
    .table-wrap tbody tr:nth-child(6)  { animation-delay: 120ms; }
    .table-wrap tbody tr:nth-child(7)  { animation-delay: 140ms; }
    .table-wrap tbody tr:nth-child(8)  { animation-delay: 160ms; }
    .table-wrap tbody tr:nth-child(n+9){ animation-delay: 180ms; }
}

/* -----------------------------------------------------------------------------
   7. Interaction polish
   -------------------------------------------------------------------------- */
a, button, .btn, input, select, textarea, summary { -webkit-tap-highlight-color: transparent; }

.btn:active:not(:disabled):not(.is-busy) { transform: translateY(1px) scale(.995); }

:where(a, button, [tabindex]):focus-visible {
    outline: 2px solid var(--mo-saffron);
    outline-offset: 2px;
    border-radius: 4px;
}

@media (prefers-reduced-motion: no-preference) {
    html { scroll-behavior: smooth; }
}

/* -----------------------------------------------------------------------------
   8. Reduced motion — last, so it wins
   -----------------------------------------------------------------------------
   Feedback is kept, movement is not: the progress bar still reports, spinners
   still turn slowly enough to read as "working", everything else is still.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
    .reveal, .reveal--left, .reveal--right, .reveal--zoom {
        opacity: 1 !important;
        transform: none !important;
    }
    .media-fade { opacity: 1 !important; filter: none !important; }
    .media-frame.is-waiting, .skeleton, .upload-progress.is-processing .upload-progress__fill { animation: none; }
    .page-enter, .alert, .alert-success, .alert-info, .alert-error, .alert-warning,
    .table-wrap tbody tr, .confirm-modal__dialog, .confirm-modal__backdrop, .action-menu__popover {
        animation: none !important;
    }
    .btn:active:not(:disabled):not(.is-busy) { transform: none; }
    .spinner { animation-duration: 1.4s; }
    .route-progress { transition: opacity 180ms linear; }
    html { scroll-behavior: auto; }
}
