/* SNALS Billing Desk — motion
 *
 * Loaded after app.css. Everything that moves is described here, so that
 * turning animation off is one rule at the bottom rather than a hunt.
 *
 * Three rules were followed throughout:
 *   1. Motion reports a change. Nothing loops for decoration except where the
 *      app is genuinely waiting on the server.
 *   2. Nothing blocks input. Every animation is on a property the compositor
 *      handles — transform and opacity — and nothing waits for one to finish
 *      before a click registers.
 *   3. Short. 120ms for a press, 200-400ms for a panel, 700ms for a figure
 *      counting up. Anything longer reads as a delay rather than a flourish.
 */

:root{
  --ease:cubic-bezier(.22,.7,.3,1);      /* settles quickly, no overshoot */
  --ease-back:cubic-bezier(.34,1.4,.5,1); /* small overshoot, for arrivals */
  --ease-in:cubic-bezier(.5,0,.9,.4);     /* for exits */
  --t-fast:.14s; --t-mid:.26s; --t-slow:.42s;
}

/* ==========================================================================
   1. Waiting on the server
   ========================================================================== */

/* A thread across the top whenever a request is in flight. It is the only
   thing that tells you the app is working before the answer arrives. */
#progress{position:fixed;top:0;left:0;right:0;height:2px;z-index:300;pointer-events:none;opacity:0;
          transition:opacity .2s ease}
#progress.on{opacity:1}
#progress .bar{height:100%;width:30%;border-radius:0 2px 2px 0;
  background:linear-gradient(90deg,transparent,var(--circuit),var(--azure),var(--circuit),transparent);
  animation:sweep 1.05s cubic-bezier(.5,0,.5,1) infinite}
@keyframes sweep{
  0%{transform:translateX(-110%) scaleX(.7)}
  55%{transform:translateX(160%) scaleX(1.25)}
  100%{transform:translateX(420%) scaleX(.7)}
}

/* Skeletons stand in for content while it loads, in the shape of the thing
   that is coming. A spinner tells you to wait; a skeleton tells you what for. */
.sk{position:relative;overflow:hidden;background:#E4EBF3;border-radius:5px;height:11px}
.sk::after{content:"";position:absolute;inset:0;transform:translateX(-100%);
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.85),transparent);
  animation:shimmer 1.25s infinite}
@keyframes shimmer{100%{transform:translateX(100%)}}
.sk.w20{width:20%}.sk.w30{width:30%}.sk.w40{width:40%}.sk.w50{width:50%}
.sk.w60{width:60%}.sk.w70{width:70%}.sk.w80{width:80%}.sk.full{width:100%}
.sk.tall{height:22px}.sk.chip{height:16px;width:62px;border-radius:20px}
.sk-row{display:flex;align-items:center;gap:14px;padding:13px 10px;border-bottom:1px solid var(--edge2)}
.sk-row>*{flex:1}
.sk-row .g{flex:0 0 78px}
.sk-stack>*{animation:skin .3s var(--ease) backwards}
@keyframes skin{from{opacity:0}to{opacity:1}}

/* The dot in the top bar: green idle, cyan and beating while saving. */
.netdot.busy{animation:beat .9s ease-in-out infinite}
@keyframes beat{0%,100%{transform:scale(1);opacity:1}50%{transform:scale(1.55);opacity:.45}}

/* ==========================================================================
   2. Moving between screens
   ========================================================================== */

/* The view slides in from the side it came from, so going deeper and coming
   back feel different. */
.wrap.anim{animation:viewin var(--t-mid) var(--ease)}
.wrap.anim.back{animation:viewback var(--t-mid) var(--ease)}
@keyframes viewin{from{opacity:0;transform:translate3d(14px,0,0)}to{opacity:1;transform:none}}
@keyframes viewback{from{opacity:0;transform:translate3d(-14px,0,0)}to{opacity:1;transform:none}}

/* Cards arrive in sequence rather than all at once. */
.wrap.anim .card,.wrap.anim .kpi{animation:cardin var(--t-slow) var(--ease) backwards}
.wrap.anim .edit .card{animation:none}
@keyframes cardin{from{opacity:0;transform:translate3d(0,14px,0) scale(.992)}to{opacity:1;transform:none}}

/* A card lifts a little under the pointer, so it reads as a surface. */
.card{transition:box-shadow var(--t-mid) var(--ease),transform var(--t-mid) var(--ease),border-color var(--t-mid)}
.kpi:hover{transform:translateY(-2px);box-shadow:0 8px 22px rgba(11,30,51,.10);border-color:#C3D4E5}
.kpi{transition:box-shadow var(--t-mid) var(--ease),transform var(--t-mid) var(--ease),border-color var(--t-mid)}
.kpi::after{transform-origin:top}
.wrap.anim .kpi::after{animation:growdown .55s var(--ease) .1s backwards}
@keyframes growdown{from{transform:scaleY(0)}to{transform:scaleY(1)}}

/* The marker beside the active sidebar item slides between items. */
.rail{position:relative}
.railmark{position:absolute;left:10px;width:3px;border-radius:2px;background:var(--circuit);
          height:0;top:0;opacity:0;pointer-events:none;
          transition:transform .32s var(--ease-back),height .32s var(--ease),opacity .2s ease;
          box-shadow:0 0 10px rgba(99,198,242,.65)}
.railmark.on{opacity:1}
.navb.on{box-shadow:none}           /* the sliding marker replaces the inset edge */
.navb .ic{transition:transform var(--t-mid) var(--ease-back),opacity var(--t-fast)}
.navb:hover .ic{transform:scale(1.22) rotate(-6deg)}
.navb.on .ic{transform:scale(1.1)}

/* Table rows arrive one after another, capped so a long list never crawls. */
@keyframes rowin{from{opacity:0;transform:translate3d(-8px,0,0)}to{opacity:1;transform:none}}
table.tb tbody tr{transition:background var(--t-fast),transform var(--t-fast)}
table.tb tbody tr:hover{background:#F5F9FD}

/* A row that was just created or changed glows once, then settles. */
@keyframes rowflash{
  0%{background:rgba(46,139,214,.20)}
  70%{background:rgba(46,139,214,.10)}
  100%{background:transparent}
}
tr.justsaved{animation:rowflash 1.6s var(--ease)}

/* ==========================================================================
   3. Controls
   ========================================================================== */

.btn{position:relative;overflow:hidden;
     transition:background var(--t-fast),border-color var(--t-fast),color var(--t-fast),
                transform .09s var(--ease),box-shadow var(--t-fast)}
.btn:hover{transform:translateY(-1px)}
.btn:active{transform:translateY(1px) scale(.985)}

/* The press spreads from where it was clicked. */
.ripple{position:absolute;border-radius:50%;transform:scale(0);pointer-events:none;
        background:currentColor;opacity:.22;animation:ripple .52s var(--ease) forwards}
@keyframes ripple{to{transform:scale(2.6);opacity:0}}

/* A save button does three things in turn: waits, then confirms, then resets. */
.btn .spin{width:13px;height:13px;border:2px solid currentColor;border-right-color:transparent;
           border-radius:50%;animation:spin .62s linear infinite;display:inline-block}
@keyframes spin{to{transform:rotate(360deg)}}
.btn.done{background:var(--good)!important;border-color:var(--good)!important;color:#fff!important}
.tick{display:inline-block;width:13px;height:13px}
.tick path{stroke:currentColor;stroke-width:2.6;fill:none;stroke-linecap:round;stroke-linejoin:round;
           stroke-dasharray:22;stroke-dashoffset:22;animation:draw .34s var(--ease) forwards}
@keyframes draw{to{stroke-dashoffset:0}}

/* Inputs: the focus ring grows rather than appearing. */
.in{transition:border-color var(--t-fast),box-shadow var(--t-mid) var(--ease),background var(--t-fast)}
.in:focus{box-shadow:0 0 0 3.5px rgba(46,139,214,.14)}
label.f>span{transition:color var(--t-fast)}
label.f:focus-within>span{color:var(--steel)}

/* Status words cross-fade when a document changes state. */
.chip{transition:background var(--t-mid),color var(--t-mid)}
@keyframes chippop{0%{transform:scale(.7);opacity:0}60%{transform:scale(1.08)}100%{transform:scale(1);opacity:1}}
.chip.changed{animation:chippop .38s var(--ease-back)}

/* ==========================================================================
   4. Panels and messages
   ========================================================================== */

.ov{animation:fadein var(--t-fast) ease-out}
.ov.out{animation:fadeout var(--t-fast) ease-in forwards}
.md{animation:mdin var(--t-mid) var(--ease-back)}
.ov.out .md{animation:mdout .18s var(--ease-in) forwards}
@keyframes fadein{from{opacity:0}to{opacity:1}}
@keyframes fadeout{to{opacity:0}}
@keyframes mdin{from{opacity:0;transform:translate3d(0,18px,0) scale(.96)}to{opacity:1;transform:none}}
@keyframes mdout{to{opacity:0;transform:translate3d(0,10px,0) scale(.98)}}

/* Toasts stack upward, newest at the bottom. */
#toasts{position:fixed;bottom:20px;left:50%;transform:translateX(-50%);z-index:120;
        display:flex;flex-direction:column-reverse;gap:8px;align-items:center;pointer-events:none;
        max-width:calc(100vw - 32px)}
.toast{position:static;transform:none;left:auto;bottom:auto;pointer-events:auto;
       animation:toastin .32s var(--ease-back)}
.toast.out{animation:toastout .2s var(--ease-in) forwards}
@keyframes toastin{from{opacity:0;transform:translate3d(0,18px,0) scale(.94)}to{opacity:1;transform:none}}
@keyframes toastout{to{opacity:0;transform:translate3d(0,8px,0) scale(.97)}}

/* The drop-down under your name. */
.whomenu{transform-origin:top right;animation:menuin .18s var(--ease-back)}
@keyframes menuin{from{opacity:0;transform:scale(.93) translateY(-5px)}to{opacity:1;transform:none}}
.wm{transition:background var(--t-fast),padding-left var(--t-fast)}
.wm:hover{padding-left:15px}

/* ==========================================================================
   5. The editor
   ========================================================================== */

/* A new line drops in and the removed one collapses out of the way. */
@keyframes linein{from{opacity:0;transform:translate3d(0,-9px,0)}to{opacity:1;transform:none}}
@keyframes lineout{to{opacity:0;transform:translate3d(-24px,0,0)}}
.lines tr.new td{animation:linein .3s var(--ease-back)}
.lines tr.going{animation:lineout .2s var(--ease-in) forwards}
.lines tr{transition:background var(--t-fast)}
.lines tr:hover{background:#FAFCFE}
.xb{transition:color var(--t-fast),background var(--t-fast),transform var(--t-fast)}
.xb:hover{transform:rotate(90deg) scale(1.1)}

/* The running total pulses whenever it changes, which is the confirmation
   that the figure you just typed actually landed. */
@keyframes totalflash{0%{background:rgba(46,139,214,.24)}100%{background:transparent}}
.tot .gt.flash{animation:totalflash .75s var(--ease)}
.tot div{transition:color var(--t-mid)}

/* The A4 preview cross-fades rather than blinking when the document changes. */
#docPage{transition:opacity .18s ease}
#docPage.swap{opacity:.35}

/* The PAID and VOID stamps land rather than appear. */
@keyframes stampdrop{
  0%{opacity:0;transform:rotate(-13deg) scale(2.1)}
  70%{opacity:.30;transform:rotate(-13deg) scale(.94)}
  100%{opacity:.17;transform:rotate(-13deg) scale(1)}
}
#docPage .stamp{animation:stampdrop .5s var(--ease) .12s backwards}

/* ==========================================================================
   6. Figures and charts
   ========================================================================== */

.barfill{transition:width .8s var(--ease)}
.bartrack{position:relative;overflow:hidden}
.trend .col{transition:height .75s var(--ease),filter var(--t-fast)}
.trend .mo:hover .col{filter:brightness(1.15) saturate(1.2)}
.trend .mo{transition:transform var(--t-fast)}
.trend .mo:hover{transform:translateY(-2px)}
.kpi .vl{transition:color var(--t-mid)}

/* ==========================================================================
   7. Empty states
   ========================================================================== */

.empty{animation:emptyin .42s var(--ease) backwards}
@keyframes emptyin{from{opacity:0;transform:translate3d(0,10px,0)}to{opacity:1;transform:none}}
.empty .mk{width:38px;height:38px;margin:0 auto 12px;opacity:.5}
.empty .mk path,.empty .mk polygon{stroke:var(--steel);stroke-width:1.6;fill:none;
  stroke-dasharray:120;stroke-dashoffset:120;animation:draw120 1.1s var(--ease) .15s forwards}
@keyframes draw120{to{stroke-dashoffset:0}}

/* ==========================================================================
   8. Sign-in and first paint
   ========================================================================== */

@keyframes markin{
  0%{opacity:0;transform:scale(.7) rotate(-16deg)}
  60%{opacity:1;transform:scale(1.06) rotate(3deg)}
  100%{opacity:1;transform:scale(1) rotate(0)}
}
@keyframes riseup{from{opacity:0;transform:translate3d(0,12px,0);letter-spacing:.44em}
                  to{opacity:1;transform:none;letter-spacing:.24em}}
@keyframes drawline{to{stroke-dashoffset:0}}
@keyframes splashout{to{opacity:0;visibility:hidden}}
@keyframes vin{from{opacity:0;transform:translate3d(0,9px,0)}to{opacity:1;transform:none}}
@keyframes softpulse{0%,100%{opacity:1}50%{opacity:.3}}
@keyframes popin{from{opacity:0;transform:scale(.95) translateY(12px)}to{opacity:1;transform:none}}
@keyframes flashv{0%{background:rgba(46,139,214,.22)}100%{background:transparent}}

#splash{transition:opacity .45s ease}
#splash.done{animation:splashout .45s ease-in forwards}
#splash img{animation:markin .8s var(--ease-back) both}
#splash .sp-t{animation:riseup .6s var(--ease) .32s both}
#splash .sp-s{animation:fadein .5s ease-out .56s both}
#splash svg path{stroke-dasharray:230;stroke-dashoffset:230;animation:drawline .88s var(--ease) .2s forwards}
#splash svg circle{opacity:0;animation:fadein .3s ease-out .82s forwards}

/* The brand mark in the corner acknowledges a click. */
.brand img{transition:transform .5s var(--ease-back)}
.brand:active img{transform:rotate(-18deg) scale(.92)}

/* ==========================================================================
   9. Off
   ========================================================================== */

/* One place. The setting adds .nomotion to <body>; the media query catches
   anyone whose device asks for less movement. Both stop everything, and the
   skeletons become plain grey blocks rather than disappearing — the layout
   must not shift between the two states. */
body.nomotion #splash{display:none}
body.nomotion #progress{display:none}
body.nomotion *,
body.nomotion *::before,
body.nomotion *::after{animation:none!important;transition:none!important}
body.nomotion .sk::after{display:none}
body.nomotion .barfill,
body.nomotion .trend .col{transition:none!important}

@media (prefers-reduced-motion:reduce){
  *,*::before,*::after{animation:none!important;transition:none!important}
  #splash,#progress{display:none!important}
  .sk::after{display:none}
}

@media print{
  *,*::before,*::after{animation:none!important;transition:none!important}
  #splash,#progress,#toasts{display:none!important}
}
