/* Mortgage Rail motion system (shared by every page; loaded after the page styles).
   Curves: ease-out-quart for moves and fades (JammJar's page curve), ease-out-expo for anything that grows or morphs,
   ease-in-out for the pointer. No bounce, no elastic. Pre-states only apply while a scene is armed and playing. */
:root{
  --e-out:cubic-bezier(.25,1,.5,1);
  --e-expo:cubic-bezier(.16,1,.3,1);
  --e-io:cubic-bezier(.65,0,.35,1);
  --skel:rgba(17,17,17,.07);
  --skel-hi:rgba(17,17,17,.13);
}
.mx-snap,.mx-snap *,.mx-snap *::before,.mx-snap *::after{transition:none!important}
[data-scene]{position:relative}

/* entrances: the finished frame is the default; .mx-armed + :not(.on) is the pre-state */
.fx-pop,.fx-rise,.fx-drop,.fx-left,.fx-right,.fx-fade,.fx-grow{transition:opacity .45s var(--e-out),transform .6s var(--e-expo)}
.mx-armed .fx-pop:not(.on){opacity:0;transform:scale(.86)}
.mx-armed .fx-grow:not(.on){opacity:0;transform:scale(.5)}
.mx-armed .fx-rise:not(.on){opacity:0;transform:translateY(14px)}
.mx-armed .fx-drop:not(.on){opacity:0;transform:translateY(-16px)}
.mx-armed .fx-left:not(.on){opacity:0;transform:translateX(-16px)}
.mx-armed .fx-right:not(.on){opacity:0;transform:translateX(16px)}
.mx-armed .fx-fade:not(.on){opacity:0}
.fx-wipe{transition:clip-path .6s var(--e-out)}
.mx-armed .fx-wipe:not(.on){clip-path:inset(0 100% 0 0)}
.fx-wipe-y{transition:clip-path .6s var(--e-out)}
.mx-armed .fx-wipe-y:not(.on){clip-path:inset(0 0 100% 0)}
/* svg line drawing: give the path pathLength="1" */
.fx-draw{stroke-dasharray:1 1;transition:stroke-dashoffset .6s var(--e-out)}
.mx-armed .fx-draw:not(.on){stroke-dashoffset:1}

/* skeleton -> value: a shimmering placeholder, then the real text pulls into focus */
.fx-res{position:relative;transition:color .35s var(--e-out)}
.fx-res::after{content:"";position:absolute;left:0;top:50%;height:.7em;width:min(100%,8.5em);transform:translateY(-50%);border-radius:4px;background:linear-gradient(90deg,var(--skel) 30%,var(--skel-hi) 50%,var(--skel) 70%) 0 0/300% 100%;opacity:0;transition:opacity .25s var(--e-out);pointer-events:none}
.mx-armed .fx-res:not(.on){color:transparent!important}
.mx-armed .fx-res:not(.on) *{visibility:hidden}
.mx-armed .fx-res:not(.on)::after{opacity:1;animation:mx-shimmer 1.3s linear infinite}
.mx-play .fx-res.on{animation:mx-focus .55s var(--e-out)}
@keyframes mx-shimmer{from{background-position:100% 0}to{background-position:-50% 0}}
@keyframes mx-focus{from{filter:blur(5px);opacity:.25}to{filter:none;opacity:1}}

/* highlight sweep for the figure that matters */
.fx-mark{background:linear-gradient(var(--mk,rgba(43,143,217,.16)),var(--mk,rgba(43,143,217,.16))) 0 0/100% 100% no-repeat;border-radius:4px;padding:0 .18em;margin:0 -.18em;transition:background-size .55s var(--e-out)}
.mx-armed .fx-mark:not(.on){background-size:0% 100%}

/* strike-through drawn across a line that stops fitting */
.fx-strike{position:relative;color:#8A919C;transition:color .4s var(--e-out)}
.mx-armed .fx-strike:not(.on){color:inherit}
.fx-strike::after{content:"";position:absolute;left:0;right:0;top:52%;height:1.5px;background:#B42318;transform-origin:left;transition:transform .5s var(--e-out)}
.mx-armed .fx-strike:not(.on)::after{transform:scaleX(0)}

/* rows that leave: collapse with grid rows, never by animating height */
.fx-gone{display:grid;grid-template-rows:0fr;opacity:0;transition:grid-template-rows .55s var(--e-out),opacity .35s var(--e-out)}
.fx-gone>*{min-height:0;overflow:hidden}
.mx-armed .fx-gone:not(.on){grid-template-rows:1fr;opacity:1}
/* rows that arrive and push the list down */
.fx-open{display:grid;grid-template-rows:1fr;transition:grid-template-rows .6s var(--e-expo),opacity .4s var(--e-out)}
.fx-open>*{min-height:0;overflow:hidden}
.mx-armed .fx-open:not(.on){grid-template-rows:0fr;opacity:0}

/* streaming text: words arrive one after another */
.mx-armed [data-type] .mx-w{opacity:0;filter:blur(3px)}
.mx-armed [data-type] .mx-w.on{opacity:1;filter:none;transition:opacity .22s var(--e-out),filter .3s var(--e-out)}

/* status ticks: wait -> working -> done (the .t3 primitive on every page) */
.t3{position:relative;transition:background-color .3s var(--e-out),box-shadow .3s var(--e-out)}
.t3 .ic{transition:transform .5s var(--e-expo),opacity .2s var(--e-out)}
.t3[data-st=wait],.t3[data-st=spin]{background:#fff!important;box-shadow:inset 0 0 0 1.5px rgba(17,17,17,.2)!important}
.t3[data-st=wait] .ic,.t3[data-st=spin] .ic{transform:scale(0);opacity:0}
.t3[data-st=spin]{box-shadow:inset 0 0 0 1.5px rgba(43,143,217,.18)!important}
.t3[data-st=spin]::after,.mx-spin{content:"";position:absolute;inset:0;border-radius:99px;border:1.5px solid transparent;border-top-color:var(--glu);border-right-color:var(--glu);animation:mx-rot .75s linear infinite}
.mx-spin{position:relative;display:inline-block;width:12px;height:12px;flex:none;inset:auto}
@keyframes mx-rot{to{transform:rotate(360deg)}}

/* chips that change state: children marked data-v show for that state; unmarked children are the finished look */
.mx-sw{transition:background-color .3s var(--e-out),color .3s var(--e-out)}
.mx-sw>[data-v]{display:none}
.mx-sw[data-st]>:not([data-v]):not(.mx-cursor){display:none}
.mx-sw[data-st=gry]>[data-v=gry],.mx-sw[data-st=amb]>[data-v=amb],.mx-sw[data-st=blu]>[data-v=blu],.mx-sw[data-st=red]>[data-v=red],.mx-sw[data-st=grn]>[data-v=grn],.mx-sw[data-st=spin]>[data-v=spin]{display:inline-flex;align-items:center;gap:5px}
.chp.mx-sw[data-st=gry],.chp.mx-sw[data-st=spin]{background:var(--panel);color:var(--ink2)}
.chp.mx-sw[data-st=amb]{background:var(--pend-t);color:var(--pend-x)}
.chp.mx-sw[data-st=blu]{background:var(--glu-w);color:#1C6FB0}
.chp.mx-sw[data-st=red]{background:#FDECEA;color:#B42318}
.chp.mx-sw[data-st=grn]{background:var(--done-t);color:var(--done)}
.mx-sw [data-v=spin] .mx-spin{width:10px;height:10px}
.chp.mx-sw.on,.chp.mx-sw[data-st]{animation:none}
.mx-play .chp.mx-sw:not([data-st]){animation:mx-chip .45s var(--e-expo)}
@keyframes mx-chip{from{transform:scale(.8)}to{transform:none}}

/* waiting: a soft amber ring that keeps breathing while the scene is on screen */
.mx-pulse{position:relative}
.mx-pulse::before{content:"";position:absolute;inset:-3px;border-radius:inherit;box-shadow:0 0 0 0 rgba(176,100,26,.35);animation:mx-breathe 2.2s var(--e-out) infinite;animation-play-state:paused;pointer-events:none}
.mx-in .mx-pulse::before,.mx-pulse.mx-in::before{animation-play-state:running}
@keyframes mx-breathe{0%{box-shadow:0 0 0 0 rgba(176,100,26,.35)}70%,100%{box-shadow:0 0 0 7px rgba(176,100,26,0)}}
.mx-live{width:7px;height:7px;border-radius:9px;background:var(--done);display:inline-block;flex:none;box-shadow:0 0 0 0 rgba(31,122,77,.4);animation:mx-live 1.8s var(--e-out) infinite;animation-play-state:paused}
.mx-in .mx-live{animation-play-state:running}
@keyframes mx-live{0%{box-shadow:0 0 0 0 rgba(31,122,77,.45)}70%,100%{box-shadow:0 0 0 6px rgba(31,122,77,0)}}

/* the demo pointer */
.mx-cursor{position:absolute;left:var(--x,60%);top:var(--y,60%);width:22px;height:22px;z-index:6;pointer-events:none;opacity:0;transition:left .6s var(--e-io),top .6s var(--e-io),opacity .3s var(--e-out)}
.mx-cursor svg{width:22px;height:22px;display:block;filter:drop-shadow(0 2px 3px rgba(0,0,0,.28))}
.mx-armed .mx-cursor{opacity:1}
.mx-armed .mx-cursor.gone{opacity:0}
.mx-cursor::after{content:"";position:absolute;left:-10px;top:-10px;width:24px;height:24px;border-radius:99px;background:rgba(43,143,217,.35);transform:scale(0);opacity:0}
.mx-cursor.click::after{animation:mx-click .5s var(--e-out)}
@keyframes mx-click{0%{transform:scale(.2);opacity:1}100%{transform:scale(1.6);opacity:0}}
.mx-press{transition:transform .15s var(--e-out)}
.mx-press[data-st=down]{transform:scale(.95)}

/* a thin sweep under the header while the system is working */
.mx-sweep{position:absolute;left:14px;right:14px;top:0;height:2px;overflow:hidden;border-radius:2px;opacity:0;transition:opacity .35s var(--e-out);pointer-events:none}
.mx-sweep::after{content:"";position:absolute;top:0;bottom:0;left:0;width:35%;background:linear-gradient(90deg,transparent,var(--glu),transparent);animation:mx-sweep 1.15s var(--e-io) infinite}
.mx-sweep[data-st=run]{opacity:1}
@keyframes mx-sweep{from{transform:translateX(-100%)}to{transform:translateX(300%)}}

/* the loop: a quick fade of the moving parts, then the scene re-arms */
.mx-rewind [data-at],.mx-rewind [data-steps]:not(.t3),.mx-rewind .mx-cursor{opacity:0!important;transition:opacity .3s var(--e-out)!important}

/* data packets travelling along a path (offset-path set inline as --p) */
.mx-pk{position:absolute;left:0;top:0;width:8px;height:8px;margin:-4px 0 0 -4px;border-radius:9px;background:var(--glu);box-shadow:0 0 0 3px rgba(43,143,217,.18);offset-path:var(--p);offset-rotate:0deg;offset-distance:0%;opacity:0;animation:mx-run var(--pd,2.4s) var(--e-io) infinite;animation-delay:var(--pdl,0s);animation-play-state:paused}
.mx-in .mx-pk{animation-play-state:running}
@keyframes mx-run{0%{offset-distance:0%;opacity:0}12%{opacity:1}85%{opacity:1}100%{offset-distance:100%;opacity:0}}

/* hover and press feedback (0.2s, as measured) */
@media (hover:hover){
  .btn .ic{transition:transform .2s var(--e-out)}
  .btn:hover .ic{transform:translateX(3px)}
  .comp .top .ic,.cs-cta .ic,.icap a .ic{transition:transform .2s var(--e-out),color .2s var(--e-out)}
  .comp:hover .top .ic,.cs-cta:hover .ic,.icap a:hover .ic{transform:translateX(4px);color:var(--ink)}
  .comp,.csc{transition:background .2s var(--e-out),box-shadow .2s var(--e-out),transform .25s var(--e-out)}
  a.comp:hover,.csc:hover{transform:translateY(-2px)}
}

@media (prefers-reduced-motion:reduce){
  .mx-pk,.mx-pulse::before,.mx-live,.t3[data-st=spin]::after,.mx-spin{animation:none!important}
  .mx-cursor{display:none}
}

/* pop message: drops in when something new arrives, can change once or twice, then leaves.
   <div class="mx-toast" data-steps="0: 500:t1 2200:t2 3800:"><div data-v="t1">...</div><div data-v="t2">...</div></div> */
.mx-toast{position:absolute;top:14px;right:14px;z-index:7;width:min(320px,calc(100% - 28px));background:#fff;border-radius:12px;padding:12px 14px;box-shadow:0 0 0 1px rgba(17,17,17,.06),0 18px 36px -14px rgba(10,30,60,.38);opacity:0;transform:translateY(-10px) scale(.94);transform-origin:top right;pointer-events:none;transition:opacity .35s var(--e-out),transform .6s var(--e-expo);text-align:left}
.mx-toast[data-st]{opacity:1;transform:none}
.mx-toast>[data-v]{display:none;gap:10px;align-items:flex-start}
.mx-toast[data-st=t1]>[data-v=t1],.mx-toast[data-st=t2]>[data-v=t2],.mx-toast[data-st=t3]>[data-v=t3]{display:flex;animation:mx-swap .45s var(--e-out)}
.mx-toast .tic{width:30px;height:30px;border-radius:9px;display:grid;place-items:center;flex:none;background:var(--glu-w);color:#1C6FB0;position:relative}
.mx-toast .tic.ok{background:var(--done-t);color:var(--done)}.mx-toast .tic.amb{background:var(--pend-t);color:var(--pend-x)}.mx-toast .tic.red{background:#FDECEA;color:#B42318}
.mx-toast .tic .ic{width:16px;height:16px;stroke-width:2}
.mx-toast .tic::after{content:"";position:absolute;inset:0;border-radius:inherit;animation:mx-ping .6s var(--e-out) .15s 2}
.mx-toast .tt{display:flex;flex-direction:column;gap:2px;min-width:0}
.mx-toast .tt b{font:600 13px/1.3 var(--body);color:var(--ink)}
.mx-toast .tt small{font:400 12px/1.35 var(--body);color:var(--ink2)}
@keyframes mx-swap{from{opacity:0;transform:translateY(4px)}to{opacity:1;transform:none}}
@keyframes mx-ping{0%{box-shadow:0 0 0 0 rgba(43,143,217,.45)}100%{box-shadow:0 0 0 10px rgba(43,143,217,0)}}

/* travelling note: follows the row being worked on, says what is being checked, then what was found.
   Rows (or their tick) carry data-msg, data-done and data-kind (ok | amb | red | blu); the scene holds one .mx-note. */
.mx-note{position:absolute;left:0;top:0;z-index:7;max-width:min(320px,calc(100% - 24px));display:flex;gap:9px;align-items:center;padding:9px 12px 9px 10px;border-radius:11px;background:#1D2330;color:#fff;font:400 12.5px/1.35 var(--body);box-shadow:0 14px 28px -12px rgba(10,20,40,.5);opacity:0;transform:translate(var(--tx,16px),calc(var(--ty,60px) - 6px)) scale(.96);transform-origin:top left;pointer-events:none;transition:transform .45s var(--e-out),opacity .3s var(--e-out),clip-path .45s var(--e-out);text-align:left}
.mx-note.mx-jump{transition:none!important} /* 2 Oct (D-136): the note moves on the same 0.45 s curve as the gap under its row, and reappears in place */
.mx-note.show{opacity:1;transform:translate(var(--tx,16px),var(--ty,60px))}
/* below its row: a small pointer up at the row; beside its text: no pointer */
.mx-note::before{content:"";position:absolute;left:18px;top:-5px;width:10px;height:10px;background:inherit;transform:rotate(45deg);border-radius:2px}
.mx-note[data-mode=in]::before{display:none}
.mx-note .fi{width:18px;height:18px;border-radius:99px;flex:none;position:relative;display:grid;place-items:center}
.mx-note[data-k=work] .fi{border:2px solid rgba(255,255,255,.25);border-top-color:#8CC8F2;animation:mx-rot .75s linear infinite}
.mx-note[data-k=ok] .fi{background:#2E9E62}.mx-note[data-k=amb] .fi{background:#D98A2B}.mx-note[data-k=red] .fi{background:#D9443B}.mx-note[data-k=blu] .fi{background:#2B8FD9}
.mx-note[data-k=ok] .fi::after,.mx-note[data-k=blu] .fi::after{content:"";width:7px;height:4px;border-left:2px solid #fff;border-bottom:2px solid #fff;transform:translateY(-1px) rotate(-45deg)}
.mx-note[data-k=amb] .fi::after,.mx-note[data-k=red] .fi::after{content:"!";font:700 11px/1 var(--body);color:#fff}
.mx-note .tt.swap{animation:mx-swap .4s var(--e-out)}
@media (prefers-reduced-motion:reduce){.mx-toast,.mx-note{display:none}}

/* ===== 2 Oct (D-136): the hero film's motion carried to every page. Springs for things that land (chips, ticks, badges,
   stepper circles, pop messages), at the researched pace (moves 0.45–0.5 s; checks/2026-10-02-film-reading-speed.md).
   CSS linear() springs, guarded so older browsers keep the eased curves. ===== */
:root{--e-spring:var(--e-expo);--e-pop:var(--e-expo)}
@supports (transition-timing-function: linear(0, 1)) {
  :root{--e-spring:linear(0, 0.279 3.6%, 0.525 7.1%, 0.722 10.7%, 0.868 14.3%, 0.966 17.9%, 1.026 21.4%, 1.056 25.0%, 1.067 28.6%, 1.064 32.1%, 1.055 35.7%, 1.043 39.3%, 1.031 42.9%, 1.020 46.4%, 1.012 50.0%, 1.006 53.6%, 1.001 57.1%, 0.999 60.7%, 0.998 64.3%, 0.997 67.9%, 0.997 71.4%, 0.998 75.0%, 0.998 78.6%, 0.999 82.1%, 0.999 85.7%, 0.999 89.3%, 1.000 92.9%, 1.000 96.4%, 1);--e-pop:linear(0, 0.266 3.6%, 0.574 7.1%, 0.852 10.7%, 1.061 14.3%, 1.185 17.9%, 1.233 21.4%, 1.221 25.0%, 1.173 28.6%, 1.110 32.1%, 1.049 35.7%, 1.000 39.3%, 0.968 42.9%, 0.953 46.4%, 0.952 50.0%, 0.960 53.6%, 0.973 57.1%, 0.986 60.7%, 0.997 64.3%, 1.005 67.9%, 1.009 71.4%, 1.010 75.0%, 1.009 78.6%, 1.007 82.1%, 1.004 85.7%, 1.001 89.3%, 0.999 92.9%, 0.998 96.4%, 1)}
  .fx-pop,.fx-grow{transition:opacity .3s var(--e-out),transform .5s var(--e-pop)}
  .fx-rise,.fx-drop,.fx-left,.fx-right{transition:opacity .35s var(--e-out),transform .55s var(--e-spring)}
  .t3 .ic{transition:transform .5s var(--e-pop),opacity .2s var(--e-out)}
  .mx-toast{transition:opacity .3s var(--e-out),transform .55s var(--e-spring)}
  .ap-steps .ap-st .c{transition:background-color .35s var(--e-out),transform .5s var(--e-pop)}
  .ap-steps .ap-st .c .bdg{transition:transform .45s var(--e-pop)}
}

/* 2 Oct (D-136): page-load entrances start hidden, so the finished frame never flashes before motion.js starts the rise.
   motion.js adds .mx-entered as each rise begins. Without the engine they simply appear after 1.6 s; reduced motion shows them at once. */
[data-enter]:not(.mx-entered),[data-enter-x]:not(.mx-entered),[data-entrance]>:not(.mx-entered){opacity:0;animation:mx-show 0s linear 1.6s forwards}
@keyframes mx-show{to{opacity:1}}
@media (prefers-reduced-motion:reduce){[data-enter],[data-enter-x],[data-entrance]>*{opacity:1!important;animation:none!important}}
