/* ==========================================================================
   BOSS — sign-in screen

   The page is one 3D scene rather than a flat two-column layout. Two things
   follow the pointer, both driven by --px / --py (each -1..1, written onto
   .auth by assets/js/auth.js and smoothed there, so nothing here needs a
   transition to keep up):

     the stage   the extruded mark, and the ring of EOS components orbiting it
     the card    the sign-in panel, which tips and catches the light with it

   A note that governs most of the odd-looking choices below: a grouping
   property — overflow, filter, backdrop-filter, clip-path, mask, or an
   opacity under 1 — forces transform-style back to flat on the element that
   carries it. Anything sitting between a perspective and the planes it is
   meant to arrange therefore has to stay clear of all six, which is why the
   glass, the glow and the sheen of the card are separate leaf elements rather
   than backgrounds and pseudo-elements on the card itself.
   ========================================================================== */

.auth {
    position: relative;
    z-index: 1;
    display: grid;
    grid-template-columns: 1.06fr .94fr;
    min-height: 100vh;

    /* Pointer, resting at dead centre until the script says otherwise. */
    --px: 0;
    --py: 0;

    /* Scene metrics. Everything on the stage is sized off these, so the whole
       composition scales as one object rather than as five loose parts. */
    --tile: clamp(96px, 10vw, 158px);
    --ring-r: clamp(165px, 17.4vw, 290px);
    --card-w: clamp(104px, 10.6vw, 140px);
    --card-h: clamp(88px, 8.6vw, 112px);

    /* The ring is a turntable under the mark rather than a belt around its
       middle: the card swinging to the front would otherwise pass straight
       across the object it is supposed to be orbiting, and at this camera it
       is magnified enough to hide it completely. --drop lowers the whole ring
       plane; --lift raises the mark off it. */
    --drop: 20%;
    --lift: 31%;

    /* One turn of the ring. The counter-rotation that keeps each card facing
       the reader, and the fade that dims the ones behind the mark, are both
       on this same duration — they only stay in step because they share it. */
    --turn: 36s;
}

/* ============================================================ left rail == */

/* The viewport height is asked for on the two columns rather than on the grid
   around them. A min-height on the grid does not reach its tracks — an auto
   row is still sized from its contents, and an fr row only divides height the
   container already has — so whenever the rail came up shorter than the
   window, both columns stopped early and left a band of bare page beneath. */
.auth__showcase {
    position: relative;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
    padding: 40px 48px 38px;
    overflow: hidden;
    border-right: 1px solid var(--border);
}

.auth__brand {
    position: relative;
    z-index: 4;
    align-self: flex-start;
    /* The copy used to clear the logo on the eyebrow badge's own margin; with
       the badge gone the headline sat flush under the wordmark, so the gap is
       stated here instead now that nothing between them is carrying it. */
    margin-bottom: 32px;
}

.auth__brand .logo {
    font-size: 19px;
}

/* ================================================================ stage == */

/* The camera. Perspective lives here and nowhere else on this side, so every
   plane below shares one vanishing point. */
.stage {
    position: relative;
    flex: 1 1 auto;
    min-height: 0;
    margin: 4px 0 8px;
    perspective: 1250px;
    perspective-origin: 50% 46%;
}

/* The stage takes whatever the rail has spare, and on a tall window that is a
   lot — but the scene inside it is a composition, not a background. Every
   piece of it is placed as a percentage of this box, so letting the box grow
   without limit would stretch the mark away from the ring it is meant to be
   hovering over. Capped and centred instead: the spare height stays spare. */
.stage__scene {
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    height: min(100%, 470px);
    transform-style: preserve-3d;
    /* A few degrees of built-in tilt, so the scene still reads as an object
       in space on a touch device where the pointer never moves it. */
    transform:
        translateY(-50%)
        rotateX(calc(5deg + var(--py) * -7deg))
        rotateY(calc(var(--px) * 13deg));
    will-change: transform;
}

/* ------------------------------------------------------------- the floor - */

/* A pool of light the object sits in — a plane laid flat, not a shadow, so it
   swings with the rest of the scene. */
.stage__floor {
    position: absolute;
    top: calc(50% + var(--drop));
    left: 50%;
    width: calc(var(--ring-r) * 2.3);
    height: calc(var(--ring-r) * 2.3);
    margin: calc(var(--ring-r) * -1.15) 0 0 calc(var(--ring-r) * -1.15);
    border-radius: 50%;
    background: radial-gradient(circle,
            rgba(var(--a1-rgb), .22) 0%,
            rgba(var(--a1-rgb), .07) 38%,
            transparent 70%);
    transform: rotateX(82deg) translateZ(calc(var(--tile) * -.75));
    filter: blur(12px);
}

/* ------------------------------------------------------- orbital plates -- */

/* Rings lying in the scene rather than drawn on top of it: each is a circle
   tipped onto its side, so perspective — not a hand-tuned ellipse — is what
   makes it read as an orbit. */
.plate {
    position: absolute;
    top: calc(50% + var(--drop));
    left: 50%;
    width: var(--size);
    height: var(--size);
    margin: calc(var(--size) / -2) 0 0 calc(var(--size) / -2);
    border-radius: 50%;
    border: 1px solid rgba(var(--a1-rgb), .34);
    transform-style: preserve-3d;
    animation: plate-turn var(--dur) linear infinite;
}

/* The static tilt has to be restated inside the keyframes: an animated
   transform replaces the property outright rather than adding to it. */
@keyframes plate-turn {
    from {
        transform: rotateX(var(--tilt)) rotateZ(0deg);
    }

    to {
        transform: rotateX(var(--tilt)) rotateZ(360deg);
    }
}

/* The inner one sits well inside the ring, in the open ground under the
   floating mark — the one part of the stage nothing else occupies, and where
   a plate actually reads as an orbit rather than as a line behind a card. */
.plate--1 {
    --size: calc(var(--ring-r) * .68);
    --tilt: 66deg;
    --dur: 18s;
    border-color: rgba(var(--a3-rgb), .45);
}

.plate--2 {
    --size: calc(var(--ring-r) * 1.06);
    --tilt: 71deg;
    --dur: 27s;
    border-style: dashed;
    animation-direction: reverse;
}

.plate--3 {
    --size: calc(var(--ring-r) * 1.9);
    --tilt: 76deg;
    --dur: 40s;
    border-color: rgba(var(--a1-rgb), .2);
}

/* Each plate carries one travelling light. It is tipped back out of the
   plate's own plane so it stays a round dot instead of a squashed one. */
.plate__sat {
    position: absolute;
    top: -4px;
    left: 50%;
    width: 8px;
    height: 8px;
    margin-left: -4px;
    border-radius: 50%;
    background: var(--a3);
    box-shadow: 0 0 14px 3px rgba(var(--a3-rgb), .85);
    transform: rotateX(calc(var(--tilt) * -1));
}

.plate--2 .plate__sat {
    background: var(--a1);
    box-shadow: 0 0 14px 3px rgba(var(--a1-rgb), .8);
}

.plate--3 .plate__sat {
    top: -3px;
    width: 6px;
    height: 6px;
    margin-left: -3px;
}

/* ------------------------------------------------ the six key components - */

/* A carousel in the round: six cards on a circle, the circle turning, and
   each card turning back by exactly as much so its text stays square to the
   reader. Three nested transforms do it — place, un-place, un-turn — because
   one element can only carry one transform, and the last of the three is the
   only one that animates. */
.ring {
    position: absolute;
    top: var(--drop);
    left: 0;
    right: 0;
    height: 100%;
    transform-style: preserve-3d;
    animation: ring-turn var(--turn) linear infinite;
}

@keyframes ring-turn {
    to {
        transform: rotateY(360deg);
    }
}

.ring__slot {
    position: absolute;
    top: 50%;
    left: 50%;
    width: var(--card-w);
    height: var(--card-h);
    margin: calc(var(--card-h) / -2) 0 0 calc(var(--card-w) / -2);
    transform-style: preserve-3d;
    transform: rotateY(calc(var(--i) * 60deg)) translateZ(var(--ring-r));
}

.ring__unturn {
    position: absolute;
    inset: 0;
    transform-style: preserve-3d;
    transform: rotateY(calc(var(--i) * -60deg));
}

.ring__spin {
    position: absolute;
    inset: 0;
    transform-style: preserve-3d;
    animation: ring-unturn var(--turn) linear infinite;
}

@keyframes ring-unturn {
    to {
        transform: rotateY(-360deg);
    }
}

/* The leaf, and the only place an opacity below 1 is safe here: it has no
   planes of its own left to flatten. The negative delay is what brings each
   card to full strength exactly as it swings to the front — six cards, six
   seconds apart on a thirty-six second turn.

   Height is the card's own business, not the slot's. Stretched to inset:0 it
   was locked to --card-h, and a note that ran to three lines then grew out
   through the rounded top and bottom edges instead of the card growing to
   hold it. It is pinned to the middle of the slot and left to size itself, so
   the copy can be as tall as it needs and stays inside the border either way.
   The slot keeps its fixed box: that box is what centres the card on the
   ring's axis, and it is never painted. */
.ring__card {
    position: absolute;
    top: 50%;
    left: 0;
    right: 0;
    min-height: var(--card-h);
    transform: translateY(-50%);
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: 7px;
    padding: 13px 14px;
    border-radius: var(--r-lg);
    background: linear-gradient(158deg, rgba(43, 55, 88, .92), rgba(10, 14, 26, .94));
    border: 1px solid rgba(var(--a1-rgb), .3);
    box-shadow:
        0 22px 46px -20px rgba(0, 0, 0, .9),
        inset 0 1px 0 rgba(var(--a3-rgb), .2);
    animation: ring-depth var(--turn) linear infinite;
    animation-delay: calc(var(--i) * -6s);
}

/* Not a straight ramp from front to back: a card an eighth of a turn out is
   still square-on and worth reading, and a linear fade would have it at 40%
   — bright enough to show through whatever is in front of it, which is what
   turns the ring into a smear. It holds full strength out to about 70
   degrees either side, drops over the next thirty, and stays down until it
   comes back round. */
@keyframes ring-depth {

    0%,
    100% {
        opacity: 1;
    }

    20% {
        opacity: .95;
    }

    28% {
        opacity: .1;
    }

    72% {
        opacity: .1;
    }

    80% {
        opacity: .95;
    }
}

.ring__top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 8px;
}

.ring__icon {
    display: grid;
    place-items: center;
    width: 30px;
    height: 30px;
    border-radius: 10px;
    color: #14192c;
    background: linear-gradient(140deg, #fbecb2, #d2a64e);
    box-shadow: 0 6px 14px -6px rgba(var(--a1-rgb), .9);
}

.ring__icon svg {
    width: 16px;
    height: 16px;
}

.ring__no {
    font-family: var(--font-mono);
    font-size: 10.5px;
    letter-spacing: .06em;
    color: rgba(var(--a3-rgb), .6);
}

.ring__label {
    font-size: 14.5px;
    font-weight: 650;
    letter-spacing: -.01em;
    color: #f2f0ea;
}

.ring__note {
    font-size: 11px;
    line-height: 1.35;
    color: rgba(242, 240, 234, .55);
}

/* Reading beats spinning: the whole mechanism stops together under the
   pointer, and it has to be all three animations or the cards come back out
   of step with the ring they are riding. */
.stage:hover .ring,
.stage:hover .ring__spin,
.stage:hover .ring__card,
.stage:focus-within .ring,
.stage:focus-within .ring__spin,
.stage:focus-within .ring__card {
    animation-play-state: paused;
}

/* ==========================================================================
   The mark, extruded

   Twelve parallel copies of the tile marching back from the face. Rotate the
   stack and the sliver of each copy showing past the one in front becomes the
   side wall — which is how the object gets real depth instead of a bevel
   painted onto a flat square. The count is fixed by the component that emits
   them; .mark3d__back below is placed one slab past the last of them.
   ========================================================================== */

.mark3d {
    position: absolute;
    top: var(--lift);
    left: 50%;
    width: var(--tile);
    height: var(--tile);
    margin: calc(var(--tile) / -2) 0 0 calc(var(--tile) / -2);
    transform-style: preserve-3d;

    /* One slab's thickness, and the corner radius carried over from the flat
       mark: 15 units of 64. Twelve slabs at this thickness put the wall at
       just over a fifth of the tile — deep enough that the sway below always
       has a band of gold edge showing, shallow enough that the mark still
       reads as a tile rather than as a brick. */
    --slab: calc(var(--tile) * .0185);
    --bevel: 23.4%;
}

.mark3d__body {
    position: absolute;
    inset: 0;
    transform-style: preserve-3d;
    animation: mark-float 9s var(--ease) infinite;
}

/* Its own slow drift, on top of whatever the pointer is doing to the stage
   around it — so the object is never completely still, and so the side wall
   is showing on one edge or the other for all but an instant of the cycle. A
   tile that only ever faces straight on is just an icon. */
@keyframes mark-float {

    0%,
    100% {
        transform: translateY(-3%) rotateY(-18deg) rotateX(4deg);
    }

    50% {
        transform: translateY(3%) rotateY(18deg) rotateX(-4deg);
    }
}

.mark3d__slab {
    position: absolute;
    inset: 0;
    border-radius: var(--bevel);
    /* Brushed gold, so the wall catches light along its length rather than
       reading as a flat brown edge. */
    background: linear-gradient(152deg, #f2dc9c 0%, #cda253 26%, #8d6a28 60%, #5d4619 100%);
    transform: translateZ(calc(var(--i) * var(--slab) * -1));
    /* An inset shadow with a spread this large floods the whole slab and
       still respects the corner radius — a cheap way to shade each layer
       deeper than the one in front of it without a second element. */
    box-shadow: inset 0 0 0 200px rgba(6, 9, 18, calc(var(--i) / 16));
}

/* One slab past the last, closing the solid off at the back — the mark is an
   object, and must not turn out to be hollow when it swings far enough to
   show its underside. */
.mark3d__back {
    position: absolute;
    inset: 0;
    border-radius: var(--bevel);
    background: #0a0e1a;
    transform: translateZ(calc(var(--slab) * -13));
}

.mark3d__tile {
    position: absolute;
    inset: 0;
    border-radius: var(--bevel);
    background: linear-gradient(148deg, #26314f 0%, #151c31 52%, #090d18 100%);
    box-shadow:
        inset 0 0 0 1.2px rgba(233, 199, 119, .24),
        inset 0 3px 26px rgba(0, 0, 0, .55);
}

/* The monogram rides a plane of its own just above the navy, which is what
   gives the letter a shadow of its own once the tile tips. */
.mark3d__glyph {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    transform: translateZ(calc(var(--tile) * .055));
    filter: drop-shadow(0 2px 4px rgba(0, 0, 0, .6));
}

.mark3d__glare {
    position: absolute;
    inset: 0;
    border-radius: var(--bevel);
    background: linear-gradient(112deg,
            transparent 32%,
            rgba(255, 255, 255, .3) 47%,
            rgba(255, 255, 255, .06) 56%,
            transparent 70%);
    background-size: 280% 100%;
    transform: translateZ(calc(var(--tile) * .075));
    pointer-events: none;
    animation: mark-glare 7s var(--ease) infinite;
}

@keyframes mark-glare {
    0% {
        background-position: 210% 0;
    }

    55%,
    100% {
        background-position: -90% 0;
    }
}

.mark3d__halo {
    position: absolute;
    inset: -46%;
    border-radius: 50%;
    background: radial-gradient(circle,
            rgba(var(--a1-rgb), .55) 0%,
            rgba(var(--a2-rgb), .16) 42%,
            transparent 70%);
    filter: blur(26px);
    animation: mark-halo 4.5s var(--ease) infinite;
}

/* The depth has to be restated in both frames for the same reason the plates
   restate their tilt — the animation owns the whole transform. */
@keyframes mark-halo {

    0%,
    100% {
        opacity: .6;
        transform: translateZ(calc(var(--tile) * -.6)) scale(.92);
    }

    50% {
        opacity: 1;
        transform: translateZ(calc(var(--tile) * -.6)) scale(1.08);
    }
}

/* The shadow it drops on the turntable below it. On a plane laid this flat,
   translateZ runs almost straight down the screen, so the distance here is
   how far under the mark the shadow lands. */
.mark3d__cast {
    position: absolute;
    top: 50%;
    left: 50%;
    width: calc(var(--tile) * 1.5);
    height: calc(var(--tile) * 1.5);
    margin: calc(var(--tile) * -.75) 0 0 calc(var(--tile) * -.75);
    border-radius: 50%;
    background: radial-gradient(circle, rgba(0, 0, 0, .55), transparent 66%);
    filter: blur(10px);
    transform: rotateX(82deg) translateZ(calc(var(--tile) * -1.25));
}

/* ============================================================== copy ===== */

.auth__showcase-body {
    position: relative;
    z-index: 3;
    max-width: 540px;
}

.auth__eyebrow {
    display: inline-flex;
    align-items: center;
    gap: 8px;
    padding: 5px 13px 5px 8px;
    margin-bottom: 18px;
    border-radius: 99px;
    font-size: 12px;
    font-weight: 600;
    letter-spacing: .01em;
    color: var(--a1s);
    background: rgba(var(--a1-rgb), .12);
    border: 1px solid rgba(var(--a1-rgb), .28);
}

.auth__eyebrow-dot {
    position: relative;
    width: 7px;
    height: 7px;
    border-radius: 50%;
    background: var(--emerald);
}

.auth__eyebrow-dot::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: 50%;
    background: var(--emerald);
    animation: pulse-ring 2s var(--ease) infinite;
}

@keyframes pulse-ring {
    from {
        transform: scale(1);
        opacity: .8;
    }

    to {
        transform: scale(3);
        opacity: 0;
    }
}

.auth__headline {
    font-size: clamp(28px, 3vw, 42px);
    font-weight: 720;
    letter-spacing: -.035em;
    line-height: 1.08;
}

.auth__headline span {
    display: block;
    /* Ink into accent, not into violet — base.css states the same ramp, and
       this file loads after it, so a literal here would win and strand the
       headline on the old palette. */
    background: linear-gradient(100deg, var(--headline-1) 0%, var(--a1s) 42%, var(--a3) 100%);
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
}

.auth__lede {
    margin-top: 14px;
    max-width: 52ch;
    font-size: 14.5px;
    line-height: 1.62;
    color: var(--text-dim);
}

/* The strip along the foot: what the six components add up to once they are
   actually being run. */
.auth__proof {
    display: flex;
    flex-wrap: wrap;
    gap: 12px 30px;
    margin-top: 22px;
    padding-top: 20px;
    border-top: 1px solid var(--border);
}

.auth__proof-item {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

.auth__proof-k {
    font-family: var(--font-mono);
    font-size: 19px;
    font-weight: 500;
    letter-spacing: -.02em;
    color: var(--a1s);
}

.auth__proof-v {
    font-size: 11.5px;
    letter-spacing: .04em;
    text-transform: uppercase;
    color: var(--text-faint);
}

/* ========================================================== right panel == */

.auth__panel {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 100vh;
    padding: 44px 40px;
    /* The card's camera. Longer than the stage's, because a form that tips
       hard stops looking like a form. */
    perspective: 1600px;
    perspective-origin: 50% 45%;
}

.auth__form-wrap {
    width: 100%;
    max-width: 408px;
    transform-style: preserve-3d;
}

/* Below 1080px the stage is gone, so the solid comes across to the form —
   same object, small enough to sit beside the wordmark. It gets a camera of
   its own: the panel's 1600px is set for a 408px card and would leave a 58px
   tile looking almost flat. */
.auth__mobile-brand {
    display: none;
    justify-content: center;
    margin-bottom: 26px;
    perspective: 620px;
}

.auth__mobile-brand .brand {
    gap: 14px;
    transform-style: preserve-3d;
}

.auth__mobile-brand .mark3d {
    position: static;
    flex: 0 0 var(--tile);
    margin: 0;
    --tile: 58px;
}

.auth__mobile-brand .logo {
    font-size: 18px;
}

.auth__head {
    transform: translateZ(26px);
}

.auth__title {
    font-size: 27px;
    letter-spacing: -.03em;
}

.auth__subtitle {
    margin-top: 7px;
    font-size: 14px;
    color: var(--text-dim);
}

/* ------------------------------------------------------------ the card -- */

/* Carries nothing but the tilt. Every surface it appears to have is one of
   the leaves below, because a background with a blur on it here would flatten
   the depth its own contents are relying on. */
.auth__card {
    position: relative;
    margin-top: 24px;
    transform-style: preserve-3d;
    transform:
        rotateX(calc(var(--py) * -5deg))
        rotateY(calc(var(--px) * 7deg));
    will-change: transform;
}

.auth__card-glow {
    position: absolute;
    inset: 4% -10%;
    border-radius: 50%;
    background: radial-gradient(circle at 50% 40%,
            rgba(var(--a1-rgb), .4) 0%,
            rgba(var(--a2-rgb), .14) 45%,
            transparent 72%);
    filter: blur(46px);
    transform: translateZ(-90px);
    pointer-events: none;
}

.auth__card-glass {
    position: absolute;
    inset: 0;
    overflow: hidden;
    border-radius: var(--r-xl);
    background: var(--card-bg);
    border: 1px solid var(--border);
    backdrop-filter: blur(22px);
    -webkit-backdrop-filter: blur(22px);
    box-shadow: var(--shadow-lg);
}

/* The sheet's own highlight, sliding with the pointer so the glass looks lit
   from wherever the cursor is rather than from a fixed studio lamp. */
.auth__card-glass::before {
    content: "";
    position: absolute;
    inset: 0;
    background: radial-gradient(58% 52% at calc(50% + var(--px) * 42%) calc(50% + var(--py) * 42%),
            rgba(var(--a3-rgb), .16) 0%,
            transparent 72%);
    pointer-events: none;
}

/* Slow sweeping highlight across the top edge. */
.auth__card-glass::after {
    content: "";
    position: absolute;
    top: 0;
    left: 0;
    right: 0;
    height: 1px;
    background: linear-gradient(90deg, transparent, rgba(var(--a1s-rgb), .9), transparent);
    animation: edge-sweep 5s var(--ease) infinite;
}

@keyframes edge-sweep {

    0%,
    100% {
        transform: translateX(-100%);
    }

    50% {
        transform: translateX(100%);
    }
}

/* The contents float a little proud of the glass — the whole point of tipping
   the card, since a flat panel tipping just looks like a picture of a panel
   being tipped. */
.auth__card-body {
    position: relative;
    padding: 28px;
    transform: translateZ(20px);
    transition: transform .4s var(--ease-out);
}

.auth__form-wrap:focus-within .auth__card-body {
    transform: translateZ(30px);
}

.auth__row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14px;
    margin: 18px 0 22px;
}

.auth__link {
    font-size: 13px;
    font-weight: 550;
    color: var(--violet-soft);
    transition: color .2s;
}

.auth__link:hover {
    color: var(--a3);
    text-decoration: underline;
    text-underline-offset: 3px;
}

.auth__legal {
    margin-top: 22px;
    text-align: center;
    font-size: 12px;
    line-height: 1.6;
    color: var(--text-faint);
    transform: translateZ(12px);
}

.auth__legal a {
    color: var(--text-dim);
    text-decoration: underline;
    text-underline-offset: 2px;
}

.auth__legal a:hover {
    color: var(--text);
}

/* ==========================================================================
   Toasts

   The demo-credential buttons and the forgotten-password link both call
   window.eosNotify, and the toast rules live in app.css — which the sign-in
   page has no reason to load, being the entire dashboard shell for the sake
   of one 320px notice. The few rules it actually needs are restated here.
   ========================================================================== */

.toasts {
    position: fixed;
    right: 22px;
    bottom: 22px;
    z-index: 90;
    display: flex;
    flex-direction: column-reverse;
    gap: 10px;
    pointer-events: none;
}

.toast {
    display: flex;
    align-items: flex-start;
    gap: 11px;
    width: 320px;
    padding: 13px 15px;
    border-radius: var(--r-md);
    font-size: 13.5px;
    background: var(--pop-bg);
    border: 1px solid var(--border-strong);
    box-shadow: var(--shadow-lg);
    backdrop-filter: blur(18px);
    -webkit-backdrop-filter: blur(18px);
    pointer-events: auto;
    animation: toast-in .4s var(--ease-out);
}

.toast.is-leaving {
    animation: toast-out .3s var(--ease) forwards;
}

.toast svg {
    width: 17px;
    height: 17px;
    flex: 0 0 17px;
    margin-top: 1px;
}

.toast--success svg {
    color: var(--t-emerald);
}

.toast--error svg {
    color: var(--t-rose);
}

@keyframes toast-in {
    from {
        opacity: 0;
        transform: translateX(40px);
    }

    to {
        opacity: 1;
        transform: translateX(0);
    }
}

@keyframes toast-out {
    to {
        opacity: 0;
        transform: translateX(40px);
    }
}

/* ============================================================ responsive = */

/* Short viewports give up the proof strip before they give up the stage: the
   thing turning in space is the reason to keep the left rail at all. */
@media (max-height: 840px) {
    .auth__proof {
        display: none;
    }

    .auth__showcase {
        padding: 32px 44px 30px;
    }
}

@media (max-height: 720px) {
    .auth {
        --tile: clamp(78px, 8vw, 104px);
        --ring-r: clamp(140px, 14.5vw, 186px);
        --card-w: clamp(96px, 9.2vw, 112px);
        --card-h: clamp(80px, 7.6vw, 92px);
    }

    .auth__lede {
        display: none;
    }
}

@media (min-width: 1081px) and (max-width: 1320px) {
    .auth {
        --ring-r: clamp(152px, 15.5vw, 200px);
        --card-w: clamp(100px, 9.6vw, 116px);
    }

    .auth__showcase {
        padding: 34px 32px 30px;
    }
}

@media (max-width: 1080px) {
    .auth {
        grid-template-columns: 1fr;
    }

    .auth__showcase {
        display: none;
    }

    .auth__mobile-brand {
        display: flex;
    }

    .auth__panel {
        padding: 40px 22px;
        min-height: 100vh;
    }
}

@media (max-width: 560px) {
    .toasts {
        right: 12px;
        left: 12px;
        bottom: 12px;
    }

    .toast {
        width: auto;
    }
}

@media (max-width: 460px) {
    /* The expansion is 37 characters set nowrap, which makes it the widest
       unbreakable thing on the page — and a flex item cannot shrink under its
       own min-content, so on a 320px screen it would push the whole form wider
       than the phone and take the card's right edge off the screen with it.
       The sidebar drops it at small sizes for the same reason. */
    .auth__mobile-brand .logo__tag {
        display: none;
    }

    .auth__card-glass {
        border-radius: var(--r-lg);
    }

    .auth__card-body {
        padding: 22px 18px;
    }

    .auth__title {
        font-size: 24px;
    }
}

/* ========================================================= reduced motion = */

/* base.css already flattens every duration to nothing, which would leave the
   ring frozen wherever the cascade happened to put it — cards stopped
   mid-turn, half of them held at the opacity their fade keyframe opens on.
   The ring is therefore dismantled rather than paused: the cards come off the
   circle and sit in a plain grid, where they can simply be read.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {

    /* Nothing is absolutely placed any more, so the stage stops claiming the
       rail's spare height and simply wraps the grid it now holds. */
    .stage {
        perspective: none;
        flex: 0 0 auto;
        margin: 26px 0 30px;
    }

    .stage__scene {
        position: static;
        display: flex;
        align-items: center;
        gap: 24px;
        width: 100%;
        height: auto;
        transform: none;
    }

    /* The halo and the cast sit after the body in source order and are only
       kept behind the tile by their depth. With the scene flattened that
       depth is gone, and the halo would wash straight over the face of the
       mark it is supposed to be glowing behind. */
    .stage__floor,
    .plate,
    .mark3d__glare,
    .mark3d__halo,
    .mark3d__cast {
        display: none;
    }

    .mark3d {
        position: static;
        flex: 0 0 var(--tile);
        margin: 0;
    }

    .ring {
        position: static;
        display: grid;
        grid-template-columns: repeat(3, minmax(0, 1fr));
        gap: 10px;
        flex: 1 1 auto;
        animation: none;
    }

    .ring__slot,
    .ring__unturn,
    .ring__spin,
    .ring__card {
        position: static;
        inset: auto;
        width: auto;
        height: auto;
        margin: 0;
        opacity: 1;
        transform: none;
        animation: none;
    }

    .auth__card,
    .auth__card-body,
    .auth__card-glow,
    .auth__head,
    .auth__legal,
    .mark3d__body,
    .mark3d__glyph,
    .mark3d__halo {
        transform: none;
    }
}

/* ==========================================================================
   Brand ground — the sign-in screen wears the logo

   Everything above follows --a1/--a2/--a3, and the page pins itself to the
   "boss" accent, so the whole screen is already gold. What is left is the
   ground it sits on: the backdrop tokens are re-pointed at the navy the mark
   is drawn in, so the page reads as the object enlarged rather than as the
   app's chrome with a gold filter over it.
   ========================================================================== */

.auth-body {
    /* Straight off the mark: tile light, tile mid, tile deep, and the two
       golds the monogram is drawn with. */
    --brand-navy: #26314f;
    --brand-navy-mid: #151c31;
    --brand-navy-deep: #090d18;
    --brand-gold: #d9ab4a;
    --brand-gold-lit: #fbecb2;

    /* The animated backdrop inherits these three. */
    --bg-deep: var(--brand-navy-deep);
    --atmos-1: #1c2440;
    --atmos-2: #2b2617;
    /* Gold blooms read as haze far sooner than violet does, so the drifting
       blobs are pulled back rather than left at dashboard strength. */
    --atmos-opacity: .3;

    /* The ring swings its cards wider than the rail at narrow desktop widths;
       the rail clips them, but the page must not grow a scrollbar over it. */
    overflow-x: hidden;
}

/* One blob stays navy. Three gold ones over a gold ground turn the whole
   page to sepia; the cool blob is what keeps the gold looking like light. */
.auth-body .atmos__blob--3 {
    background: radial-gradient(circle, rgba(38, 49, 79, .9), transparent 68%);
    opacity: .8;
}

/* The backdrop drifts against the pointer, a half-step behind the stage, so
   the two read as near and far rather than as one flat sheet. */
.auth-body .atmos__grid {
    transform: translate3d(calc(var(--px, 0) * -18px), calc(var(--py, 0) * -14px), 0);
}

/* Light mode gets warm paper rather than the navy, or the gold has nothing
   to sit against and every gold edge disappears into white. */
[data-theme="light"] .auth-body {
    --bg-deep: #f7f5f0;
    --atmos-1: #ece5d5;
    --atmos-2: #e3e7f2;
}

/* ---------------------------------------------------- showcase surface -- */

/* The left rail is the tile made page-sized: the same navy ramp on the
   diagonal, with the accent washes above it doing the glow. */
.auth-body .auth__showcase {
    border-right-color: rgba(var(--a1-rgb), .16);
    background:
        radial-gradient(90% 70% at 20% 12%, rgba(var(--a1-rgb), .17) 0%, transparent 58%),
        radial-gradient(70% 60% at 85% 92%, rgba(var(--a3-rgb), .1) 0%, transparent 60%),
        linear-gradient(155deg, rgba(38, 49, 79, .55) 0%, rgba(21, 28, 49, .5) 52%, rgba(9, 13, 24, .55) 100%);
}

[data-theme="light"] .auth-body .auth__showcase {
    background:
        radial-gradient(90% 70% at 20% 12%, rgba(var(--a1-rgb), .16) 0%, transparent 58%),
        radial-gradient(70% 60% at 85% 92%, rgba(var(--a2-rgb), .08) 0%, transparent 60%),
        linear-gradient(155deg, rgba(255, 255, 255, .72), rgba(247, 245, 240, .55));
}

/* A gold seam down the join, echoing the hairline around the tile. */
.auth-body .auth__showcase::after {
    content: "";
    position: absolute;
    top: 0;
    right: -1px;
    bottom: 0;
    width: 1px;
    background: linear-gradient(180deg, transparent, rgba(var(--a1-rgb), .55) 42%, rgba(var(--a3-rgb), .3) 70%, transparent);
    pointer-events: none;
    z-index: 5;
}

/* ------------------------------------------------------- the lockup ----- */

/* The flat lockup in the corner is the same object as the solid turning in
   the middle of the stage, so it gets the same gold halo — at a fraction of
   the strength, since the corner is not where the eye should land first. */
.auth-body .brand .logo__mark {
    filter: drop-shadow(0 8px 20px rgba(0, 0, 0, .5)) drop-shadow(0 0 14px rgba(var(--a1-rgb), .22));
}

/* --------------------------------------------------- brand-tinted card -- */

/* The card leans on the navy rather than plain white-on-black, so the panel
   and the solid look cut from the same material. */
.auth-body .auth__card-glass {
    background:
        linear-gradient(160deg, rgba(var(--a3-rgb), .06), rgba(var(--a1-rgb), .02) 45%, transparent),
        linear-gradient(160deg, rgba(38, 49, 79, .72), rgba(13, 18, 33, .84));
    border-color: rgba(var(--a1-rgb), .16);
}

[data-theme="light"] .auth-body .auth__card-glass {
    background: linear-gradient(160deg, #fff, #fdfbf6);
    border-color: rgba(var(--a1-rgb), .3);
    box-shadow: var(--shadow-md);
}

/* The focus ring is the one place the gold has to beat the field behind it
   in both themes, so it is stated rather than inherited. */
.auth-body .input:focus {
    border-color: rgba(var(--a1-rgb), .65);
    box-shadow: 0 0 0 3px rgba(var(--a1-rgb), .16);
}

/* ------------------------------------------------ light-mode legibility - */

/* Champagne (--a3) is the top of the gold ramp: it carries the glow on navy
   and disappears on cream. Anything that leans on it for legibility rather
   than for sparkle is moved down the ramp on the light theme. */

[data-theme="light"] .auth-body .auth__headline span {
    background: linear-gradient(100deg, var(--headline-1) 0%, var(--a1) 45%, var(--a2) 100%);
    -webkit-background-clip: text;
    background-clip: text;
}

[data-theme="light"] .auth-body .auth__proof-k {
    color: #a8802c;
}

/* The ring cards keep their navy in both themes — they are objects in the
   scene, lit by it, rather than surfaces of the page. Only the plates and the
   floor, which are light rather than material, are pulled up on cream. */
[data-theme="light"] .auth-body .plate {
    border-color: rgba(168, 128, 44, .3);
}

[data-theme="light"] .auth-body .stage__floor {
    background: radial-gradient(circle,
            rgba(168, 128, 44, .16) 0%,
            rgba(168, 128, 44, .05) 38%,
            transparent 70%);
}


