/* RapidXpk Studio motion layer.
   Only transform and opacity animate (clip-path / filter sparingly). Everything that hides content is inside
   prefers-reduced-motion: no-preference AND needs html.js, so without JS or with reduced motion the page is final. */
:root {
  --ease-out: cubic-bezier(.16, 1, .3, 1);
  --ease-spring: cubic-bezier(.34, 1.56, .64, 1);
  --ease-in-out: cubic-bezier(.65, 0, .35, 1);
  --t-micro: 180ms;
  --t-reveal: 560ms;
}

/* Pause every ambient loop off screen and in hidden tabs (rule 7) */
.is-paused, .is-paused *, .tab-hidden *, .tab-hidden *::before, .tab-hidden *::after { animation-play-state: paused !important; }

@media (prefers-reduced-motion: no-preference) {

  /* ===== Global ===== */
  /* #1 header slide-down on load */
  .js .nav { animation: nav-in 400ms var(--ease-out) both; }
  @keyframes nav-in { from { transform: translateY(-100%); opacity: 0; } }
  /* #3 compact header after 80px (adjusted: stays solid, logo scales, no blur) */
  .logo { transition: transform 240ms var(--ease-out); }
  .nav.compact .logo { transform: scale(.86); }
  /* #4 hide on scroll down, show on scroll up (desktop) */
  .nav { transition: transform 300ms var(--ease-out); }
  .nav.hide { transform: translateY(-110%); }
  /* #5 active link underline glides between links */
  .nav-ink { transition: transform 420ms var(--ease-out), opacity 200ms; }
  /* #6 smooth anchor scrolling with header offset */
  html { scroll-behavior: smooth; }
  /* #7 custom cursor (desktop, fine pointer only) */
  .cursor { display: block; position: fixed; left: -6px; top: -6px; z-index: 90; width: 12px; height: 12px; pointer-events: none; opacity: 0; transition: opacity 200ms; }
  .cursor::after { content: ""; position: absolute; inset: 0; border-radius: 50%; background: var(--lime); box-shadow: 0 0 0 1px rgba(17,17,17,.6); transition: transform 220ms var(--ease-out), background-color 200ms; }
  .cursor.on { opacity: 1; }
  .cursor.big::after { transform: scale(3.2); background: transparent; box-shadow: inset 0 0 0 1px var(--lime); }
  /* #8 magnetic buttons: transform set from motion.js */
  .hero-ctas .btn { transition: transform 260ms var(--ease-out), background-color 180ms, color 180ms; }
  /* #9 press scale with spring back */
  .btn, .preset, .option, .msg-btn, .mic, .soc-link { transition: transform var(--t-micro) var(--ease-spring), background-color var(--t-micro), color var(--t-micro), border-color var(--t-micro); }
  .btn:active, .preset:active, .msg-btn:active, .soc-link:active { transform: scale(.97); transition-duration: 90ms; }
  /* #10 shine sweep across lime buttons on hover */
  .btn::after { content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 40%; background: rgba(255,255,255,.45); transform: translateX(-150%) skewX(-20deg); pointer-events: none; }
  .btn:hover::after { transform: translateX(320%) skewX(-20deg); transition: transform 650ms var(--ease-out); }
  .btn-ghost::after, [data-theme="light"] .btn::after, [data-theme="lime"] .btn::after, .plan-software .btn::after, .plan-care .btn::after, .form-foot .btn::after { background: rgba(220,245,34,.25); }
  /* #11 link underline draws from left to right */
  .foot-col a::after, .more-link::after, .offer-bar a::after, .prose a::after { content: ""; position: absolute; left: 0; right: 0; bottom: -2px; height: 2px; background: currentColor; transform: scaleX(0); transform-origin: left; transition: transform 240ms var(--ease-out); }
  .more-link, .offer-bar a, .prose a { position: relative; text-decoration: none; }
  .foot-col a:hover::after, .more-link:hover::after, .offer-bar a:hover::after, .prose a:hover::after { transform: scaleX(1); }
  .offer-bar a::after, .prose a::after, .more-link::after { transform: scaleX(1); opacity: .5; }
  .offer-bar a:hover::after, .prose a:hover::after, .more-link:hover::after { opacity: 1; }
  /* #12 focus ring grows in for keyboard users */
  :focus-visible { animation: focus-in 180ms var(--ease-out); }
  @keyframes focus-in { from { outline-width: 0; outline-offset: 0; } }

  /* ===== Launch banner ===== */
  /* #13 slides down on first load */
  .js .offer-bar { animation: bar-in 420ms var(--ease-out) both; }
  @keyframes bar-in { from { transform: translateY(-100%); opacity: 0; } }
  /* #14 spots count pulses every 6s */
  .spots { animation: spots 6s var(--ease-in-out) 2s infinite; }
  @keyframes spots { 0%, 88%, 100% { transform: none; } 93% { transform: scale(1.12); } }
  /* #15 shimmer travels across every 8s */
  .offer-bar::before { content: ""; position: absolute; top: 0; bottom: 0; left: 0; width: 30%; background: rgba(255,255,255,.4); transform: translateX(-120%) skewX(-20deg); animation: shimmer 8s var(--ease-in-out) 3s infinite; pointer-events: none; }
  @keyframes shimmer { 0%, 80% { transform: translateX(-120%) skewX(-20deg); } 100% { transform: translateX(460%) skewX(-20deg); } }
  /* #16 collapses when dismissed */
  .offer-bar.closing { animation: bar-out 400ms var(--ease-in-out) forwards; }
  @keyframes bar-out { to { transform: translateY(-100%); opacity: 0; } }

  /* ===== Hero ===== */
  /* #17 headline words rise from a mask, 60ms stagger */
  .js .split .wi { transform: translateY(105%); animation: word-up 640ms var(--ease-out) forwards; }
  .js .split .w:nth-child(1) .wi { animation-delay: 80ms; } .js .split .w:nth-child(2) .wi { animation-delay: 140ms; } .js .split .w:nth-child(3) .wi { animation-delay: 200ms; }
  .js .split .line + .line .w:nth-child(1) .wi { animation-delay: 260ms; } .js .split .line + .line .w:nth-child(2) .wi { animation-delay: 320ms; } .js .split .line + .line .w:nth-child(3) .wi { animation-delay: 380ms; }
  @keyframes word-up { to { transform: none; } }
  /* #18 "sleep" breathes with a soft lime glow (opacity on a duplicate layer) */
  .glow::after { content: ""; position: absolute; left: 0; right: 0; bottom: .06em; height: .1em; background: var(--lime); transform-origin: left; opacity: .25; animation: breathe 4.5s var(--ease-in-out) 1.4s infinite; pointer-events: none; will-change: opacity; }
  @keyframes breathe { 50% { opacity: 1; } }
  /* #19 subtext fades up after the headline */
  .js .hero-sub, .js .trust-line { animation: fade-up 560ms var(--ease-out) 380ms both; }
  .js .trust-line { animation-delay: 620ms; }
  @keyframes fade-up { from { opacity: 0; transform: translateY(16px); } }
  /* #20 CTAs pop in with a spring, 80ms apart */
  .js .hero-ctas .btn { animation: pop-in 520ms var(--ease-spring) both; }
  .js .hero-ctas .btn:nth-child(1) { animation-delay: 460ms; } .js .hero-ctas .btn:nth-child(2) { animation-delay: 540ms; } .js .hero-ctas .btn:nth-child(3) { animation-delay: 620ms; }
  @keyframes pop-in { from { opacity: 0; transform: scale(.85) translateY(10px); } }
  /* #21 mockup slides in from the right with a tilt that settles */
  .js .hero-stage { animation: stage-in 900ms var(--ease-out) 200ms both; }
  @keyframes stage-in { from { opacity: 0; transform: translateX(60px) rotate(3deg); } }
  /* #22 chat bubbles appear one at a time, typing dots first */
  .js .bubble, .js .chat-status { opacity: 0; transform: translateY(10px) scale(.98); transition: opacity 320ms var(--ease-out), transform 320ms var(--ease-out); }
  .js .bubble.show, .js .chat-status.show { opacity: 1; transform: none; }
  .typing i { animation: dots 1s var(--ease-in-out) infinite; }
  .typing i:nth-child(2) { animation-delay: 150ms; } .typing i:nth-child(3) { animation-delay: 300ms; }
  @keyframes dots { 0%, 60%, 100% { transform: none; opacity: .5; } 30% { transform: translateY(-4px); opacity: 1; } }
  /* #23 tap ripple on Confirm order */
  .chip-yes { transition: background-color 200ms, color 200ms; }
  .chip-yes.tapped .ripple { animation: ripple 600ms var(--ease-out); }
  @keyframes ripple { from { transform: scale(0); opacity: 1; } to { transform: scale(9); opacity: 0; } }
  /* #24 checkmark draws itself */
  .tick-draw circle, .tick-draw path { stroke-dasharray: 1; stroke-dashoffset: 1; }
  .b4.show .tick-draw circle { animation: draw 480ms var(--ease-out) forwards; }
  .b4.show .tick-draw path { animation: draw 320ms var(--ease-out) 300ms forwards; }
  @keyframes draw { to { stroke-dashoffset: 0; } }
  /* #25 tally pops when it counts up */
  .tally b.bump { animation: bump 480ms var(--ease-spring); }
  @keyframes bump { 40% { transform: scale(1.16); } }
  /* #26 bills drift on their own paths (smoother, lighter set) */
  .bill { animation: dance var(--d, 10s) var(--ease-in-out) var(--delay, 0s) infinite; will-change: transform; }
  @keyframes dance {
    0%, 100% { transform: translate(0, 0) rotate(var(--r0, -10deg)); }
    33% { transform: translate(var(--dx, 12px), -16px) rotate(var(--r1, 8deg)); }
    66% { transform: translate(calc(var(--dx, 12px) * -.7), -26px) rotate(var(--r0, -10deg)); }
  }
  /* #27 bills dodge the cursor (wrapper transform from motion.js) */
  .bill-wrap { transition: transform 500ms var(--ease-out); }
  .bill-pop { animation: payout 1.2s var(--ease-out) both; }
  @keyframes payout { 0% { transform: translate(-50%, -50%) scale(.3); opacity: 0; } 12% { opacity: 1; } 100% { transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy))) scale(1) rotate(var(--rot)); opacity: 0; } }
  /* #28 mockup tilts toward the cursor (max 6deg), eased */
  .hero-stage .chat { transition: transform 400ms var(--ease-out); }
  /* #29 clock colon blinks every second */
  .colon { animation: blink 1s steps(1) infinite; }
  @keyframes blink { 50% { opacity: 0; } }

  /* ===== Shared reveal system (#41 #55 #57 #63 #79 #87 #92 and section entrances) ===== */
  .js [data-reveal]:not(.is-in) { opacity: .35; transform: translateY(12px); }
  .js [data-reveal] { transition: opacity 300ms var(--ease-out), transform 300ms var(--ease-out); }
  .js .rv-child { opacity: 0; transform: translateY(18px); transition: opacity 480ms var(--ease-out), transform 480ms var(--ease-out); transition-delay: calc(var(--i, 0) * 70ms); }
  .js .is-in > .rv-child { opacity: 1; transform: none; }
  .js .items .rv-child { transition-delay: calc(var(--i, 0) * 50ms); } /* #63 50ms stagger */

  /* ===== Dial ===== */
  /* #32 ticks light up as the handle passes */
  .tick { transition: opacity 200ms, transform 200ms var(--ease-spring); }
  .tick.lit { transform: scaleY(1.25); }
  /* #33 lime trail brightens while dragging */
  .dial-fill { transition: opacity 200ms; }
  .dragging .dial-fill { opacity: 1; }
  /* #34 "Drag me" bounces twice, then fades out for good after first use */
  .drag-hint { animation: hint 700ms var(--ease-spring) 1.2s 2; transition: opacity 300ms; }
  @keyframes hint { 50% { transform: translateY(-8px); } }
  .dial-used .drag-hint { animation: none; }
  /* #37 night and day crossfade (opacity layer), #38 sun and moon swap */
  .dial-sec::after { transition: opacity 600ms var(--ease-in-out); }
  .dial-sec { transition: color 300ms; }
  .sun, .moon { transition: opacity 400ms var(--ease-out), transform 500ms var(--ease-spring); }
  .moon { transform: rotate(-90deg) scale(.5); }
  .dial-sec[data-mode="night"] .sun { transform: rotate(90deg) scale(.5); }
  .dial-sec[data-mode="night"] .moon { transform: none; }
  /* #40 handle snaps to each hour with a small overshoot */
  .dial input.snap::-webkit-slider-thumb { animation: snap 260ms var(--ease-spring); }
  .dial input.snap::-moz-range-thumb { animation: snap 260ms var(--ease-spring); }
  @keyframes snap { 40% { transform: scale(1.2); } }

  /* ===== Demos ===== */
  /* #41 frames rise with a soft shadow growing beneath */
  .sim-frame::after { content: ""; position: absolute; left: 6%; right: 6%; bottom: -14px; height: 28px; background: rgba(0,0,0,.18); filter: blur(14px); transform: scaleX(.6); opacity: 0; z-index: -1; transition: transform 700ms var(--ease-out) 200ms, opacity 700ms var(--ease-out) 200ms; }
  .demo.is-in .sim-frame::after { transform: none; opacity: 1; }
  /* #42 preset pressed state and ripple */
  .preset.rippling::after { animation: ripple 520ms var(--ease-out); }
  /* #47 "Test data" badge pulses slowly */
  .badge-live { animation: badge 3.2s var(--ease-in-out) infinite; }
  @keyframes badge { 50% { opacity: .65; } }
  /* #48 waveform while the receptionist speaks */
  .wave.speaking i { animation: wave 700ms var(--ease-in-out) infinite; }
  .wave i:nth-child(2) { animation-delay: 90ms; } .wave i:nth-child(3) { animation-delay: 180ms; } .wave i:nth-child(4) { animation-delay: 270ms; } .wave i:nth-child(5) { animation-delay: 120ms; } .wave i:nth-child(6) { animation-delay: 220ms; } .wave i:nth-child(7) { animation-delay: 40ms; }
  @keyframes wave { 50% { transform: scaleY(1); } }
  /* #49 mic pulses with rings while held */
  .mic.holding .mic-rings { animation: rings 900ms var(--ease-out) infinite; }
  @keyframes rings { from { transform: scale(1); opacity: .8; } to { transform: scale(1.35); opacity: 0; } }
  /* #50 tool call types with a blinking cursor */
  .typing-cursor::after { content: "\258C"; margin-left: 2px; animation: blink 800ms steps(1) infinite; }

  /* ===== About ===== */
  /* #53 lime wipe across the photo */
  .js .about-photo-wrap:not(.is-in) img { opacity: 0; }
  .js .about-photo-wrap.is-in img { animation: photo-show 1ms linear 380ms both; }
  @keyframes photo-show { from { opacity: 0; } to { opacity: 1; } }
  .js .about-photo-wrap.is-in .wipe { animation: wipe 900ms var(--ease-in-out) both; }
  @keyframes wipe { 0% { transform: translateX(-101%); } 45%, 55% { transform: none; } 100% { transform: translateX(101%); } }
  /* #56 online dot pulses during working hours */
  .status-dot.online .dot::after { animation: ping 2s var(--ease-out) infinite; }
  @keyframes ping { from { transform: scale(1); opacity: .7; } to { transform: scale(2.6); opacity: 0; } }
  /* #57 social icons pop in one after another */
  .js .about-copy.is-in .soc-link { animation: pop-in 420ms var(--ease-spring) both; }
  .about-copy .soc-link:nth-child(2) { animation-delay: 70ms; } .about-copy .soc-link:nth-child(3) { animation-delay: 140ms; } .about-copy .soc-link:nth-child(4) { animation-delay: 210ms; }
  /* #58 photo zooms slightly on hover */
  .about-photo img { transition: transform 600ms var(--ease-out); }
  .about-photo-wrap:hover img { transform: scale(1.06); }

  /* ===== Services ===== */
  /* #59 headline slides in from the left behind a mask */
  .js .mask-in { clip-path: inset(0 100% 0 0); transform: translateX(-24px); transition: clip-path 700ms var(--ease-out), transform 700ms var(--ease-out); }
  .js .is-in .mask-in { clip-path: inset(0 0 0 0); transform: none; }
  /* #60 category icons draw themselves */
  .js .draw svg [pathLength] { stroke-dasharray: 1; stroke-dashoffset: 1; }
  .js .cat.is-in .draw svg [pathLength] { animation: draw 900ms var(--ease-out) 150ms forwards; }
  /* #61 category headings scale in */
  .js .cat .cat-head h3 { transform: scale(.9); opacity: 0; transform-origin: left center; transition: transform 520ms var(--ease-spring), opacity 400ms; }
  .js .cat.is-in .cat-head h3 { transform: none; opacity: 1; }
  /* #64 row hover: lime bar grows on the left and the row nudges right */
  .svc-row::before { transition: transform 220ms var(--ease-out); }
  .svc-row:hover::before, .svc.open .svc-row::before { transform: scaleY(1); }
  .svc-row > * { transition: transform 200ms var(--ease-out); }
  .svc-row:hover > :not(.chev) { transform: translateX(4px); }
  /* #66 price wiggles on hover */
  .svc-row:hover .price-now { animation: wiggle 360ms var(--ease-in-out); }
  @keyframes wiggle { 25% { transform: rotate(-4deg); } 75% { transform: rotate(3deg); } }
  /* #67 panel opens via grid-template-rows (0fr to 1fr), #68 chevron rotates */
  .svc-panel { transition: grid-template-rows 360ms var(--ease-out); }
  .chev svg { transition: transform 260ms var(--ease-out); }
  .svc.open .chev svg { transform: rotate(180deg); }
  /* #69 tool chips fade in one by one, #70 the button slides up */
  .svc .tool-chips span, .svc .svc-actions { opacity: 0; transform: translateY(8px); transition: opacity 260ms var(--ease-out), transform 260ms var(--ease-out); }
  .svc.open .tool-chips span, .svc.open .svc-actions { opacity: 1; transform: none; }
  .svc.open .tool-chips span:nth-child(1) { transition-delay: 120ms; } .svc.open .tool-chips span:nth-child(2) { transition-delay: 180ms; } .svc.open .tool-chips span:nth-child(3) { transition-delay: 240ms; }
  .svc.open .svc-actions { transition-delay: 280ms; }

  /* ===== Process ===== */
  /* #71 line fill and #78 plane are scroll-linked from motion.js */
  .line-fill, .plane { will-change: transform; }
  /* #72 card lights up, #75 icon bounces when its card activates */
  .step { transition: background-color 300ms, transform 220ms var(--ease-out); }
  .step.active .step-icon { animation: bounce 520ms var(--ease-spring); }
  @keyframes bounce { 40% { transform: translateY(-8px) scale(1.08); } }
  .js .step:not(.active) .step-time { opacity: .7; }
  /* #77 hover lift with a shadow */
  .step:hover { transform: translateY(-4px); box-shadow: 0 14px 30px rgba(0,0,0,.35); }

  /* ===== Prices ===== */
  /* #79 cards rise in with a stagger */
  .js .plans .plan:not(.is-in) { opacity: 0; transform: translateY(28px); }
  .js .plans .plan { transition: opacity 560ms var(--ease-out), transform 560ms var(--ease-out); }
  .js .plans .plan:nth-child(2) { transition-delay: 90ms; } .js .plans .plan:nth-child(3) { transition-delay: 180ms; }
  /* #81 strike line draws, then the new price drops in */
  .js .plan .price-was::after { transform: scaleX(0); }
  .js .plan.is-in .price-was::after { transform: scaleX(1); transition: transform 360ms var(--ease-out) 300ms; }
  .js .plan .price-was + .price-now { opacity: 0; transform: translateY(-12px); }
  .js .plan.is-in .price-was + .price-now { opacity: 1; transform: none; transition: opacity 360ms var(--ease-out) 620ms, transform 420ms var(--ease-spring) 620ms; }
  /* #82 "50% off" badge spins in with a spring */
  .js .plan .badge-off { transform: rotate(-180deg) scale(0); }
  .js .plan.is-in .badge-off { transform: none; transition: transform 700ms var(--ease-spring) 500ms; }
  /* #83 recommended card: slow rotating lime border (desktop only) */
  @media (min-width: 1001px) {
    .plan-rec::before { content: ""; position: absolute; inset: -60%; z-index: -2; background: conic-gradient(from 0deg, var(--lime), transparent 25%, transparent 50%, var(--lime) 75%, transparent); animation: spin 8s linear infinite; }
    .plan-rec::after { content: ""; position: absolute; inset: 3px; z-index: -1; background: var(--ink); }
    @keyframes spin { to { transform: rotate(360deg); } }
  }
  /* #84 hover lift and tilt toward the cursor (transform from motion.js on desktop) */
  .plan { transition: transform 300ms var(--ease-out); }
  .plan:hover { transform: translateY(-6px); }
  /* #85 included checkmarks draw in one by one */
  .js .plan-inc svg [pathLength] { stroke-dasharray: 1; stroke-dashoffset: 1; }
  .js .plan.is-in .plan-inc svg [pathLength] { animation: draw 380ms var(--ease-out) forwards; }
  .js .plan.is-in .plan-inc li:nth-child(1) svg [pathLength] { animation-delay: 300ms; } .js .plan.is-in .plan-inc li:nth-child(2) svg [pathLength] { animation-delay: 400ms; }
  .js .plan.is-in .plan-inc li:nth-child(3) svg [pathLength] { animation-delay: 500ms; } .js .plan.is-in .plan-inc li:nth-child(4) svg [pathLength] { animation-delay: 600ms; }
  /* #86 "/month" slides in after the price */
  .js .plan .price-unit { opacity: 0; transform: translateX(-8px); }
  .js .plan.is-in .price-unit { opacity: 1; transform: none; transition: opacity 300ms var(--ease-out) 900ms, transform 300ms var(--ease-out) 900ms; }

  /* ===== FAQ ===== */
  /* #88 accordion via grid-template-rows */
  .qa-panel { transition: grid-template-rows 340ms var(--ease-out); }
  /* #89 plus rotates into an X */
  .qa-icon { transition: transform 260ms var(--ease-out); }
  .qa.open .qa-icon { transform: rotate(45deg); }
  /* #90 answer text fades in after the panel opens */
  .qa-inner p { opacity: 0; transition: opacity 200ms; }
  .qa.open .qa-inner p { opacity: 1; transition: opacity 300ms var(--ease-out) 140ms; }
  /* #91 question shifts right and turns lime on hover */
  .qa-q { transition: transform 200ms var(--ease-out), color 200ms; }
  .qa-btn:hover .qa-q { transform: translateX(4px); color: var(--lime); }

  /* ===== Contact and form ===== */
  /* #92 form fields slide up in sequence */
  .js .contact-form:not(.is-in) > * { opacity: 0; transform: translateY(16px); }
  .js .contact-form > * { transition: opacity 440ms var(--ease-out), transform 440ms var(--ease-out); }
  .js .contact-form > :nth-child(2) { transition-delay: 60ms; } .js .contact-form > :nth-child(3) { transition-delay: 120ms; } .js .contact-form > :nth-child(4) { transition-delay: 180ms; } .js .contact-form > :nth-child(5) { transition-delay: 240ms; } .js .contact-form > :nth-child(n+6) { transition-delay: 300ms; }
  /* #93 floating labels */
  .fl label { transition: transform 200ms var(--ease-out), color 200ms; }
  /* #94 valid check draws in */
  .ok { transition: opacity 200ms; }
  .ok svg [pathLength] { stroke-dasharray: 1; stroke-dashoffset: 1; }
  .field.valid .ok svg [pathLength] { animation: draw 320ms var(--ease-out) forwards; }
  /* #95 submit turns into a spinner, then morphs into a checkmark */
  .submit-label, .spinner, .submit-check { transition: opacity 180ms, transform 220ms var(--ease-spring); }
  .submit.is-pending .submit-label, .submit.is-done .submit-label { opacity: 0; transform: scale(.8); }
  .submit.is-pending .spinner { opacity: 1; animation: spin 700ms linear infinite; }
  .submit-check path { stroke-dasharray: 1; stroke-dashoffset: 1; }
  .submit.is-done .submit-check { opacity: 1; }
  .submit.is-done .submit-check path { animation: draw 360ms var(--ease-out) forwards; }
  @keyframes spin { to { transform: rotate(360deg); } }
  /* #96 success message slides in (confetti from motion.js) */
  .form-status[data-kind="success"] { animation: fade-up 420ms var(--ease-out); }
  /* #97 contact cards lift and the icon nudges up */
  .option svg { transition: transform 220ms var(--ease-spring); }
  .option:hover { transform: translateY(-4px); }
  .option:hover svg { transform: translateY(-3px); }
  /* #98 floating WhatsApp bounces in after 30% scroll, single wiggle after 20s */
  .wa-float { transition: transform 420ms var(--ease-spring); }
  .wa-float.wiggle { animation: fab-wiggle 700ms var(--ease-in-out) 1; }
  @keyframes fab-wiggle { 20% { rotate: -14deg; } 40% { rotate: 12deg; } 60% { rotate: -8deg; } 80% { rotate: 4deg; } }

  /* ===== Footer ===== */
  /* #99 wordmark reveals letter by letter */
  .js .foot-word .ch { transform: translateY(105%); transition: transform 520ms var(--ease-out); transition-delay: calc(var(--i, 0) * 22ms); }
  .js .foot.is-in .foot-word .ch { transform: none; }

  /* ===== Library (#100) ===== */
  .lib-flash { animation: lib-flash 600ms var(--ease-out); }
  @keyframes lib-flash { 0% { box-shadow: inset 0 0 0 3px var(--lime); } 100% { box-shadow: inset 0 0 0 3px transparent; } }
}

/* Mobile: no cursor effects, parallax or heavy loops under 768px (tap feedback stays) */
@media (max-width: 767px) {
  .cursor { display: none !important; }
  .bill { animation-duration: 14s; }
  .glow::after { display: none; }
}
/* Reduced motion: final state, nothing loops, nothing moves */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .bills, .bill-pop, .cursor, .glow::after, .typing, .offer-bar::before { display: none !important; }
  .bubble, .chat-status { opacity: 1 !important; transform: none !important; }
  .drag-hint { display: none; }
}

/* ---------- Text reveal (words rise from a mask, blur to sharp, lime sweep) ---------- */
@media (prefers-reduced-motion: no-preference) {
  .tx { position: relative; }
  .tx-m { display: inline-block; overflow: clip; overflow-clip-margin: .3em; vertical-align: top; padding: 0 .04em .22em; margin: 0 -.04em -.22em; }
  .tx-w { display: inline-block; transform: translateY(12px); opacity: .35; }
  .tx-in .tx-w { animation: tx-rise 400ms cubic-bezier(.16,1,.3,1) both; animation-delay: calc(var(--wi, 0) * 30ms); }
  @keyframes tx-rise {
    0% { transform: translateY(12px); opacity: .35; }
    100% { transform: none; opacity: 1; }
  }
  .tx::after { content: ""; position: absolute; left: 0; bottom: .02em; height: .12em; width: 100%; background: var(--lime); transform: scaleX(0); transform-origin: left; pointer-events: none; }
  .tx-in::after { animation: tx-sweep 1100ms cubic-bezier(.65,0,.35,1) 200ms both; }
  @keyframes tx-sweep { 0% { transform: scaleX(0); transform-origin: left; } 50% { transform: scaleX(1); transform-origin: left; } 50.01% { transform-origin: right; } 100% { transform: scaleX(0); transform-origin: right; } }
}
