/* Rrage - motion (website half of the motion spec).
 *
 * Tokens, the hero main moment, scroll reveal once, smooth FAQ / job-group
 * open, button press, dialog + toast entrance, text-field focus, errors and
 * the loading skeleton.
 *
 * Compositor-only (transform/opacity) with the two exceptions the spec names:
 * the FAQ open height (Web Animations API, in motion.js) and the tick draw.
 * There is no colour literal here - every colour is a var(--token) from
 * site.css, so the palette stays in exactly one place.
 */
:root{
  --m-press:120ms;   /* button press only */
  --m-small:260ms;   /* menus, state colour, focus ring, marker turn */
  --m-enter:600ms;   /* rows and messages appearing */
  --m-open:400ms;    /* FAQs and expandable parts (height, in JS) */
  --m-fill:600ms;    /* progress bars / row entrance */
  --m-ease:cubic-bezier(.22,1,.36,1);
  --m-ring:rgba(255,107,74,.18); /* soft coral focus ring (18% coral) */
}

/* -- button press: scale to .96 in 120ms, back on release. All buttons. -- */
.btn{transition:transform var(--m-press) var(--m-ease),
  background-color var(--dur) var(--ease),border-color var(--dur) var(--ease),
  color var(--dur) var(--ease)}
.btn:active,.btn.is-press{transform:scale(.96)}

/* ============================ the hero main moment ====================== */
/* rows rise 12px and fade over 600ms, one at a time (motion.js feeds them) */
.win .row{animation:rowIn var(--m-fill) var(--m-ease) both}
@keyframes rowIn{from{opacity:0;transform:translateY(12px)}}

/* status dots: working = coral breathing, done = still green + a 300ms check
   draw, needs you = a coral ring pulse. Nothing else on screen pulses. */
.d{position:relative;display:inline-grid;place-items:center}
.d .ck{width:7px;height:7px;opacity:0}
.d .ck path{fill:none;stroke:var(--ink);stroke-width:2;stroke-linecap:round;
  stroke-linejoin:round;stroke-dasharray:14;stroke-dashoffset:14}
.d.work{background:var(--co);animation:breathe 1.1s ease-in-out infinite}
.d.done{background:var(--mint)}
.d.done .ck{opacity:1;animation:draw 300ms var(--m-ease) forwards}
.d.o::after{content:"";position:absolute;inset:-3px;border-radius:50%;
  border:2px solid var(--co);animation:ring 1.8s ease-out infinite}
@keyframes breathe{50%{opacity:.4}}
@keyframes draw{to{stroke-dashoffset:0}}
@keyframes ring{from{opacity:.55;transform:scale(.7)}to{opacity:0;transform:scale(1.9)}}

/* Approve / Look first fade in; the demo may auto-approve (motion.js). */
.win__acts{margin-top:14px}
.win__acts.mw-arm{opacity:0;transform:translateY(12px)}
.win__acts.in{opacity:1;transform:none;
  transition:opacity var(--m-fill) var(--m-ease),transform var(--m-fill) var(--m-ease)}
.win__replay{margin-top:10px}

/* the hero toast: rise 16px + fade 450ms, hold 2.4s, fade out. */
.htoast{display:inline-block;margin-top:12px;background:var(--fg);color:var(--bg);
  border-radius:var(--radius);padding:9px 14px;font-size:var(--fs-200);
  opacity:0;transform:translateY(16px);
  transition:opacity 450ms var(--m-ease),transform 450ms var(--m-ease)}
.htoast.in{opacity:1;transform:none}
.htoast[hidden]{display:none}

/* the hero headline only: rise word by word (spans added by motion.js). */
.hero__title .hw{display:inline-block}
.hero__title.hw-on .hw{opacity:0;transform:translateY(16px);
  animation:hwIn 800ms var(--m-ease) forwards}
@keyframes hwIn{to{opacity:1;transform:none}}

/* -- scroll reveal once: rises 14px + fades 700ms, first entry only.
 * The .reveal class is added by motion.js, so with JS off every block is
 * already visible. Side-by-side blocks step 60ms via --rd (max 4 steps). -- */
.reveal{opacity:0;transform:translateY(14px);
  transition:opacity 700ms var(--m-ease) var(--rd,0ms),
    transform 700ms var(--m-ease) var(--rd,0ms)}
.reveal.is-in{opacity:1;transform:none}

/* -- FAQs and the job groups: the marker turns 180deg (260ms). The height
 * itself is eased over 400ms by the Web Animations API in motion.js. -- */
.faq__item summary::after,.tasks summary::after{
  transition:transform var(--m-small) var(--m-ease)}
.faq__item[open] summary::after{transform:translateY(-50%) rotate(180deg)}
.tasks details[open] summary::after{transform:rotate(180deg)}

/* -- dialogs: fade in 450ms with a 12px rise; backdrop fades 200ms. -- */
.ov.is-open{animation:ovIn 200ms var(--m-ease)}
.ov.is-open .dc{animation:dcIn 450ms var(--m-ease)}
@keyframes ovIn{from{opacity:0}}
@keyframes dcIn{from{opacity:0;transform:translateY(12px)}}

/* -- toasts (account / help): rise 16px and fade in 450ms. -- */
.ts:not([hidden]){animation:toastIn 450ms var(--m-ease)}
@keyframes toastIn{from{opacity:0;transform:translateX(-50%) translateY(16px)}}

/* -- text fields: the border turns coral and a soft coral ring fades in over
 * 260ms; on blur it fades out the same way. The visible keyboard focus ring
 * (site.css :focus-visible outline) is kept. -- */
.field input,.field select,.field textarea{
  transition:border-color var(--m-small) var(--m-ease),
    box-shadow var(--m-small) var(--m-ease)}
.field input:focus-visible,.field select:focus-visible,.field textarea:focus-visible{
  border-color:var(--co);box-shadow:0 0 0 4px var(--m-ring)}
/* placeholder dims to 45% on focus, then disappears as typing starts. */
.field input::placeholder,.field textarea::placeholder{
  opacity:1;transition:opacity var(--m-small) var(--m-ease)}
.field input:focus::placeholder,.field textarea:focus::placeholder{opacity:.45}
/* errors: red border + the message fades in with a 4px drop over 260ms. */
.field [aria-invalid="true"]{border-color:var(--danger)}
.err:not([hidden]),.field__error:not([hidden]),
.field__error.is-in{animation:errIn var(--m-small) var(--m-ease)}
@keyframes errIn{from{opacity:0;transform:translateY(-4px)}}
/* character counter fades in only within 10% of the limit. */
.field__count{font-size:var(--fs-100);color:var(--mut);opacity:0;
  transition:opacity var(--m-small) var(--m-ease)}
.field__count.in{opacity:1}

/* -- loading: a shimmer skeleton, shown after 300ms and only while real work
 * happens. Never a full-screen spinner. -- */
.sk{position:relative;overflow:hidden;height:14px;border-radius:8px;
  background:var(--bd);margin-top:12px}
.sk::after{content:"";position:absolute;inset:0;transform:translateX(-100%);
  background:linear-gradient(90deg,transparent,rgba(255,255,255,.35),transparent);
  animation:shimmer 1.4s linear infinite}
@keyframes shimmer{to{transform:translateX(100%)}}

/* -- stop the loops the moment the window is hidden or minimised. -- */
html.is-hidden .d.work,html.is-hidden .d.o::after,html.is-hidden .cur,
html.is-hidden .status-pill i,html.is-hidden .sk::after{animation-play-state:paused}

/* -- reduced motion: show the final state at once. No typing, pulses,
 * shimmer or cursor blink. Everything still works. -- */
@media (prefers-reduced-motion:reduce){
  .win .row,.d.work,.d.o::after,.d.done .ck,.win__acts.in,.htoast,.reveal,.ts,
  .ov.is-open,.ov.is-open .dc,.err:not([hidden]),.field__error:not([hidden]),
  .sk::after,.hero__title .hw{animation:none !important}
  .reveal,.win__acts.mw-arm,.htoast{opacity:1;transform:none}
  .d.o::after{opacity:1}
  .field input::placeholder,.field textarea::placeholder{opacity:1}
}
