/* loading — every wait in the product, built from the mark.
 *
 * From the Xconversion Loading States design. One shape carries all of it: the
 * two chevrons of the x. They draw in on boot, tick while a form saves, and
 * dim into skeletons while a record loads.
 *
 * ── The rule the rest of this file exists to keep ─────────────────────
 *
 *   0–300ms      nothing. A flash of loading reads as a bug, not as speed.
 *   300ms–2s     the tick spinner, in place — inside the button, or beside
 *                the field that caused it. **The page does not move.**
 *   2s–8s        skeletons that match the real layout, so nothing jumps.
 *   over 8s      a determinate bar and a count, and let them leave.
 *   cold start   the boot mark, once, 2.4s at most.
 *   failure      plain text and a retry, in the same spot. Never a spinner
 *                left turning on a dead request.
 *
 * The version this replaced put a full-screen veil over everything for a
 * form save. It broke the middle rule twice over: the page moved, and the
 * rail — which is how somebody clicks away from a mistake — went with it.
 *
 * Its own file because shell.css is four thousand lines, and the last two
 * names taken in there were already spoken for.
 */

/* ── the mark ─────────────────────────────────────────────────────────── */

/* Sized by `--x`, so one class covers 13px inside a button and 86px on a boot
   screen. Colour comes from `currentColor`: dropped into a filled button it
   is white, dropped beside a field it is the brand. */
.xc-x {
    inline-size: var(--x, 15px);
    block-size: var(--x, 15px);
    flex: none;
    display: block;
    color: inherit;
}

.xc-x path {
    stroke: currentColor;
    stroke-width: 3.4;
    stroke-linecap: round;
    stroke-linejoin: round;
    fill: none;
}

/* The trailing chevron is the quieter half, in every behaviour. */
.xc-x path:last-child { opacity: .45; }

/* ── working · the default ────────────────────────────────────────────── */

/* Quarter turns, because the x lands on itself every 90°: it reads as
   deliberate steps rather than as a wheel, and it never looks like it is
   going backwards at the wrong frame rate. */
.xc-x[data-spin="tick"] {
    animation: xc-tick 1.7s cubic-bezier(.7, 0, .3, 1) infinite;
    transform-origin: 50% 50%;
}

@keyframes xc-tick {
    0% { transform: rotate(0); }
    18%, 25% { transform: rotate(90deg); }
    43%, 50% { transform: rotate(180deg); }
    68%, 75% { transform: rotate(270deg); }
    93%, 100% { transform: rotate(360deg); }
}

/* ── thinking · the agent, and only the agent ─────────────────────────── */

/* The two halves open and close. Reserved for the model doing model things —
   drafting a reply, reading a thread — so that "the product is busy" and "the
   agent is thinking" are never the same picture. */
.xc-x[data-spin="think"] path:first-child {
    transform-origin: 9px 9px;
    animation: xc-sciL 1.3s ease-in-out infinite;
}

.xc-x[data-spin="think"] path:last-child {
    transform-origin: 9px 9px;
    animation: xc-sciR 1.3s ease-in-out infinite;
}

@keyframes xc-sciL { 0%, 100% { transform: rotate(0); } 50% { transform: rotate(-26deg); } }
@keyframes xc-sciR { 0%, 100% { transform: rotate(0); } 50% { transform: rotate(26deg); } }

/* ── boot · once per cold start ───────────────────────────────────────── */

.xc-x[data-spin="draw"] path {
    stroke-dasharray: 23;
    stroke-dashoffset: 23;
    animation: xc-draw .72s cubic-bezier(.5, 0, .15, 1) .15s forwards;
}

.xc-x[data-spin="draw"] path:last-child { animation-delay: .34s; }

@keyframes xc-draw { to { stroke-dashoffset: 0; } }

/* ── typing · in a chat bubble, in place of three grey dots ───────────── */

.xc-trail {
    display: inline-flex;
    align-items: center;
    gap: 6px;
}

.xc-trail .xc-x {
    animation: xc-dot 1.1s ease-in-out infinite;
    transform-origin: 50% 50%;
}

.xc-trail .xc-x:nth-child(2) { animation-delay: .16s; }
.xc-trail .xc-x:nth-child(3) { animation-delay: .32s; }

@keyframes xc-dot {
    0%, 100% { opacity: .25; transform: scale(.7); }
    40% { opacity: 1; transform: scale(1); }
}

/* ── a button that is working ─────────────────────────────────────────── */

/* The width is locked by the script before the label is swapped — see
   modules/busy.js — so nothing shifts under the cursor. Label changes,
   position does not.

   This is the whole of what an ordinary save shows. No overlay, no veil,
   nothing over the rail: the one control somebody pressed says it is working,
   and everything else on the page still works. */
.xc-btn-busy {
    cursor: progress;
    opacity: .92;
}

/* Stops a second press landing on the same action. Not `disabled`, which
   would drop the button out of the tab order mid-action and take the
   keyboard somewhere else. */
.xc-btn-busy { pointer-events: none; }

/* Held for a beat after a save, so "it worked" is seen rather than inferred
   from the page not changing. */
.xc-btn-done {
    background: var(--green) !important;
    border-color: var(--green) !important;
    color: #FFFFFF !important;
    pointer-events: none;
}

/* ── skeletons ────────────────────────────────────────────────────────── */

/* Match the real layout — same row heights, same column count — so nothing
   jumps on arrival. A skeleton that is not the shape of its content is a
   second layout the reader has to forget. */
.xc-skel {
    border-radius: 8px;
    background: linear-gradient(90deg,
        rgba(var(--ink), .07) 0%,
        rgba(var(--ink), .028) 40%,
        rgba(var(--ink), .07) 80%);
    background-size: 300% 100%;
    animation: xc-shim 1.4s linear infinite;
}

.xc-skel--line { block-size: 14px; border-radius: 5px; }
.xc-skel--tile { block-size: 62px; border-radius: 12px; }
.xc-skel--field { block-size: 40px; border-radius: 10px; }

.xc-skel:nth-child(2) { animation-delay: .08s; }
.xc-skel:nth-child(3) { animation-delay: .16s; }
.xc-skel:nth-child(4) { animation-delay: .24s; }

@keyframes xc-shim {
    from { background-position: 100% 0; }
    to { background-position: -100% 0; }
}

/* ── an indeterminate bar ─────────────────────────────────────────────── */

/* For a job with no count behind it. Never paired with a fake percentage:
   the number is the promise, and a made-up one is a broken promise on a
   screen somebody is watching. */
.xc-sweep {
    block-size: 6px;
    border-radius: 6px;
    background: rgba(var(--ink), .07);
    overflow: hidden;
}

.xc-sweep::after {
    content: '';
    display: block;
    block-size: 100%;
    inline-size: 100%;
    background: linear-gradient(90deg, transparent, rgb(var(--brand)), transparent);
    animation: xc-sweepx 1.5s cubic-bezier(.4, 0, .3, 1) infinite;
}

@keyframes xc-sweepx {
    0% { transform: translateX(-100%) scaleX(.45); }
    50% { transform: translateX(20%) scaleX(.75); }
    100% { transform: translateX(220%) scaleX(.45); }
}

/* ── what it all becomes with motion turned off ───────────────────────── */

@media (prefers-reduced-motion: reduce) {
    /* The mark stays, and stops. It is still the thing that says a wait is
       happening; it simply stops being the thing that moves. */
    .xc-x,
    .xc-x path,
    .xc-trail .xc-x,
    .xc-skel,
    .xc-sweep::after {
        animation: none !important;
    }

    .xc-x { opacity: .55; }
    .xc-x[data-spin="draw"] path { stroke-dashoffset: 0; }
    .xc-sweep::after { transform: none; opacity: .5; }
}

/* ── selecting rows ───────────────────────────────────────────────────── */

/* Appears only once something is ticked. A bar that is always there is a bar
   about an action nobody has chosen, taking a row of the table to say so. */
.xc-bulk {
    display: flex;
    align-items: center;
    gap: 11px;
    margin-block-start: 12px;
    padding: 10px 14px;
    border-radius: 14px;
    background: rgba(var(--brand), .07);
    border: 1px solid rgba(var(--brand), .22);
    font-size: 13.2px;
    color: rgba(var(--ink), .6);
    animation: xc-riseup .22s cubic-bezier(.22, .68, .3, 1) both;
}

.xc-bulk__n {
    display: flex;
    align-items: center;
    justify-content: center;
    min-inline-size: 26px;
    block-size: 24px;
    padding-inline: 7px;
    flex: none;
    border-radius: 20px;
    background: rgb(var(--brand));
    color: #FFFFFF;
    font-family: 'JetBrains Mono', monospace;
    font-size: 12px;
    font-variant-numeric: tabular-nums;
}

.xc-rows__tick {
    display: flex;
    align-items: center;
    justify-content: center;
}

/* ── the cold start ───────────────────────────────────────────────────── */

/* Once per session, 2.4s at most, and then the shell takes over.

   Dark whatever the theme is. It is the moment before the product exists, and
   a boot screen that changes with a preference somebody set inside the
   product is a boot screen claiming to know things it has not loaded yet. */
.xc-boot {
    position: fixed;
    inset: 0;
    z-index: 2147483000;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: 22px;
    background: radial-gradient(120% 90% at 50% 40%, #1E1E42 0%, #14142B 45%, #0C0C1C 100%);
    animation: xc-in .2s ease both;
}

/* Nothing behind it scrolls while it is up. */
html[data-booting] { overflow: hidden; }

/* The short version, for every load after the first of a session.

   Same picture, roughly half the time: the mark still draws, the name still
   arrives, the bar still runs — they simply do it at a pace that says "one
   moment" rather than "welcome". The three status lines are hidden by the
   script, because three lines that need three quarters of a second each
   cannot be read in one. */
.xc-boot[data-quick] { gap: 18px; }
.xc-boot[data-quick] .xc-boot__mark { --x: 64px; animation-duration: .3s; }
.xc-boot[data-quick] .xc-boot__mark path { animation-duration: .42s; animation-delay: .06s; }
.xc-boot[data-quick] .xc-boot__mark path:last-child { animation-delay: .16s; }
.xc-boot[data-quick] .xc-boot__name { font-size: 18px; animation-delay: .34s; animation-duration: .3s; }
.xc-boot[data-quick] .xc-boot__bar { animation-delay: .4s; }
.xc-boot[data-quick] .xc-boot__bar > span { animation-duration: .6s; animation-delay: .42s; }

.xc-boot__mark {
    --x: 86px;
    color: #FFFFFF;
    animation: xc-pop .5s cubic-bezier(.2, .9, .3, 1.3) both;
}

/* The trailing chevron is the brand's periwinkle here rather than a dimmer
   white: on black, opacity reads as a rendering fault. */
.xc-boot__mark path:last-child { stroke: #8F8FF7; opacity: 1; }

.xc-boot__name {
    font-family: Poppins, sans-serif;
    font-weight: 600;
    font-size: 21px;
    color: #FFFFFF;
    letter-spacing: .01em;
    animation: xc-up .6s ease .85s both;
}

.xc-boot__bar {
    inline-size: 190px;
    block-size: 2px;
    border-radius: 2px;
    background: rgba(255, 255, 255, .14);
    overflow: hidden;
    animation: xc-in .4s ease 1s both;
}

.xc-boot__bar > span {
    display: block;
    block-size: 100%;
    inline-size: 100%;
    transform-origin: left;
    background: linear-gradient(90deg, #5B5BF0, #FFFFFF);
    animation: xc-grow 1.35s cubic-bezier(.35, .6, .25, 1) 1.05s both;
}

/* Three lines in one place, each taking its turn. Stacked rather than
   swapped, so the box never changes height under them. */
.xc-boot__says {
    position: relative;
    display: block;
    inline-size: 260px;
    block-size: 14px;
}

.xc-boot__says > span {
    position: absolute;
    inset: 0;
    text-align: center;
    font-family: 'JetBrains Mono', monospace;
    font-size: 10px;
    letter-spacing: .14em;
    color: rgba(255, 255, 255, .5);
    animation: xc-say 2.4s linear both;
}

@keyframes xc-say {
    0%, 22% { opacity: 0; }
    32%, 60% { opacity: 1; }
    72%, 100% { opacity: 0; }
}

.xc-boot__foot {
    position: absolute;
    inset-block-end: 18px;
    font-family: 'JetBrains Mono', monospace;
    font-size: 9.5px;
    letter-spacing: .14em;
    color: rgba(255, 255, 255, .3);
}

@keyframes xc-up { from { opacity: 0; transform: translateY(10px); } to { opacity: 1; transform: none; } }
@keyframes xc-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes xc-grow { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes xc-pop { from { opacity: 0; transform: scale(.7); } to { opacity: 1; transform: none; } }

@media (prefers-reduced-motion: reduce) {
    /* It still appears, and it still says the same three things. It simply
       arrives rather than performing. */
    .xc-boot,
    .xc-boot__mark,
    .xc-boot__name,
    .xc-boot__bar,
    .xc-boot__bar > span { animation: none; }

    .xc-boot__says > span { animation-duration: 2.4s; }
}

/* ── pages of a list ──────────────────────────────────────────────────── */

.xc-pager {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    flex-wrap: wrap;
    margin-block-start: 14px;
    padding-block-start: 13px;
    border-block-start: 1px solid var(--line);
    font-size: 13px;
}

/* The half people actually read: how big the thing is they are paging
   through, not only how to leave it. */
.xc-pager__where { color: rgba(var(--ink), .5); }

.xc-pager__go {
    display: flex;
    align-items: center;
    gap: 8px;
}

.xc-pager__step {
    padding: 7px 14px;
    border: 1px solid var(--line);
    border-radius: 10px;
    background: var(--surface);
    color: rgba(var(--ink), .65);
    text-decoration: none;
    transition: border-color .14s ease, color .14s ease;
}

.xc-pager__step:hover {
    border-color: rgba(var(--brand), .45);
    color: rgb(var(--brand));
}

/* At the ends. A control that looks pressable and goes nowhere is the one
   somebody presses twice. */
.xc-pager__step[data-off] {
    opacity: .4;
    pointer-events: none;
}

.xc-pager__n {
    font-family: 'JetBrains Mono', monospace;
    font-size: 12px;
    color: rgba(var(--ink), .45);
    padding-inline: 4px;
}
