/* ------------------------------------------------------------------ *
 * The illustrations — a picture in a pool of light, and three layouts
 *
 * Every animated picture on the customer side is an `<x-illustration>`
 * (resources/views/components/illustration.blade.php), and every place that shows
 * one arranges it in one of three ways defined here:
 *
 *   .illo-moment   the picture above a heading, a sentence and a button —
 *                  an empty account, an empty search;
 *   .illo-card     the picture beside them, in a card at the top of a page —
 *                  a finished build, a ticket just sent, a server waiting for
 *                  its answers, a build that failed;
 *   .illo-overlay  a scene over the whole screen, for the seconds while an
 *                  order is placed or a payment is confirmed.
 *
 * ## No box
 *
 * The pictures used to sit on a pale square tile. It kept them readable in the
 * dark theme and made every one of them look pasted on: a drawing of a scene
 * inside a hard-edged card inside another card. Now each stands in a soft pool
 * of its brand's colour that fades to nothing, so the drawing sits *in* the page
 * rather than on top of it — breathing slowly, with a few specks of light
 * drifting round it. In the dark theme the pool is a lit one, which is what
 * keeps the drawings' white paper and pale panels looking like lit objects
 * rather than holes cut in the page.
 *
 * Loaded by the client panel as a Filament asset, and linked directly by the
 * pages that are not a panel — the help centre and the error pages — so the
 * same rules dress the same pictures everywhere. Written against Filament's
 * colour variables with plain fallbacks, and against Bootstrap's dark theme as
 * well as Filament's, because both kinds of page use it.
 * ------------------------------------------------------------------ */

.illo {
    --illo-tint: #4f46e5;
    --illo-size: 12rem;
    box-sizing: border-box;
    position: relative;
    isolation: isolate;
    flex: none;
    width: var(--illo-size);
    max-width: 100%;
    aspect-ratio: 1 / 1;
}

.illo--xs {
    --illo-size: 5.5rem;
}

.illo--sm {
    --illo-size: 9rem;
}

.illo--md {
    --illo-size: 12rem;
}

.illo--lg {
    --illo-size: 16rem;
}

.illo--xl {
    --illo-size: 20rem;
}

.illo--fill {
    --illo-size: 100%;
}

/* -- The pool of light ----------------------------------------------- */

.illo--halo::before {
    content: '';
    position: absolute;
    inset: -24%;
    z-index: -1;
    border-radius: 50%;
    pointer-events: none;
    background: radial-gradient(
        closest-side,
        color-mix(in oklab, var(--illo-tint) 20%, transparent) 0%,
        color-mix(in oklab, var(--illo-tint) 9%, transparent) 48%,
        transparent 100%
    );
    animation: illo-breathe 8s ease-in-out infinite;
}

.dark .illo--halo::before,
[data-bs-theme='dark'] .illo--halo::before,
.illo-on-dark .illo--halo::before {
    background: radial-gradient(
        closest-side,
        color-mix(in oklab, var(--illo-tint) 34%, rgb(255 255 255 / 0.5)) 0%,
        color-mix(in oklab, var(--illo-tint) 26%, transparent) 42%,
        transparent 100%
    );
}

/* `.illo-on-dark` is for a surface that is dark in both themes — the scene
   beside the immersive sign-in form — and gets the lit pool either way. */

/* A few specks of light, drifting. Fixed in size rather than scaled with the
   picture: they are motes in the air around it, not part of the drawing. */
.illo--halo::after {
    content: '';
    position: absolute;
    inset: -10%;
    z-index: -1;
    pointer-events: none;
    background:
        radial-gradient(circle at 12% 24%, color-mix(in oklab, var(--illo-tint) 60%, transparent) 0 0.2rem, transparent 0.22rem),
        radial-gradient(circle at 88% 16%, color-mix(in oklab, var(--illo-tint) 45%, transparent) 0 0.15rem, transparent 0.17rem),
        radial-gradient(circle at 84% 80%, color-mix(in oklab, var(--illo-tint) 50%, transparent) 0 0.22rem, transparent 0.24rem),
        radial-gradient(circle at 18% 86%, color-mix(in oklab, var(--illo-tint) 35%, transparent) 0 0.13rem, transparent 0.15rem),
        radial-gradient(circle at 50% 4%, color-mix(in oklab, var(--illo-tint) 30%, transparent) 0 0.11rem, transparent 0.13rem);
    animation: illo-drift 11s ease-in-out infinite alternate;
}

.illo--xs.illo--halo::after {
    display: none;
}

@keyframes illo-breathe {
    0%, 100% { transform: scale(0.95); opacity: 0.85; }
    50% { transform: scale(1.05); opacity: 1; }
}

@keyframes illo-drift {
    from { transform: translate3d(0, 0.3rem, 0) rotate(-3deg); }
    to { transform: translate3d(0, -0.4rem, 0) rotate(6deg); }
}

/* -- The drawing ------------------------------------------------------ */

/* Rises into place as it arrives, rather than blinking on. */
.illo-art {
    position: relative;
    width: 100%;
    height: 100%;
    opacity: 0;
    transform: translateY(0.5rem) scale(calc(var(--illo-zoom, 1) * 0.96));
    transition:
        opacity 480ms ease-out,
        transform 700ms cubic-bezier(0.2, 0.8, 0.2, 1);
}

.illo-art.is-ready {
    opacity: 1;
    transform: scale(var(--illo-zoom, 1));
}

/* A picture that did not load takes its light with it; the words stay. */
.illo.is-failed {
    display: none;
}

/* ------------------------------------------------------------------ *
 * Words, in all three layouts
 * ------------------------------------------------------------------ */

.illo-moment,
.illo-card,
.illo-overlay {
    --illo-heading: var(--gray-950, #09090b);
    --illo-body: var(--gray-600, #52525b);
}

.dark :is(.illo-moment, .illo-card, .illo-overlay),
[data-bs-theme='dark'] :is(.illo-moment, .illo-card, .illo-overlay) {
    --illo-heading: #fff;
    --illo-body: var(--gray-300, #d4d4d8);
}

.illo-title {
    margin: 0;
    font-size: 1.125rem;
    line-height: 1.5rem;
    font-weight: 700;
    letter-spacing: -0.01em;
    color: var(--illo-heading);
}

.illo-text {
    margin: 0.375rem 0 0;
    font-size: 0.875rem;
    line-height: 1.375rem;
    color: var(--illo-body);
}

.illo-actions {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.5rem;
    margin-top: 1rem;
}

/* ------------------------------------------------------------------ *
 * The moment: picture, heading, sentence, button — centred
 *
 * The words follow the picture in, a beat apart, so the eye lands on the
 * drawing first and reads down.
 * ------------------------------------------------------------------ */

.illo-moment {
    display: flex;
    flex-direction: column;
    align-items: center;
    padding: 2rem 1rem 1.75rem;
    text-align: center;
}

.illo-moment > .illo {
    margin-bottom: 1.75rem;
}

.illo-moment .illo-text {
    max-width: 28rem;
}

.illo-moment .illo-actions {
    justify-content: center;
    margin-top: 1.25rem;
}

.illo-moment > :not(.illo) {
    animation: illo-rise 560ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}

.illo-moment > :nth-child(2) { animation-delay: 90ms; }
.illo-moment > :nth-child(3) { animation-delay: 160ms; }
.illo-moment > :nth-child(n + 4) { animation-delay: 230ms; }

@keyframes illo-rise {
    from { opacity: 0; transform: translateY(0.5rem); }
}

/* ------------------------------------------------------------------ *
 * The card: picture beside the words, at the top of a page
 *
 * The picture's light spills into the card and is cut off only by the card's
 * own rounded edge, so the two read as one scene.
 *
 * The tone is the one colour the card is about — green for something that
 * worked, the brand's for something being asked, grey for something that went
 * wrong on our side. Grey on purpose for that last one: a failed build is ours
 * to fix, and a red card tells the customer it is an emergency they have to act
 * on when the sentence inside it says the opposite.
 * ------------------------------------------------------------------ */

.illo-card {
    --illo-card-tone: var(--primary-500, #4f46e5);
    position: relative;
    display: flex;
    align-items: center;
    gap: 1.75rem;
    padding: 1.5rem 1.5rem 1.5rem 2rem;
    overflow: hidden;
    border-radius: var(--radius-xl, 0.75rem);
    background:
        radial-gradient(90% 140% at 0% 50%, color-mix(in oklab, var(--illo-card-tone) 12%, transparent), transparent 62%),
        white;
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--illo-card-tone) 24%, transparent);
}

.dark .illo-card {
    background:
        radial-gradient(90% 140% at 0% 50%, color-mix(in oklab, var(--illo-card-tone) 18%, transparent), transparent 62%),
        var(--gray-900, #18181b);
    box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--illo-card-tone) 30%, transparent);
}

.illo-card--success {
    --illo-card-tone: var(--success-500, #10b981);
}

.illo-card--neutral {
    --illo-card-tone: var(--gray-400, #a1a1aa);
}

.illo-card--dismissible {
    padding-right: 3rem;
}

.illo-card-body {
    position: relative;
    min-width: 0;
    flex: 1 1 auto;
}

/* A thumbnail beside a sentence has room to grow once the sentence does. */
@media (min-width: 640px) {
    .illo-card > .illo--xs {
        --illo-size: 6.5rem;
    }
}

.illo-eyebrow {
    display: block;
    margin-bottom: 0.25rem;
    font-size: 0.75rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: color-mix(in oklab, var(--illo-card-tone) 80%, var(--illo-heading));
}

.illo-card-close {
    position: absolute;
    top: 0.625rem;
    right: 0.625rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    border-radius: 0.5rem;
    color: var(--gray-400, #a1a1aa);
    transition: background-color 100ms, color 100ms;
}

.illo-card-close:hover {
    color: var(--gray-700, #3f3f46);
    background-color: color-mix(in oklab, var(--gray-500, #71717a) 10%, transparent);
}

.dark .illo-card-close:hover {
    color: var(--gray-200, #e4e4e7);
}

.illo-card-close-icon {
    width: 1.125rem;
    height: 1.125rem;
}

/* A picture larger than a thumbnail goes above the words on a phone, where
   beside them it would leave each line two words long. */
@media (max-width: 639px) {
    .illo-card {
        padding: 1.5rem 1.25rem 1.25rem;
        background:
            radial-gradient(140% 70% at 50% 0%, color-mix(in oklab, var(--illo-card-tone) 12%, transparent), transparent 62%),
            white;
    }

    .dark .illo-card {
        background:
            radial-gradient(140% 70% at 50% 0%, color-mix(in oklab, var(--illo-card-tone) 18%, transparent), transparent 62%),
            var(--gray-900, #18181b);
    }

    .illo-card:has(> .illo:not(.illo--xs)) {
        flex-direction: column;
        align-items: stretch;
        gap: 1.5rem;
    }

    .illo-card:has(> .illo:not(.illo--xs)) > .illo {
        align-self: center;
    }
}

/* ------------------------------------------------------------------ *
 * The overlay: a scene over the screen, for a few seconds
 *
 * No panel. The page behind goes soft and a wash of the brand's colour rises
 * from the middle, and the picture and its words stand in it — the few seconds
 * of an order being placed or a payment being checked get the whole screen,
 * because nothing else on it matters until they are over.
 *
 * Above Filament's sidebar, top bar and modals, and below its notifications —
 * so the toast that follows a payment, with the amount and the new balance in
 * it, lands on top of the scene rather than behind it.
 * ------------------------------------------------------------------ */

.illo-overlay {
    --illo-wash: var(--primary-500, #4f46e5);
    position: fixed;
    inset: 0;
    z-index: 45;
    display: grid;
    place-items: center;
    padding: 1.5rem;
    overflow: hidden;
    background:
        radial-gradient(48rem 34rem at 50% 42%, color-mix(in oklab, var(--illo-wash) 16%, transparent), transparent 70%),
        color-mix(in oklab, white 84%, transparent);
    -webkit-backdrop-filter: blur(14px) saturate(1.1);
    backdrop-filter: blur(14px) saturate(1.1);
    animation: illo-fade-in 260ms ease-out;
}

.dark .illo-overlay {
    background:
        radial-gradient(48rem 34rem at 50% 42%, color-mix(in oklab, var(--illo-wash) 24%, transparent), transparent 70%),
        color-mix(in oklab, var(--gray-950, #09090b) 88%, transparent);
}

.illo-overlay--success {
    --illo-wash: var(--success-500, #10b981);
}

.illo-overlay--danger {
    --illo-wash: var(--danger-500, #ef4444);
}

.illo-overlay-scene {
    display: flex;
    width: min(32rem, 100%);
    flex-direction: column;
    align-items: center;
    text-align: center;
}

.illo-overlay-scene > .illo {
    --illo-size: min(18rem, 70vw);
    margin-bottom: 2rem;
}

.illo-overlay-scene .illo-title {
    font-size: 1.5rem;
    line-height: 2rem;
}

.illo-overlay-scene .illo-text {
    max-width: 24rem;
    font-size: 0.9375rem;
    line-height: 1.5rem;
}

.illo-overlay-scene > :not(.illo) {
    animation: illo-rise 560ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}

.illo-overlay-scene > :nth-child(2) { animation-delay: 90ms; }
.illo-overlay-scene > :nth-child(n + 3) { animation-delay: 160ms; }

.illo-overlay-scene .illo-actions {
    justify-content: center;
    margin-top: 1.5rem;
}

/* Still working: a bar that never claims to know how far along it is. */
.illo-progress {
    position: relative;
    width: 12rem;
    height: 0.25rem;
    margin-top: 1.5rem;
    overflow: hidden;
    border-radius: 9999px;
    background-color: color-mix(in oklab, var(--illo-wash, #4f46e5) 16%, transparent);
}

.illo-progress::after {
    content: '';
    position: absolute;
    inset: 0;
    width: 40%;
    border-radius: inherit;
    background: linear-gradient(90deg, transparent, var(--illo-wash, #4f46e5), transparent);
    animation: illo-progress 1.4s ease-in-out infinite;
}

@keyframes illo-progress {
    from { transform: translateX(-100%); }
    to { transform: translateX(250%); }
}

@keyframes illo-fade-in {
    from { opacity: 0; }
}

/* ------------------------------------------------------------------ *
 * Filament's table empty state, with a picture where its icon was
 *
 * Filament renders an Htmlable icon inside its own grey circle and a sized
 * icon box; both are undone for a picture, which brings its own light.
 * ------------------------------------------------------------------ */

.fi-ta-empty-state:has(.illo) {
    overflow: hidden;
}

.fi-ta-empty-state .fi-ta-empty-state-icon-bg:has(.illo) {
    margin: 0.5rem 0 1.75rem;
    padding: 0;
    border-radius: 0;
    background: none;
}

.fi-ta-empty-state .fi-ta-empty-state-icon-bg:has(.illo) > .fi-icon {
    width: auto;
    height: auto;
}

/* ------------------------------------------------------------------ *
 * Less motion
 *
 * The drawings stand still (the script picks the frame), and so does
 * everything around them: no breathing, no drifting, no rising in.
 * ------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
    .illo--halo::before,
    .illo--halo::after,
    .illo-overlay,
    .illo-moment > :not(.illo),
    .illo-overlay-scene > :not(.illo) {
        animation: none;
    }

    .illo-art {
        transform: scale(var(--illo-zoom, 1));
        transition: opacity 200ms linear;
    }

    .illo-progress::after {
        animation-duration: 3s;
    }
}
