/* Bon Voyage — the five send-off modules, verbatim from mockups/r7-parts (round 4–7). Every selector is prefixed so-<name>-. */
/* ─── send-off: the loop (round 4, ported from 07) ───
   Everything is prefixed so-loop-. Timings live in sendoff-loop.js (one table), so a
   seek can freeze every element exactly; this file only holds the look and the keyframes. */

.so-loop-root{position:absolute;inset:0;pointer-events:none}

/* the approved photo, drawn where the host had it, then shrunk into its own centre */
.so-loop-photo{position:absolute;border-radius:26px;overflow:hidden;
  background:radial-gradient(120% 90% at 50% 30%,color-mix(in srgb,var(--paper) 18%,var(--ink)) 0%,var(--ink) 100%);
  box-shadow:0 26px 60px rgba(0,0,0,.55),0 0 0 1px color-mix(in srgb,var(--paper) 14%,transparent)}
.so-loop-rcpt{position:absolute;inset:0;display:flex;align-items:center;justify-content:center}

/* the faint map: 07's ground. Masked (inline, per play) so the status bar and the pill stay the host's */
.so-loop-map{position:absolute;inset:0;opacity:0;
  background:radial-gradient(110% 70% at 50% 40%,var(--ink-2,#17293D),var(--ink))}

.so-loop-chart{position:absolute;left:0;top:0;overflow:visible}
.so-loop-grat{fill:none;stroke:color-mix(in srgb,var(--paper) 7%,transparent);stroke-width:1}
.so-loop-route{fill:none;stroke:color-mix(in srgb,var(--paper) 72%,transparent);stroke-width:2;stroke-linecap:round;stroke-dasharray:7 7}
.so-loop-mask{fill:none;stroke:#fff;stroke-width:14;stroke-linecap:round;stroke-dasharray:1010 1010;stroke-dashoffset:1010}
.so-loop-origin{fill:var(--lime)}
.so-loop-ring{fill:none;stroke:color-mix(in srgb,var(--lime) 60%,transparent);stroke-width:1.5;transform-box:fill-box;transform-origin:center;opacity:0}

.so-loop-wtext{font:italic 800 40px var(--display);letter-spacing:-.01em;fill:var(--paper);fill-opacity:0;stroke:var(--paper);stroke-width:1.1;stroke-dasharray:160;stroke-dashoffset:160;paint-order:stroke}
.so-loop-wrect{transform-box:fill-box;transform-origin:left center;transform:scaleX(0)}
.so-loop-swash{fill:var(--lime);transform-box:fill-box;transform-origin:left center;transform:scaleX(0)}
.so-loop-wsub{font:400 13px var(--body);fill:color-mix(in srgb,var(--paper) 86%,transparent);opacity:0}
.so-loop-pen{fill:var(--lime);opacity:0}

/* the ticket. Its wrapper cuts a slot the shape of the Receipts tab, so it posts itself in */
.so-loop-slot{position:absolute;inset:0}
.so-loop-tk{position:absolute;left:0;top:0;width:54px;height:34px;offset-rotate:auto;offset-anchor:50% 50%;offset-distance:0%;opacity:0;filter:drop-shadow(0 8px 12px rgba(0,0,0,.45))}
.so-loop-tkb{position:absolute;inset:0;border-radius:6px;background:var(--white,#fff);display:flex;overflow:hidden}
.so-loop-tkb i{width:15px;background:var(--lime);border-right:1.5px dashed color-mix(in srgb,var(--ink) 35%,transparent);flex:none}
.so-loop-tkb span{flex:1;display:flex;flex-direction:column;justify-content:center;gap:4px;padding:0 6px}
.so-loop-tkb u{display:block;height:3px;border-radius:2px;background:var(--hair-2,#C9C1BA)}
.so-loop-tkb u:nth-child(2){width:70%}
.so-loop-tkb u:nth-child(3){width:45%;background:var(--ink)}

/* the finished state, for prefers-reduced-motion */
.so-loop-calm .so-loop-wtext{fill-opacity:1;stroke-dashoffset:0}
.so-loop-calm .so-loop-wrect,.so-loop-calm .so-loop-swash{transform:scaleX(1)}
.so-loop-calm .so-loop-wsub{opacity:1}

/* ─── keyframes (07's, renamed) ─── */
@keyframes so-loop-shrink{0%{transform:scale(1);opacity:1;border-radius:26px}70%{opacity:1}100%{transform:scale(.1);opacity:0;border-radius:80px}}
@keyframes so-loop-fadein{from{opacity:0}to{opacity:1}}
@keyframes so-loop-fadeaway{from{opacity:1}to{opacity:0}}
@keyframes so-loop-draw{from{stroke-dashoffset:1010}to{stroke-dashoffset:0}}
@keyframes so-loop-ring{0%{opacity:.9;transform:scale(.4)}100%{opacity:0;transform:scale(3.2)}}
@keyframes so-loop-fly{from{offset-distance:0%}to{offset-distance:100%}}
@keyframes so-loop-tklife{0%,14%{opacity:0;transform:scale(.3)}25%{opacity:1;transform:scale(1.12)}30%{transform:scale(1)}93%{opacity:1;transform:scale(1)}100%{opacity:0;transform:scale(.25)}}
@keyframes so-loop-reveal{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@keyframes so-loop-ink{from{stroke-dashoffset:160}to{stroke-dashoffset:0}}
@keyframes so-loop-fillin{from{fill-opacity:0}to{fill-opacity:1}}
@keyframes so-loop-pen{0%{opacity:0;transform:translateX(0)}8%{opacity:1}90%{opacity:1}100%{opacity:0;transform:translateX(222px)}}
@keyframes so-loop-calm{0%{opacity:0}25%{opacity:1}75%{opacity:1}100%{opacity:0}}

/* ─── send-off: "To the stars", in the style of 07 "The loop" ───
   The photo folds into an ember, shoots up as a comet through a paper starfield shedding
   sparkles, bursts into a new star, is joined into a little constellation with 07's dashed
   strokes while "Bon voyage!" twinkles in, then the star travels a dashed route down and
   shrinks into the Receipts tab. Ink ground, paper stars, lime only as glow and accent. */

.so-stars-root{position:absolute;inset:0;overflow:hidden;pointer-events:none;font-family:var(--body);color:var(--paper)}
.so-stars-root > *{position:absolute}

/* the ground: 07's ink, with the faintest blue lift where the sky is */
.so-stars-ground{inset:0;opacity:0;background:radial-gradient(120% 62% at 50% 22%,rgba(91,130,180,.16) 0%,rgba(91,130,180,0) 70%),var(--ink)}

/* chart: 07's graticule, the constellation lines, the route down */
.so-stars-chart{left:0;top:0;width:390px;height:844px;overflow:visible}
.so-stars-grat{fill:none;stroke:rgba(246,241,238,.07);stroke-width:1}
.so-stars-gratg{opacity:0}
.so-stars-join{fill:none;stroke:rgba(246,241,238,.58);stroke-width:1.5;stroke-linecap:round;stroke-dasharray:5 6}
.so-stars-route{fill:none;stroke:rgba(246,241,238,.72);stroke-width:2;stroke-linecap:round;stroke-dasharray:7 7}
.so-stars-reveal{fill:none;stroke:#fff;stroke-width:16;stroke-linecap:butt;stroke-dasharray:100.5 100.5;stroke-dashoffset:100.5}
.so-stars-ghost{fill:none;stroke:none}

/* the starfield: small paper dots */
.so-stars-field{inset:0;opacity:0}
.so-stars-field i{position:absolute;border-radius:50%;background:var(--paper)}
.so-stars-field i.so-stars-big{box-shadow:0 0 5px rgba(246,241,238,.7)}

/* a four-point sparkle, the one star shape used everywhere */
.so-stars-spk{position:absolute;display:block;overflow:visible}
.so-stars-spk path{fill:var(--paper)}
.so-stars-spk.so-stars-lime path{fill:var(--lime)}

/* the constellation's other stars: dim until a line reaches them */
.so-stars-cons{inset:0;opacity:0}
.so-stars-nb{opacity:.55}

/* the photo, which folds into the ember */
.so-stars-card{border-radius:26px;overflow:hidden;background:radial-gradient(120% 90% at 50% 30%,rgba(246,241,238,.13),rgba(246,241,238,.03) 70%),var(--ink);box-shadow:0 26px 60px rgba(0,0,0,.55),0 0 0 1px rgba(246,241,238,.14);display:flex;align-items:center;justify-content:center;transform-origin:50% 50%}
.so-stars-card > .so-stars-rc{flex:none}

/* the comet: a paper core with a lime glow, a thin tail behind it */
.so-stars-comet{left:0;top:0;width:150px;height:24px;offset-rotate:auto;offset-anchor:138px 12px;offset-distance:0%}
.so-stars-core{position:absolute;right:1px;top:1px;width:22px;height:22px;border-radius:50%;opacity:0;
  background:radial-gradient(circle,var(--white) 0%,var(--paper) 34%,rgba(231,250,134,.95) 58%,rgba(231,250,134,0) 72%);
  box-shadow:0 0 14px 5px rgba(231,250,134,.55),0 0 38px 14px rgba(231,250,134,.18)}
.so-stars-tail{position:absolute;right:12px;top:50%;width:138px;height:3px;margin-top:-1.5px;border-radius:2px;transform-origin:100% 50%;transform:scaleX(0);opacity:0;
  background:linear-gradient(to right,rgba(231,250,134,0) 0%,rgba(231,250,134,.45) 55%,var(--paper) 100%)}
.so-stars-tail.so-stars-soft{height:12px;margin-top:-6px;filter:blur(5px);background:linear-gradient(to right,rgba(231,250,134,0),rgba(231,250,134,.5))}

/* sparkles shed on the way up, and the burst */
.so-stars-shed{opacity:0}
.so-stars-ring{border-radius:50%;border:1.5px solid rgba(231,250,134,.9);opacity:0}
.so-stars-bst{opacity:0}
.so-stars-new{opacity:0;filter:drop-shadow(0 0 7px rgba(231,250,134,.95)) drop-shadow(0 0 2px rgba(246,241,238,.9))}

/* "Bon voyage!" — 07's italic Montserrat, paper on ink, a lime swash under it */
.so-stars-bv{left:40px;top:362px;width:320px}
.so-stars-bvr{transform:rotate(-6deg);transform-origin:40% 50%}
.so-stars-bvt{position:relative;display:inline-block;font:italic 800 40px/1.12 var(--display);letter-spacing:-.01em;color:var(--paper);white-space:nowrap}
.so-stars-l{display:inline-block;white-space:pre;opacity:0}
.so-stars-glint{right:-16px;top:-8px;width:18px;height:18px;opacity:0}
.so-stars-swash{display:block;width:236px;height:18px;margin:-3px 0 0 2px;overflow:visible}
.so-stars-swash path{fill:var(--lime)}
.so-stars-swg{transform-box:fill-box;transform-origin:left center;transform:scaleX(0)}
.so-stars-sub{margin:10px 0 0 6px;font:400 13px/1.3 var(--body);color:rgba(246,241,238,.86);opacity:0}

/* the Receipts tab: a dashed promise, then lime when the star lands */
.so-stars-tab{box-sizing:border-box;border:1.5px dashed rgba(246,241,238,.5);display:flex;align-items:center;justify-content:center;font:600 13.5px var(--display);color:rgba(246,241,238,.86);opacity:0}
.so-stars-tabfill{box-sizing:border-box;background:var(--lime);display:flex;align-items:center;justify-content:center;font:600 13.5px var(--display);color:var(--ink);opacity:0;box-shadow:0 0 22px rgba(231,250,134,.45)}
.so-stars-tabring{box-sizing:border-box;border:1.5px solid rgba(231,250,134,.75);opacity:0}

/* the star that travels down */
.so-stars-trav{left:0;top:0;width:22px;height:22px;offset-anchor:50% 50%;offset-rotate:0deg;offset-distance:0%;opacity:0}
.so-stars-trav .so-stars-spk{left:0;top:0;width:22px;height:22px;filter:drop-shadow(0 0 6px rgba(231,250,134,.95)) drop-shadow(0 0 2px rgba(246,241,238,.9))}

/* ─── keyframes ─── */
@keyframes so-stars-in{from{opacity:0}to{opacity:1}}
@keyframes so-stars-out{from{opacity:1}to{opacity:0}}
@keyframes so-stars-tw{0%,100%{opacity:var(--o1)}50%{opacity:.12}}
@keyframes so-stars-fold{
  0%{transform:none;border-radius:26px;opacity:1;filter:brightness(1)}
  30%{transform:scale(.62,.64) rotate(-5deg);border-radius:40px;opacity:1;filter:brightness(1.08)}
  72%{transform:translateY(-6px) scale(.12,.085) rotate(-14deg);border-radius:50%;opacity:1;filter:brightness(1.5)}
  100%{transform:translateY(-8px) scale(.04,.028) rotate(-20deg);border-radius:50%;opacity:0;filter:brightness(1.8)}}
/* ember life, 150 → 900 ms */
@keyframes so-stars-ember{
  0%{opacity:0;transform:scale(.2)}
  20%{opacity:1;transform:scale(1)}
  86.7%{opacity:1;transform:scale(.9)}
  90%{opacity:1;transform:scale(1.3)}
  100%{opacity:0;transform:scale(.2)}}
@keyframes so-stars-go{from{offset-distance:0%}to{offset-distance:100%}}
/* tail, 340 → 820 ms */
@keyframes so-stars-tail{0%{transform:scaleX(0);opacity:0}20%{transform:scaleX(1);opacity:1}75%{transform:scaleX(1);opacity:1}100%{transform:scaleX(0);opacity:0}}
@keyframes so-stars-shed{0%{opacity:0;transform:scale(.2)}15%{opacity:1;transform:scale(1)}100%{opacity:0;transform:translate(var(--sx),26px) scale(.2) rotate(80deg)}}
@keyframes so-stars-ring{0%{opacity:0;transform:scale(.2)}8%{opacity:.95;transform:scale(.35)}100%{opacity:0;transform:scale(3.4)}}
@keyframes so-stars-bst{0%{opacity:0;transform:translate(0,0) scale(0)}12%{opacity:1;transform:translate(0,0) scale(1.15)}100%{opacity:0;transform:translate(var(--bx),var(--by)) scale(.15) rotate(90deg)}}
/* the new star, 790 → 1880 ms: pops, pulses as the spark leaves it at 1400, fades with the words */
@keyframes so-stars-new{
  0%{opacity:0;transform:scale(0) rotate(-40deg)}
  5.5%{opacity:1;transform:scale(1.7) rotate(15deg)}
  14.7%{opacity:1;transform:scale(1) rotate(0)}
  54%{opacity:1;transform:scale(1) rotate(0)}
  58.7%{opacity:1;transform:scale(1.35) rotate(20deg)}
  64%{opacity:1;transform:scale(1) rotate(0)}
  81.7%{opacity:1;transform:scale(1)}
  100%{opacity:0;transform:scale(.6)}}
@keyframes so-stars-draw{from{stroke-dashoffset:100.5}to{stroke-dashoffset:0}}
@keyframes so-stars-lit{
  0%{opacity:.55;transform:scale(1);filter:none}
  40%{opacity:1;transform:scale(2);filter:drop-shadow(0 0 5px rgba(231,250,134,.95))}
  100%{opacity:1;transform:scale(1.35);filter:drop-shadow(0 0 4px rgba(231,250,134,.75))}}
@keyframes so-stars-dim{
  from{opacity:1;transform:scale(1.35);filter:drop-shadow(0 0 4px rgba(231,250,134,.75))}
  to{opacity:.55;transform:scale(1);filter:none}}
@keyframes so-stars-letter{
  0%{opacity:0;transform:translateY(10px) scale(.3);filter:blur(6px)}
  55%{opacity:1;transform:translateY(-2px) scale(1.16);filter:blur(0)}
  100%{opacity:1;transform:none;filter:none}}
@keyframes so-stars-wipe{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@keyframes so-stars-glint{0%{opacity:0;transform:scale(.3) rotate(0)}40%{opacity:1;transform:scale(1.3) rotate(45deg)}100%{opacity:0;transform:scale(.3) rotate(90deg)}}
@keyframes so-stars-bvout{from{opacity:1;transform:none}to{opacity:0;transform:translateY(-12px)}}
@keyframes so-stars-spin{from{transform:rotate(0)}to{transform:rotate(180deg)}}
/* the traveller, 1400 → 1990 ms: out of the new star, down the route (to 1840),
   blink at the tab (1840 → 1920), shrink into it (→ 1990) */
@keyframes so-stars-trav{
  0%{opacity:1;transform:scale(.2)}
  8%{opacity:1;transform:scale(1)}
  74.6%{opacity:1;transform:scale(1)}
  79.7%{opacity:1;transform:scale(1.5)}
  84.7%{opacity:1;transform:scale(.85)}
  88.1%{opacity:1;transform:scale(1.15)}
  100%{opacity:1;transform:scale(0)}}
@keyframes so-stars-grow{0%{opacity:0;transform:scale(.1)}35%{opacity:1}100%{opacity:1;transform:scale(1)}}
@keyframes so-stars-tabring{0%{opacity:0;transform:scale(1)}15%{opacity:.9}100%{opacity:0;transform:scale(1.16,1.4)}}
@keyframes so-stars-rm{0%{opacity:0}25%{opacity:1}70%{opacity:1}100%{opacity:0}}

/* ── Round 4 send-off · the postcard ─────────────────────────────────────
   The photo shrinks and turns over into a postcard addressed to Moneybird,
   a BON VOYAGE postmark thunks on, a line-art hand waves it off, and the
   card flies a short dashed route into the Receipts tab.
   07's ink ground and palette. Every selector and keyframe is prefixed
   so-postcard-. Timing lives in sendoff-postcard.js (one timeline table),
   so a seek can freeze every element exactly. */

.so-postcard-root{position:absolute;inset:0;overflow:hidden;pointer-events:none;font-family:var(--body);color:var(--paper)}

/* the ground: 07's map, stopping just above the pill so the tab stays visible */
.so-postcard-ground{position:absolute;left:0;right:0;top:0;opacity:0;
  background:radial-gradient(110% 70% at 50% 40%,rgba(246,241,238,.035),rgba(246,241,238,0) 70%),var(--ink);
  -webkit-mask-image:linear-gradient(to bottom,transparent 36px,#000 66px,#000 calc(100% - 46px),transparent);
          mask-image:linear-gradient(to bottom,transparent 36px,#000 66px,#000 calc(100% - 46px),transparent)}
.so-postcard-chart{position:absolute;left:0;top:0;width:390px;height:844px;overflow:visible}
.so-postcard-grat{opacity:0}
.so-postcard-grat path{fill:none;stroke:rgba(246,241,238,.07);stroke-width:1}

/* the route: 07's dashed line, drawn in through a mask */
.so-postcard-routein{opacity:0}
.so-postcard-route{fill:none;stroke:rgba(246,241,238,.72);stroke-width:2;stroke-linecap:round;stroke-dasharray:7 7}
.so-postcard-routemask{fill:none;stroke:#fff;stroke-width:14;stroke-linecap:round;stroke-dashoffset:var(--so-postcard-L,1200)}
.so-postcard-origin{fill:var(--lime)}
.so-postcard-ring{fill:none;stroke:rgba(231,250,134,.6);stroke-width:1.5;transform-box:fill-box;transform-origin:center;opacity:0}

/* "Bon voyage!", written the way 07 writes it */
.so-postcard-wtext{font:italic 800 40px var(--display);letter-spacing:-.01em;fill:var(--paper);fill-opacity:0;stroke:var(--paper);stroke-width:1.1;stroke-dasharray:160;stroke-dashoffset:160;paint-order:stroke}
.so-postcard-wrect{transform-box:fill-box;transform-origin:left center;transform:scaleX(0)}
.so-postcard-swash{fill:var(--lime);transform-box:fill-box;transform-origin:left center;transform:scaleX(0)}
.so-postcard-wsub{font:400 13px var(--body);fill:rgba(246,241,238,.86);opacity:0}
.so-postcard-pen{fill:var(--lime);opacity:0}

/* the photo, as the host showed it at the moment of approval */
/* an ink patch over the host's own photo from frame 0, in case the host has not hidden it yet */
.so-postcard-patch{position:absolute;border-radius:26px;background:var(--ink)}
.so-postcard-photopos{position:absolute;transform-origin:50% 50%}
.so-postcard-photo{position:absolute;inset:0;border-radius:26px;overflow:hidden;
  background:radial-gradient(120% 90% at 50% 30%,#5b5046 0%,#3b332d 52%,#1e1a17 100%);
  box-shadow:0 26px 60px rgba(0,0,0,.55),0 0 0 1px rgba(246,241,238,.14);
  -webkit-backface-visibility:hidden;backface-visibility:hidden}
.so-postcard-till{position:absolute;left:50%;top:50%;width:60%;aspect-ratio:120/172;transform:translate(-50%,-50%) rotate(-4deg);filter:drop-shadow(0 18px 22px rgba(0,0,0,.5))}

/* the postcard: fly (route + shrink) > squash (postmark impact) > back (the turn) */
.so-postcard-fly{position:absolute;left:0;top:0;width:300px;height:196px;offset-anchor:50% 50%;offset-rotate:0deg;offset-distance:0%}
.so-postcard-squash{position:absolute;inset:0;transform-origin:50% 100%}
.so-postcard-back{position:absolute;inset:0;border-radius:12px;overflow:hidden;background:var(--paper);color:var(--ink);
  box-shadow:0 22px 44px rgba(0,0,0,.48),0 0 0 1px rgba(246,241,238,.18);
  -webkit-backface-visibility:hidden;backface-visibility:hidden;transform:perspective(900px) rotateY(-90deg)}
.so-postcard-label{position:absolute;left:16px;top:16px;font:700 8.5px/1 var(--display);letter-spacing:.3em;color:var(--ink)}
.so-postcard-msg{position:absolute;left:16px;top:42px;width:136px;font:italic 700 13px/1.36 var(--display);letter-spacing:-.005em;color:var(--ink)}
.so-postcard-sign{position:absolute;left:16px;bottom:16px;font:400 10.5px/1 var(--body);color:var(--muted)}
.so-postcard-rule{position:absolute;left:160px;top:16px;bottom:16px;border-left:1.5px dashed var(--dash)}
.so-postcard-stamp{position:absolute;right:14px;top:14px;width:50px;height:62px;background:var(--lime);
  -webkit-mask:radial-gradient(2.4px,#0000 97%,#000) round -3.5px -3.5px/7px 7px,linear-gradient(#000 0 0) no-repeat 50%/calc(100% - 7px) calc(100% - 7px);
          mask:radial-gradient(2.4px,#0000 97%,#000) round -3.5px -3.5px/7px 7px,linear-gradient(#000 0 0) no-repeat 50%/calc(100% - 7px) calc(100% - 7px)}
.so-postcard-stamp svg{display:block;width:100%;height:100%}
.so-postcard-st-frame{fill:none;stroke:var(--ink);stroke-opacity:.45;stroke-width:.8}
.so-postcard-st-slip{fill:var(--paper);stroke:var(--ink);stroke-width:1.3;stroke-linejoin:round}
.so-postcard-st-line{fill:none;stroke:var(--ink);stroke-width:1.2;stroke-linecap:round}
.so-postcard-st-word{font:800 5.4px var(--display);letter-spacing:.9px;fill:var(--ink)}
.so-postcard-to{position:absolute;left:172px;right:16px;top:100px}
.so-postcard-to small{display:block;font:700 7.5px/1 var(--display);letter-spacing:.28em;color:var(--muted);margin-bottom:3px}
.so-postcard-ln{font:400 10.5px/22px var(--body);color:var(--ink);border-bottom:1px dashed var(--dash);white-space:nowrap;overflow:hidden}
.so-postcard-ln.so-postcard-b{font:700 12.5px/22px var(--display)}

/* the postmark: ink on paper, crossing the lime stamp */
.so-postcard-pmark{position:absolute;left:167px;top:18px;width:80px;height:80px;opacity:0;transform-origin:50% 50%;overflow:visible}
.so-postcard-pm-r1{fill:none;stroke:var(--ink);stroke-width:2.2}
.so-postcard-pm-r2{fill:none;stroke:var(--ink);stroke-width:1.1;stroke-dasharray:2.6 2.4}
.so-postcard-pm-t{font:800 8.4px var(--display);letter-spacing:1.1px;fill:var(--ink)}
.so-postcard-pm-b{font:700 5.6px var(--display);letter-spacing:.7px;fill:var(--ink)}
.so-postcard-pm-d{font:800 9px var(--display);letter-spacing:.4px;fill:var(--ink)}
.so-postcard-pm-y{font:600 7px var(--display);letter-spacing:.6px;fill:var(--ink)}
.so-postcard-pm-dot{fill:var(--ink)}
.so-postcard-waves{position:absolute;left:242px;top:33px;width:50px;height:26px;opacity:0;transform-origin:0 50%;overflow:visible}
.so-postcard-waves path{fill:none;stroke:var(--ink);stroke-width:1.7;stroke-linecap:round}
.so-postcard-splat{position:absolute;left:155px;top:6px;width:104px;height:104px;opacity:0;transform-origin:50% 50%;overflow:visible}
.so-postcard-splat path{fill:none;stroke:var(--ink);stroke-width:2;stroke-linecap:round}
/* paper over the card's face as it becomes the tab */
.so-postcard-seal{position:absolute;inset:0;background:var(--paper);opacity:0}

/* the waving hand: paper line work on ink, a lime cuff */
.so-postcard-handpos{position:absolute;left:18px;top:590px;width:96px;height:120px;opacity:0;transform-origin:50% 100%}
.so-postcard-hand{position:absolute;inset:0;width:96px;height:120px;overflow:visible;transform-origin:50% 90%}
.so-postcard-hand .so-postcard-ho{fill:none;stroke:var(--paper);stroke-linecap:round;stroke-linejoin:round}
.so-postcard-hand .so-postcard-hi{fill:none;stroke:var(--ink);stroke-linecap:round;stroke-linejoin:round}
.so-postcard-cuff{fill:var(--lime)}
.so-postcard-arcs{position:absolute;left:18px;top:590px;width:96px;height:120px;opacity:0;overflow:visible}
.so-postcard-arcs path{fill:none;stroke:rgba(246,241,238,.55);stroke-width:2.2;stroke-linecap:round}

/* the tab lights up as the card slips in */
.so-postcard-ping{position:absolute;border-radius:999px;opacity:0;outline:2px solid var(--lime);outline-offset:0}

/* ── keyframes (start times and durations are in the JS timeline) ── */
@keyframes so-postcard-fadein{from{opacity:0}to{opacity:1}}
@keyframes so-postcard-fadeout{from{opacity:1}to{opacity:0}}
@keyframes so-postcard-shrink{from{transform:none}to{transform:translate(var(--so-postcard-dx),var(--so-postcard-dy)) scale(var(--so-postcard-s))}}
@keyframes so-postcard-turnaway{from{transform:perspective(1100px) rotateY(0deg)}to{transform:perspective(1100px) rotateY(90deg)}}
@keyframes so-postcard-turnin{
  0%{transform:perspective(900px) rotateY(-90deg)}
  68%{transform:perspective(900px) rotateY(9deg)}
  100%{transform:perspective(900px) rotateY(0deg)}}
@keyframes so-postcard-thunk{
  0%{opacity:0;transform:scale(2.4) rotate(-38deg);animation-timing-function:cubic-bezier(.6,0,.95,.5)}
  12%{opacity:.35}
  40%{opacity:1;transform:scale(1.22,.76) rotate(-12deg);animation-timing-function:ease-out}
  66%{opacity:1;transform:scale(.93,1.08) rotate(-12deg)}
  100%{opacity:1;transform:scale(1) rotate(-12deg)}}
@keyframes so-postcard-impact{
  0%{transform:none}
  30%{transform:translateY(4px) scale(1.035,.95)}
  62%{transform:translateY(-3px) scale(.985,1.02)}
  100%{transform:none}}
@keyframes so-postcard-splat{
  0%{opacity:0;transform:scale(.7)}
  16%{opacity:1;transform:scale(1)}
  100%{opacity:0;transform:scale(1.3)}}
@keyframes so-postcard-waves{from{opacity:0;transform:scaleX(0)}to{opacity:1;transform:scaleX(1)}}
@keyframes so-postcard-reveal{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@keyframes so-postcard-ink{from{stroke-dashoffset:160}to{stroke-dashoffset:0}}
@keyframes so-postcard-fillin{from{fill-opacity:0}to{fill-opacity:1}}
@keyframes so-postcard-pen{0%{opacity:0;transform:translateX(0)}8%{opacity:1}90%{opacity:1}100%{opacity:0;transform:translateX(222px)}}
@keyframes so-postcard-handlife{
  0%{opacity:0;transform:translateX(-120px) rotate(-34deg);animation-timing-function:cubic-bezier(.3,1.3,.5,1)}
  8%{opacity:1}
  22%{opacity:1;transform:translateX(0) rotate(0deg)}
  80%{opacity:1;transform:translateX(0) rotate(0deg);animation-timing-function:cubic-bezier(.5,0,.9,.6)}
  100%{opacity:0;transform:translateX(-130px) rotate(-26deg)}}
@keyframes so-postcard-wave{
  0%{transform:rotate(0deg)}
  15%{transform:rotate(18deg)}
  33%{transform:rotate(-12deg)}
  51%{transform:rotate(18deg)}
  69%{transform:rotate(-12deg)}
  86%{transform:rotate(12deg)}
  100%{transform:rotate(0deg)}}
@keyframes so-postcard-arcs{0%{opacity:0}14%{opacity:1}86%{opacity:1}100%{opacity:0}}
@keyframes so-postcard-draw{from{stroke-dashoffset:var(--so-postcard-L,1200)}to{stroke-dashoffset:0}}
@keyframes so-postcard-ring{0%{opacity:.9;transform:scale(.4)}100%{opacity:0;transform:scale(3.2)}}
@keyframes so-postcard-fly{from{offset-distance:0%}to{offset-distance:100%}}
@keyframes so-postcard-flyscale{
  0%{transform:scale(1) rotate(0deg)}
  16%{transform:scale(.62) rotate(-4deg)}
  42%{transform:scale(.36) rotate(5deg)}
  76%{transform:scale(.25) rotate(-2deg)}
  88%{transform:scale(.24) rotate(0deg)}
  100%{transform:scale(var(--so-postcard-sx),var(--so-postcard-sy)) rotate(0deg)}}
@keyframes so-postcard-pill{from{border-radius:12px}to{border-radius:var(--so-postcard-rx) / var(--so-postcard-ry)}}
@keyframes so-postcard-ping{
  0%{opacity:0;outline-offset:0}
  12%{opacity:.95;outline-offset:1px}
  100%{opacity:0;outline-offset:11px}}
@keyframes so-postcard-calm{0%{opacity:0}20%{opacity:1}75%{opacity:1}100%{opacity:0}}

/* ── reduced motion: the finished card, the words, the hand — held, then gone ── */
.so-postcard-reduced .so-postcard-photopos,
.so-postcard-reduced .so-postcard-patch,
.so-postcard-reduced .so-postcard-routein,
.so-postcard-reduced .so-postcard-arcs,
.so-postcard-reduced .so-postcard-pen,
.so-postcard-reduced .so-postcard-ping,
.so-postcard-reduced .so-postcard-splat{display:none}
.so-postcard-reduced .so-postcard-ground,
.so-postcard-reduced .so-postcard-grat,
.so-postcard-reduced .so-postcard-handpos,
.so-postcard-reduced .so-postcard-wsub{opacity:1}
.so-postcard-reduced .so-postcard-back{transform:none}
.so-postcard-reduced .so-postcard-pmark{opacity:1;transform:rotate(-12deg)}
.so-postcard-reduced .so-postcard-waves{opacity:1;transform:none}
.so-postcard-reduced .so-postcard-wrect,
.so-postcard-reduced .so-postcard-swash{transform:none}
.so-postcard-reduced .so-postcard-wtext{fill-opacity:1;stroke-dashoffset:0}

/* ─── send-off: plane ─────────────────────────────────────────────────────────
   The photo folds itself into a paper plane in 3D, turns edge-on, becomes a
   paper dart that climbs, loops out of the right edge and back, and lands level
   on the Receipts tab, drawing 07's dashed route as a contrail that dissipates
   behind it. A small split-flap line flips to BON VOYAGE! and 07's lime swash
   underlines it.

   Every selector and keyframe is prefixed so-plane-. Motion lives under
   .so-plane-go; .so-plane-rm is the reduced-motion still. All timing is here in
   ms from start; the JS only supplies geometry through custom properties.

     0–230    lift: photo shrinks from o.from into a sheet mid-screen
     230–470  fold 1: top corners to the centre line
     470–680  fold 2: the new edges to the centre line (a dart)
     680–760  the dart turns edge-on (rotateY 88°)
     760–940  a paper plane opens out of that edge and turns onto its heading
     940–1840 flight along the route (contrail drawn in step)
     1790–2000 touchdown: it shrinks into the Receipts tab rect
   ───────────────────────────────────────────────────────────────────────── */

.so-plane-root{position:absolute;inset:0;overflow:hidden;pointer-events:none;font-family:var(--body);color:var(--paper)}

/* the ground: 07's map, kept clear of the status bar and the pill */
.so-plane-bg{position:absolute;inset:0;opacity:0;
  background:radial-gradient(110% 70% at 50% 40%,color-mix(in srgb,var(--paper) 5%,var(--ink)) 0%,var(--ink) 100%);
  -webkit-mask-image:linear-gradient(to bottom,transparent 0,transparent 50px,#000 76px,#000 704px,transparent 744px);
          mask-image:linear-gradient(to bottom,transparent 0,transparent 50px,#000 76px,#000 704px,transparent 744px)}
.so-plane-bg svg{position:absolute;left:0;top:0;width:390px;height:844px}
.so-plane-grat{fill:none;stroke:var(--paper);stroke-opacity:.06;stroke-width:1}

/* the split-flap line */
.so-plane-board{position:absolute;left:0;right:0;top:70px;display:flex;flex-direction:column;align-items:center;opacity:0}
.so-plane-lab{font:600 10px/1 var(--display);letter-spacing:.2em;text-transform:uppercase;color:var(--paper);opacity:.55;margin:0 0 10px .2em}
.so-plane-tiles{display:flex;gap:4px}
.so-plane-tile{position:relative;width:26px;height:36px;border-radius:5px;overflow:hidden;
  background:color-mix(in srgb,var(--paper) 7%,transparent);
  box-shadow:inset 0 0 0 1px color-mix(in srgb,var(--paper) 13%,transparent)}
.so-plane-tile::before{content:"";position:absolute;left:0;right:0;top:0;height:50%;background:color-mix(in srgb,var(--paper) 4%,transparent);z-index:1}
.so-plane-tile::after{content:"";position:absolute;left:0;right:0;top:50%;height:1px;margin-top:-.5px;background:var(--ink);opacity:.9;z-index:2}
.so-plane-gap{width:6px;flex:none}
.so-plane-strip{display:flex;flex-direction:column}
.so-plane-strip b{display:block;height:36px;font:700 19px/36px var(--display);color:var(--paper);text-align:center;font-variant-numeric:tabular-nums}
.so-plane-swash{display:block;width:292px;height:15px;margin-top:6px;fill:var(--lime);transform:scaleX(0);transform-origin:left center}
.so-plane-sub{margin-top:9px;font:400 13px/1 var(--body);color:var(--paper);opacity:0}

/* the sheet that folds */
.so-plane-sheet{position:absolute;transform-origin:50% 50%;perspective:1100px}
.so-plane-intro{position:absolute;inset:0;overflow:hidden;border-radius:26px}
/* no shared 3D context: each flap is projected on its own and painted in DOM
   order (base, first folds, second folds), so Chrome's 3D sorting can never put
   the photo over a flap that is lifting off it */
.so-plane-turn{position:absolute;inset:0}
.so-plane-ori{position:absolute;inset:0;perspective:900px;visibility:hidden}
.so-plane-face{position:absolute;left:0;top:0}
.so-plane-ph{position:absolute;left:0;top:0;overflow:hidden;background:radial-gradient(120% 90% at 50% 30%,#5b5046 0%,#3b332d 52%,#1e1a17 100%)}
.so-plane-rc{position:absolute;left:19%;top:19%;width:62%;height:62%;transform:rotate(-4deg);filter:drop-shadow(0 18px 20px rgba(0,0,0,.5))}
.so-plane-vig{position:absolute;inset:0;background:radial-gradient(90% 90% at 50% 50%,transparent 55%,rgba(0,0,0,.38) 100%)}
/* a flap hinges on an element edge lying on its fold line */
.so-plane-hg{position:absolute;left:0;top:0;width:0;height:0;transform-style:preserve-3d;transform-origin:0 0;
  transform:translate3d(var(--px),var(--py),var(--pz)) rotate(var(--phi)) rotateX(0deg)}
.so-plane-hg.so-plane-f2{visibility:hidden}
.so-plane-hf{position:absolute;top:0;overflow:hidden}
.so-plane-hf .so-plane-pw{position:absolute;left:0;top:0;transform-origin:0 0}
.so-plane-bk{background:var(--paper);visibility:hidden}
.so-plane-ov{position:absolute;left:0;top:0;overflow:visible}
.so-plane-ov polygon{fill:none;stroke:var(--ink);stroke-opacity:.5;stroke-width:2.2;stroke-linejoin:round}
.so-plane-ov polygon.so-plane-over{fill:var(--paper)}
.so-plane-shd{position:absolute;inset:0;background:var(--ink);opacity:0;pointer-events:none}

/* the contrail: 07's dashed route, revealed behind the plane and wiped after it */
.so-plane-trail{position:absolute;left:0;top:0;width:390px;height:844px;overflow:visible}
.so-plane-route{fill:none;stroke:var(--paper);stroke-opacity:.72;stroke-width:2;stroke-linecap:round;stroke-dasharray:7 7}
.so-plane-mdraw,.so-plane-merase{fill:none;stroke-linecap:butt;stroke-dasharray:var(--L) var(--L);stroke-dashoffset:var(--L)}
.so-plane-mdraw{stroke:var(--white,#fff);stroke-width:16}
.so-plane-merase{stroke:#000;stroke-width:20}

/* the plane in flight */
.so-plane-flight{position:absolute;left:0;top:0;width:80px;height:42px;offset-rotate:auto;offset-anchor:50% 50%;offset-distance:0%;visibility:hidden}
.so-plane-land,.so-plane-pose{position:absolute;inset:0}
.so-plane-pose svg{display:block;width:80px;height:42px;overflow:visible;filter:drop-shadow(0 8px 12px rgba(0,0,0,.45))}
.so-plane-w1{fill:var(--paper)}
.so-plane-w2{fill:var(--hair)}
.so-plane-keel{fill:var(--dash)}
.so-plane-tip{fill:var(--lime)}
.so-plane-edge{fill:none;stroke:var(--ink);stroke-width:1.3;stroke-linejoin:round}
.so-plane-crease{fill:none;stroke:var(--ink);stroke-width:1;stroke-opacity:.8}

/* touchdown on the tab */
.so-plane-ring{position:absolute;border-radius:999px;opacity:0;
  box-shadow:0 0 0 2px var(--lime),0 0 0 8px color-mix(in srgb,var(--lime) 28%,transparent)}

/* ── the choreography ── */
.so-plane-go .so-plane-bg{animation:so-plane-fade 200ms ease-out 0ms both}
.so-plane-go .so-plane-board{animation:so-plane-board-in 280ms var(--ease) 180ms both}
.so-plane-go .so-plane-swash{animation:so-plane-reveal 380ms var(--ease) 1290ms both}
.so-plane-go .so-plane-sub{animation:so-plane-sub 320ms ease 1400ms both}

.so-plane-go .so-plane-sheet{animation:so-plane-lift 230ms cubic-bezier(.2,.8,.3,1) 0ms both,so-plane-gone 1ms linear 760ms forwards}
.so-plane-go .so-plane-intro{animation:so-plane-radius 230ms cubic-bezier(.2,.8,.3,1) 0ms both,so-plane-gone 1ms linear 230ms forwards}
.so-plane-go .so-plane-ori{animation:so-plane-show 1ms linear 230ms forwards}
.so-plane-go .so-plane-base{animation:so-plane-clip-house 1ms linear 230ms forwards,so-plane-clip-dart 1ms linear 470ms forwards}
.so-plane-go .so-plane-hg.so-plane-f1{animation:so-plane-hinge 240ms cubic-bezier(.55,0,.35,1) 230ms both}
.so-plane-go .so-plane-f1 .so-plane-fr{animation:so-plane-hide 1ms linear 350ms forwards}
.so-plane-go .so-plane-f1 .so-plane-fr .so-plane-shd{animation:so-plane-shade-in 120ms linear 230ms both}
.so-plane-go .so-plane-f1 .so-plane-bk{animation:so-plane-show 1ms linear 350ms forwards,so-plane-clip-to 1ms linear 470ms forwards}
.so-plane-go .so-plane-f1 .so-plane-bk .so-plane-shd{animation:so-plane-shade-out 120ms linear 350ms both}
.so-plane-go .so-plane-hg.so-plane-f2{animation:so-plane-show 1ms linear 470ms forwards,so-plane-hinge 210ms cubic-bezier(.55,0,.35,1) 470ms both}
.so-plane-go .so-plane-f2 .so-plane-fr{animation:so-plane-hide 1ms linear 575ms forwards}
.so-plane-go .so-plane-f2 .so-plane-fr .so-plane-shd{animation:so-plane-shade-in 105ms linear 470ms both}
.so-plane-go .so-plane-f2 .so-plane-bk{animation:so-plane-show 1ms linear 575ms forwards}
.so-plane-go .so-plane-f2 .so-plane-bk .so-plane-shd{animation:so-plane-shade-out 105ms linear 575ms both}
.so-plane-go .so-plane-turn{animation:so-plane-turn 80ms cubic-bezier(.5,0,1,1) 680ms both}

.so-plane-go .so-plane-flight{animation:so-plane-show 1ms linear 760ms forwards,so-plane-fly 900ms linear 940ms both}
.so-plane-go .so-plane-pose{animation:so-plane-pose 180ms var(--ease) 760ms both}
.so-plane-go .so-plane-land{animation:so-plane-land 210ms cubic-bezier(.5,0,.75,.4) 1790ms both}
.so-plane-go .so-plane-mdraw{animation:so-plane-draw 900ms linear 940ms both}
.so-plane-go .so-plane-merase{animation:so-plane-draw 900ms linear 1300ms both}
.so-plane-go .so-plane-trail-g{animation:so-plane-out 220ms ease 1780ms both}
.so-plane-go .so-plane-ring{animation:so-plane-ring 230ms var(--ease) 1770ms both}

@keyframes so-plane-fade{from{opacity:0}to{opacity:1}}
@keyframes so-plane-out{from{opacity:1}to{opacity:0}}
@keyframes so-plane-board-in{from{opacity:0;transform:translateY(-8px)}to{opacity:1;transform:none}}
@keyframes so-plane-reveal{from{transform:scaleX(0)}to{transform:scaleX(1)}}
@keyframes so-plane-sub{from{opacity:0;transform:translateY(3px)}to{opacity:.86;transform:none}}
@keyframes so-plane-flap{from{transform:translateY(0)}to{transform:translateY(var(--roll))}}
@keyframes so-plane-lift{from{transform:translate(var(--dx),var(--dy)) scale(1)}to{transform:translate(0px,0px) scale(var(--s1))}}
@keyframes so-plane-radius{from{border-radius:26px}to{border-radius:0}}
@keyframes so-plane-show{from,to{visibility:visible}}
@keyframes so-plane-hide{from,to{visibility:hidden}}
/* opacity, not visibility: a descendant that sets visibility:visible would outlive a hidden parent */
@keyframes so-plane-gone{from,to{opacity:0}}
@keyframes so-plane-clip-house{from,to{clip-path:var(--ch)}}
@keyframes so-plane-clip-dart{from,to{clip-path:var(--cd)}}
@keyframes so-plane-clip-to{from,to{clip-path:var(--c2)}}
@keyframes so-plane-shade-in{from{opacity:0}to{opacity:.34}}
@keyframes so-plane-shade-out{from{opacity:.22}to{opacity:0}}
@keyframes so-plane-hinge{
  0%{transform:translate3d(var(--px),var(--py),var(--pz)) rotate(var(--phi)) rotateX(0deg)}
  50%{transform:translate3d(var(--px),var(--py),var(--pz)) rotate(var(--phi)) rotateX(-90deg)}
  100%{transform:translate3d(var(--px),var(--py),var(--pz)) rotate(var(--phi)) rotateX(-180deg)}}
@keyframes so-plane-turn{from{transform:rotateY(0deg)}to{transform:rotateY(88deg)}}
@keyframes so-plane-pose{
  0%{transform:rotate(var(--pose)) scale(var(--ps)) scaleY(.06)}
  45%{transform:rotate(var(--pose)) scale(var(--ps2)) scaleY(1)}
  100%{transform:rotate(0deg) scale(1)}}
@keyframes so-plane-fly{0%{offset-distance:0%}12%{offset-distance:4%}86%{offset-distance:95%}100%{offset-distance:100%}}
@keyframes so-plane-draw{
  0%{stroke-dashoffset:var(--L)}
  12%{stroke-dashoffset:calc(var(--L) * .96)}
  86%{stroke-dashoffset:calc(var(--L) * .05)}
  100%{stroke-dashoffset:0px}}
@keyframes so-plane-land{from{transform:scale(1);opacity:1}to{transform:scale(.3);opacity:0}}
@keyframes so-plane-ring{0%{opacity:0;transform:scale(1.35)}65%{opacity:1;transform:scale(.98)}100%{opacity:1;transform:scale(1)}}

/* reduced motion: the finished line, held and faded; nothing travels */
.so-plane-rm{animation:so-plane-still 560ms ease both}
.so-plane-rm .so-plane-bg,.so-plane-rm .so-plane-board{opacity:1}
.so-plane-rm .so-plane-swash{transform:scaleX(1)}
.so-plane-rm .so-plane-sub{opacity:.86}
.so-plane-rm .so-plane-parked{position:absolute;left:155px;top:309px;width:80px;height:42px}
@keyframes so-plane-still{0%{opacity:0}22%{opacity:1}72%{opacity:1}100%{opacity:0}}

/* ─── send-off: sail ─────────────────────────────────────────────────────────
   The receipt lifts off the photo and becomes the sail of a paper boat on a
   paper-line sea; a pennant writes "Bon voyage!", a hand waves from the dock,
   and the boat rides the sea line down into the Receipts tab.
   Every rule's resting state is the FINISHED look; animations run from their
   start state with fill-mode both, so seek and reduced motion both just work.
   Timeline (ms): ground 0-300 · lift 80-560 · dock 240-560 · sea draws 200-820
   · hull 420-660 · hand 460 + waves 640-1510 · mast 600-740 · pennant 700-1020
   · words 800-1300 · voyage 1160-1880 · ripple 1840-2320 · fade 1880-2080     */

.so-sail-root{position:absolute;left:0;top:0;width:100%;height:100%;overflow:hidden;pointer-events:none}
.so-sail-root,.so-sail-root *{animation-fill-mode:both!important}

/* the ink ground — 07's chart, opening up at the bottom so the real pill shows */
.so-sail-ground{position:absolute;inset:0;background:radial-gradient(110% 70% at 50% 40%,rgba(246,241,238,.035),rgba(246,241,238,0) 70%),var(--ink,#101E30);
  -webkit-mask-image:linear-gradient(#000 0,#000 var(--so-sail-mask-a,730px),transparent var(--so-sail-mask-b,752px));
          mask-image:linear-gradient(#000 0,#000 var(--so-sail-mask-a,730px),transparent var(--so-sail-mask-b,752px));
  animation:so-sail-fadein 300ms ease}
.so-sail-grat{fill:none;stroke:rgba(246,241,238,.06);stroke-width:1}

/* the photo's table, which the receipt lifts off */
.so-sail-table{position:absolute;border-radius:26px;overflow:hidden;opacity:0;
  background:radial-gradient(120% 90% at 50% 30%,#5b5046 0%,#3b332d 52%,#1e1a17 100%);
  box-shadow:0 26px 60px rgba(0,0,0,.55),0 0 0 1px rgba(246,241,238,.14);
  animation:so-sail-table 360ms 60ms cubic-bezier(.5,0,.4,1)}
.so-sail-table::after{content:"";position:absolute;inset:0;background:radial-gradient(90% 90% at 50% 50%,transparent 55%,rgba(0,0,0,.38) 100%)}
@keyframes so-sail-table{0%{opacity:1;transform:none;border-radius:26px}100%{opacity:0;transform:translate(var(--so-sail-tx),var(--so-sail-ty)) scale(.42);border-radius:60px}}

/* the sea: one solid surface line, two dashed currents, all pouring into the tab */
.so-sail-sea{position:absolute;left:0;top:0;width:100%;height:100%;overflow:visible;animation:so-sail-fadeout 200ms 1880ms ease}
.so-sail-maskline{fill:none;stroke:#fff;stroke-width:110;stroke-linecap:round;stroke-dasharray:var(--so-sail-L) var(--so-sail-L);stroke-dashoffset:0;
  animation:so-sail-draw 620ms 200ms cubic-bezier(.45,.05,.3,1)}
@keyframes so-sail-draw{from{stroke-dashoffset:var(--so-sail-L)}to{stroke-dashoffset:0}}
.so-sail-surface{fill:none;stroke:rgba(246,241,238,.86);stroke-width:2;stroke-linecap:round;stroke-linejoin:round}
.so-sail-current{fill:none;stroke:rgba(246,241,238,.46);stroke-width:1.6;stroke-linecap:round;stroke-dasharray:7 7;
  animation:so-sail-flow 900ms linear infinite}
.so-sail-current.so-sail-deep{stroke:rgba(246,241,238,.26);stroke-dasharray:5 9;animation-duration:1400ms}
@keyframes so-sail-flow{from{stroke-dashoffset:0}to{stroke-dashoffset:-28}}

/* the dock and the waving hand */
.so-sail-dock{animation:so-sail-dock 320ms 240ms var(--spring,cubic-bezier(.34,1.56,.64,1))}
@keyframes so-sail-dock{from{transform:translateX(-96px)}to{transform:none}}
.so-sail-deck{fill:none;stroke:var(--paper,#F6F1EE);stroke-width:2;stroke-linejoin:round}
.so-sail-post{fill:none;stroke:var(--paper,#F6F1EE);stroke-width:2.2;stroke-linecap:round}
.so-sail-post-wet{fill:none;stroke:rgba(246,241,238,.4);stroke-width:2;stroke-linecap:round;stroke-dasharray:3 4}
.so-sail-bollard{fill:var(--paper,#F6F1EE)}
.so-sail-handpop{transform-box:fill-box;transform-origin:50% 100%;animation:so-sail-pop 200ms 460ms var(--spring,cubic-bezier(.34,1.56,.64,1))}
@keyframes so-sail-pop{from{transform:scale(0)}to{transform:none}}
.so-sail-hand{fill:none;stroke:var(--paper,#F6F1EE);stroke-width:1.35;stroke-linecap:round;stroke-linejoin:round;
  transform-box:fill-box;transform-origin:45% 100%;animation:so-sail-wave 290ms 640ms ease-in-out 3}
@keyframes so-sail-wave{0%,100%{transform:rotate(0)}30%{transform:rotate(18deg)}75%{transform:rotate(-12deg)}}
.so-sail-arc{fill:none;stroke:rgba(246,241,238,.6);stroke-width:1.5;stroke-linecap:round;animation:so-sail-arcs 870ms 640ms ease-out}
@keyframes so-sail-arcs{0%{opacity:0}15%{opacity:1}100%{opacity:1}}
.so-sail-shore{animation:so-sail-fadeout 200ms 1880ms ease}
.so-sail-caption{font:400 13px var(--body,'Poppins',system-ui,sans-serif);fill:rgba(246,241,238,.86);animation:so-sail-fadein 300ms 1100ms ease}

/* the boat: voyage (JS-built keyframes) > bob > layers */
.so-sail-boat{position:absolute;left:0;top:0;transform-origin:100px 200px;animation-timing-function:linear}
.so-sail-bob{position:absolute;inset:0;transform-origin:100px 200px;animation:so-sail-bob 1000ms 700ms ease-in-out infinite}
@keyframes so-sail-bob{0%,100%{transform:translateY(0) rotate(-2deg)}50%{transform:translateY(-4px) rotate(2deg)}}
.so-sail-layer{position:absolute;left:0;top:0;overflow:visible}

.so-sail-mast{fill:none;stroke:var(--paper,#F6F1EE);stroke-width:2.4;stroke-linecap:round;transform-origin:141px 162px;animation:so-sail-grow 160ms 580ms ease-out}
@keyframes so-sail-grow{from{transform:scaleY(0)}to{transform:none}}

.so-sail-pennant{transform-origin:140px 23px;animation:so-sail-unfurl 320ms 700ms var(--spring,cubic-bezier(.34,1.56,.64,1))}
@keyframes so-sail-unfurl{0%{transform:scaleX(0) skewY(12deg)}100%{transform:none}}
.so-sail-flutter{transform-origin:140px 23px;animation:so-sail-flutter 900ms 1020ms ease-in-out infinite}
@keyframes so-sail-flutter{0%,100%{transform:none}35%{transform:skewY(-3deg) scaleX(.985)}70%{transform:skewY(2deg)}}
.so-sail-flag{fill:var(--paper,#F6F1EE)}
.so-sail-stub{fill:var(--lime,#E7FA86)}
.so-sail-perf{stroke:rgba(16,30,48,.35);stroke-width:1.5;stroke-dasharray:3 3}
.so-sail-words{font:italic 800 21px var(--display,'Montserrat',system-ui,sans-serif);letter-spacing:-.01em;fill:var(--ink,#101E30);fill-opacity:1;
  stroke:var(--ink,#101E30);stroke-width:.9;stroke-dasharray:160;stroke-dashoffset:0;paint-order:stroke;
  animation:so-sail-ink 360ms 800ms linear,so-sail-fillin 240ms 1060ms ease}
@keyframes so-sail-ink{from{stroke-dashoffset:160}to{stroke-dashoffset:0}}
@keyframes so-sail-fillin{from{fill-opacity:0}to{fill-opacity:1}}
.so-sail-reveal{transform-origin:-26px 0;animation:so-sail-reveal 360ms 800ms cubic-bezier(.5,0,.5,1)}
@keyframes so-sail-reveal{from{transform:scaleX(0)}to{transform:none}}

.so-sail-sail{position:absolute;transform-origin:50% 50%;transform:scale(var(--so-sail-k));filter:drop-shadow(0 6px 8px rgba(0,0,0,.35));
  animation:so-sail-lift 480ms 80ms cubic-bezier(.55,0,.3,1)}
@keyframes so-sail-lift{0%{transform:translate(var(--so-sail-dx),var(--so-sail-dy)) rotate(-4deg) scale(1)}100%{transform:scale(var(--so-sail-k))}}
.so-sail-rc{position:absolute;left:0;top:0;transform-origin:0 0}
.so-sail-rc > *{display:block;margin:0!important;transform:none!important}  /* the lift owns the tilt, not the receipt */

.so-sail-hull{transform-origin:100px 212px;animation:so-sail-fold 240ms 420ms var(--spring,cubic-bezier(.34,1.56,.64,1))}
@keyframes so-sail-fold{from{transform:scaleY(0) scaleX(1.2)}to{transform:none}}
.so-sail-hullbody{fill:var(--white,#fff);stroke:var(--ink,#101E30);stroke-width:2.2;stroke-linejoin:round}
.so-sail-crease{fill:none;stroke:rgba(16,30,48,.3);stroke-width:1.4;stroke-linecap:round}
.so-sail-flap{fill:var(--paper,#F6F1EE);stroke:var(--ink,#101E30);stroke-width:2;stroke-linejoin:round;transform-origin:100px 162px;animation:so-sail-flapdown 200ms 560ms var(--spring,cubic-bezier(.34,1.56,.64,1))}
@keyframes so-sail-flapdown{from{transform:scaleY(0)}to{transform:none}}
.so-sail-wake{fill:none;opacity:0;stroke:rgba(246,241,238,.75);stroke-width:2;stroke-linecap:round;transform-origin:32px 200px;animation:so-sail-wake 380ms 1180ms linear}
.so-sail-wake.so-sail-dashed{stroke:rgba(246,241,238,.45);stroke-dasharray:5 6}
@keyframes so-sail-wake{0%{opacity:0;transform:scaleX(.15)}45%{opacity:1;transform:none}70%{opacity:1}100%{opacity:0;transform:none}}

/* the ripple where it lands */
.so-sail-ripple{fill:none;stroke:var(--lime,#E7FA86);stroke-width:2;opacity:0;transform-box:fill-box;transform-origin:50% 50%;animation:so-sail-ripple 480ms 1840ms ease-out}
.so-sail-ripple.so-sail-r2{stroke:rgba(246,241,238,.7);stroke-width:1.5;animation-delay:1960ms}
@keyframes so-sail-ripple{0%{opacity:0;transform:scale(.7)}15%{opacity:1}100%{opacity:0;transform:scale(1.28,1.6)}}

@keyframes so-sail-fadein{from{opacity:0}to{opacity:1}}
@keyframes so-sail-fadeout{from{opacity:1}to{opacity:0}}

/* reduced motion: the finished scene, no motion, fades in and out inside 600 ms */
.so-sail-root.so-sail-calm *{animation:none!important}
.so-sail-root.so-sail-calm .so-sail-table,.so-sail-root.so-sail-calm .so-sail-ripple{display:none}
.so-sail-root.so-sail-calm{animation:so-sail-calm 600ms linear}
@keyframes so-sail-calm{0%{opacity:0}20%{opacity:1}72%{opacity:1}100%{opacity:0}}

/* seek: freeze everything; the JS then rewrites each delay to (start − seek) */
.so-sail-root.so-sail-frozen,.so-sail-root.so-sail-frozen *{animation-play-state:paused!important}

