/* ==========================================================================
   Brand animations.

   Owen asked for motion in the spirit of the "Direct Mail Experts" video:
   "stuff like the rocket actually taking off and the Paper Airplane
   animation." The rocket has since been taken back off the site; the plane
   and the mail truck remain.

   Worth being straight about how these were made: I cannot watch video, so
   these are not frame-extracted from the YouTube clip. They are built from the
   brand's own SVG assets already on the site
   (about_us_-_icons_-_paper_airplane.svg) and animated to match the behaviour
   described. If the video's timing or path differs, send stills or describe
   the movement and these are quick to retune.

   Everything here is decorative and sits behind prefers-reduced-motion, which
   tokens.css already neutralises globally.
   ========================================================================== */

/* ---- Rocket launch ------------------------------------------------------- */
/* REMOVED at Owen's request. The homepage was the only page that ever had a
   rocket, so everything that drove it is gone with it: .rocket and its ship,
   flame and smoke parts, the .rocket-stage lane that clipped the climb, the
   .heading-rocket row, all four keyframe sets, and the [data-rocket] entry in
   animations.js. rocket_only.svg stays in the asset store, and the commit
   history has the animation intact if it is ever wanted back. */

/* ---- Paper airplane ----------------------------------------------------- */
/* Flies across the band on a gentle arc, banking as it goes, leaving a dashed
   flight path behind it. The mail metaphor, which is the point. */

.flightpath {
  position: relative;
  overflow: hidden;
  min-height: 132px;
}

.flightpath__plane {
  position: absolute;
  top: 50%;
  left: -80px;
  width: 46px;
  height: 46px;
  margin-top: -23px;
  opacity: 0;
  will-change: transform;
}
.flightpath.is-flying .flightpath__plane {
  animation: plane-fly 3.4s cubic-bezier(0.3, 0, 0.5, 1) forwards;
}

@keyframes plane-fly {
  0%   { opacity: 0; transform: translate(0, 26px) rotate(-14deg) scale(0.8); }
  12%  { opacity: 1; }
  30%  { transform: translate(28vw, -22px) rotate(-6deg) scale(1); }
  55%  { transform: translate(52vw, 12px) rotate(4deg) scale(1.04); }
  80%  { transform: translate(78vw, -18px) rotate(-4deg) scale(1); }
  92%  { opacity: 1; }
  100% { opacity: 0; transform: translate(108vw, -34px) rotate(-12deg) scale(0.86); }
}

/* The dashed trail it leaves. Drawn with stroke-dashoffset so it appears to be
   traced by the plane rather than simply fading in. */
.flightpath__trail {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}
.flightpath__trail path {
  fill: none;
  stroke: currentColor;
  stroke-width: 2;
  stroke-dasharray: 7 9;
  stroke-linecap: round;
  opacity: 0.35;
  stroke-dashoffset: 1200;
}
.flightpath.is-flying .flightpath__trail path {
  animation: trail-draw 3.4s cubic-bezier(0.3, 0, 0.5, 1) forwards;
}
@keyframes trail-draw {
  0%   { stroke-dashoffset: 1200; opacity: 0; }
  12%  { opacity: 0.35; }
  100% { stroke-dashoffset: 0; opacity: 0.35; }
}

/* ---- Tab image cross-fade ----------------------------------------------- */
/* The live site swaps a different photo behind each of Plan, Produce and
   Deliver. Stacked and cross-faded rather than swapped, so it does not flash. */

.tab-images {
  position: relative;
  border-radius: var(--radius);
  overflow: hidden;
  aspect-ratio: 4 / 3;
  background: var(--off-white);
}
.tab-images img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transform: scale(1.04);
  transition: opacity 0.55s var(--ease), transform 0.7s var(--ease);
}
.tab-images img.is-active { opacity: 1; transform: scale(1); }

/* Kept from the rocket work: cta-band is the positioning context for its own
   .section__bg, and the clip stops any future decoration escaping the band. */
.cta-band { position: relative; overflow: hidden; }

/* ---- Mail truck on the dotted route ------------------------------------- */
/* The dashed line is the road and the truck drives the length of it at one
   constant speed, entering off the left edge and leaving off the right. Linear
   timing throughout: a truck that eases in and out looks like it is braking. */

.roadtrip {
  --truck-w: clamp(104px, 13vw, 150px);
  /* Wheel contact sits at the bottom of the truck's viewBox, so the road line
     and the truck share one offset no matter what width the clamp lands on. */
  --road-y: 20px;
  position: relative;
  height: calc(var(--truck-w) * 92 / 150 + var(--road-y) + 10px);
  margin-bottom: clamp(20px, 3vw, 36px);
  overflow: hidden;
}

.roadtrip__road {
  position: absolute;
  left: 0;
  right: 0;
  bottom: calc(var(--road-y) - 3px);
  width: 100%;
  height: 6px;
}
.roadtrip__road line {
  stroke: var(--pink);
  stroke-width: 3;
  stroke-dasharray: 12 12;
  stroke-linecap: round;
  opacity: 0.4;
}

.roadtrip__truck {
  position: absolute;
  bottom: var(--road-y);
  left: calc(-1 * (var(--truck-w) + 20px));
  width: var(--truck-w);
  height: auto;
}
.roadtrip.is-driving .roadtrip__truck {
  animation: truck-drive 7s linear forwards;
}
@keyframes truck-drive {
  from { transform: translateX(0); }
  to   { transform: translateX(calc(100vw + var(--truck-w) + 20px)); }
}

/* Spokes rather than plain discs, so the roll is actually visible. */
.roadtrip__wheel {
  transform-box: fill-box;
  transform-origin: 50% 50%;
}
.roadtrip.is-driving .roadtrip__wheel {
  animation: wheel-roll 0.85s linear infinite;
}
@keyframes wheel-roll {
  to { transform: rotate(360deg); }
}

/* ---- Heading plane float ------------------------------------------------- */
/* /services sets the paper plane beside "How Can We Help?" instead of pinning it
   to the band's corner. Owen chose a gentle float over a one-shot fly-in, so it
   bobs and banks on a slow loop. Deliberately small amplitude: it sits next to a
   heading, and motion beside text you are trying to read has to stay quiet.

   The class is added by animations.js when the heading scrolls into view, so
   nothing loops away off screen, and a reduced-motion visitor never gets it. */

.heading-deco--plane {
  transform-origin: 60% 60%;
  will-change: transform;
}
.heading-deco--plane.is-floating {
  animation: plane-float 6s ease-in-out infinite;
}

@keyframes plane-float {
  0%   { transform: translate3d(0, 0, 0) rotate(0deg); }
  25%  { transform: translate3d(2px, -7px, 0) rotate(-2.2deg); }
  50%  { transform: translate3d(0, -3px, 0) rotate(0.6deg); }
  75%  { transform: translate3d(-2px, -8px, 0) rotate(2deg); }
  100% { transform: translate3d(0, 0, 0) rotate(0deg); }
}

/* The /careers hero plane takes the same float, at Owen's request: one motion for
   the plane wherever it appears. Its trigger is [data-plane-float] on the hero
   section rather than on the graphic itself, because the graphic hangs off the
   left edge of the page and its own visible fraction never reaches the observer's
   threshold. */
.hero-plane {
  transform-origin: 60% 60%;
  will-change: transform;
}
.hero--plane.is-floating .hero-plane {
  animation: plane-float 6s ease-in-out infinite;
}

/* ---- Card icon hover ---------------------------------------------------- */
/* Hover on the card lifts and tips its icon, then it settles back. Applied by
   putting .card--hover-icon on the card, currently the four "Wildly Efficient"
   cards, which appear on both /services and /campaign-types.

   Driven from the card rather than the icon itself, because the icon is a 64px
   square in the corner of a much larger card and hovering it exactly is fiddly.
   Guarded by a hover media query so a touch device, where :hover can stick after
   a tap, never gets a permanently mid-animation icon. */

@media (hover: hover) {
  .card--hover-icon .card__icon {
    transform-origin: 50% 80%;
    transition: transform var(--t-base) var(--ease);
    will-change: transform;
  }
  .card--hover-icon:hover .card__icon {
    animation: icon-nudge 0.9s var(--ease);
  }
}

@keyframes icon-nudge {
  0%   { transform: translateY(0) rotate(0deg) scale(1); }
  30%  { transform: translateY(-7px) rotate(-7deg) scale(1.08); }
  55%  { transform: translateY(-3px) rotate(5deg) scale(1.06); }
  78%  { transform: translateY(-5px) rotate(-2deg) scale(1.04); }
  100% { transform: translateY(0) rotate(0deg) scale(1); }
}

/* ==========================================================================
   PAGE SEGUE

   A delayed loading indicator, not a transition. It does not run on every
   navigation: segue.js leaves the click to navigate natively and only adds
   .segue-waiting if the next page is still pending ~350ms later. When that
   happens a deep-navy screen fades up (140ms) over the page being left and one
   scene plays on it, covering a wait that is genuinely happening. There is no
   scheduled fade-out: the arriving document replaces the whole overlay the
   instant it is ready, so the cover simply vanishes with the old page.

   On a fast static site the timer is almost always discarded before it fires
   and nothing shows at all. That is the intended outcome. The numbers to touch
   if this wants retiming are the fade-in below and THRESHOLD_MS in segue.js.

   Nothing in a scene has to stay in lockstep with anything else, because the
   screen itself does not move. That is the point of holding it still: an earlier
   version wiped the navy across behind a flying plane, and the panel and the
   plane had to share identical linear timing forever or the navy visibly slid
   away from the nose.

   Every scene sits in a fixed coordinate box scaled as a unit, so its parts
   cannot drift apart.

   The reduced-motion rule in tokens.css collapses every animation to 0.01ms,
   which would leave the panel stuck over the page. So the cover is never armed
   under reduced motion: segue.js returns before wiring any click, and the
   belt-and-braces reset is at the bottom of this block.
   ========================================================================== */

.segue {
  position: fixed;
  inset: 0;
  /* Above the sticky header (100), its panels (110) and the skip link (200). */
  z-index: 300;
  display: none;
  /* Never eats a click, even mid-transition. */
  pointer-events: none;
  overflow: hidden;
}

/* Armed only by the class segue.js sets on <html> once a load proves slow. */
.segue-waiting .segue { display: block; }

.segue__panel {
  position: absolute;
  inset: 0;
  background: var(--deep-navy);
}

/* ---- Stages ------------------------------------------------------------- */

.segue__stage {
  position: absolute;
  inset: 0;
  display: none;
  place-items: center;
}
/* One scene at a time, chosen by the class segue.js sets alongside
   segue-waiting. */
.segue-waiting.segue-plane .segue__stage--plane,
.segue-waiting.segue-truck .segue__stage--truck,
.segue-waiting.segue-letter .segue__stage--letter { display: grid; }

/* Scaled as a unit rather than reflowed, so scene geometry holds together. */
.segue__scene { position: relative; }
.segue__scene--plane { width: 560px; height: 340px; }
.segue__scene--truck { width: 560px; height: 340px; }
.segue__scene--letter { width: 520px; height: 360px; }
.segue__scene--letter svg { width: 100%; height: 100%; display: block; }

@media (max-width: 900px) {
  .segue__scene--plane,
  .segue__scene--truck { transform: scale(0.8); }
  .segue__scene--letter { transform: scale(0.8); }
}
@media (max-width: 560px) {
  .segue__scene--plane,
  .segue__scene--truck { transform: scale(0.58); }
  .segue__scene--letter { transform: scale(0.62); }
}

/* ---- Plane: flying on the spot ------------------------------------------ */

/* Both hero scenes work the same way: the subject holds the centre and never
   travels, and everything around it does the moving. For the van that is the
   bump and the exhaust; for the plane it is the wind. */

.segue__plane {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 260px;
  height: auto;
  /* Centred on margins, not on a transform, because the transform belongs to the
     hover animation and the two would overwrite each other. */
  margin: -48px 0 0 -130px;
  filter: drop-shadow(0 12px 22px rgba(0, 0, 0, 0.45));
  will-change: transform;
}

.segue-waiting.segue-plane .segue__plane {
  animation: segue-hover 900ms ease-in-out infinite;
}

/* Wind, streaming past and behind it. The streaks are laid out around the
   plane's centre and are drawn before it in the markup, so they pass behind the
   plane rather than over it. */
.segue__wind {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 0;
  height: 0;
}
.segue__wind i {
  position: absolute;
  left: 0;
  height: 4px;
  border-radius: 4px;
  background: rgba(255, 255, 255, 0.7);
  opacity: 0;
}
/* Varied length, weight and height, so the streaks read as moving air rather
   than as a set of identical rules. */
.segue__wind i:nth-child(1) { top: -58px; width: 96px; }
.segue__wind i:nth-child(2) { top: -20px; width: 62px; height: 3px; background: rgba(255, 255, 255, 0.5); }
.segue__wind i:nth-child(3) { top: 16px;  width: 108px; }
.segue__wind i:nth-child(4) { top: 50px;  width: 54px; height: 3px; background: rgba(255, 255, 255, 0.5); }
.segue__wind i:nth-child(5) { top: -38px; width: 72px; }

/* Staggered so the streaks never arrive as a rank. */
.segue-waiting.segue-plane .segue__wind i {
  animation: segue-wind 620ms linear infinite;
}
.segue-waiting.segue-plane .segue__wind i:nth-child(2) { animation-delay: 120ms; }
.segue-waiting.segue-plane .segue__wind i:nth-child(3) { animation-delay: 250ms; }
.segue-waiting.segue-plane .segue__wind i:nth-child(4) { animation-delay: 370ms; }
.segue-waiting.segue-plane .segue__wind i:nth-child(5) { animation-delay: 490ms; }

/* A lazy figure of eight rather than a straight bob, so it reads as flying
   rather than as bouncing on a spring. */
@keyframes segue-hover {
  0%   { transform: translate3d(0, 0, 0) rotate(-1.5deg); }
  28%  { transform: translate3d(-5px, -9px, 0) rotate(1.5deg); }
  55%  { transform: translate3d(4px, 2px, 0) rotate(-0.5deg); }
  78%  { transform: translate3d(6px, -4px, 0) rotate(1deg); }
  100% { transform: translate3d(0, 0, 0) rotate(-1.5deg); }
}
/* Starts ahead of the nose (the plane is 260 wide, so its nose is at +130) and
   streams back past the tail. Beginning at +90 put the streaks inside the
   plane's own silhouette, so they appeared to be born under it. */
@keyframes segue-wind {
  0%   { transform: translateX(210px) scaleX(0.45); opacity: 0; }
  18%  { opacity: 0.85; }
  70%  { opacity: 0.5; }
  100% { transform: translateX(-350px) scaleX(1.35); opacity: 0; }
}

/* ---- Truck: driving on the spot ----------------------------------------- */

/* Centred on margins for the same reason as the plane: the transform is the
   bump animation's. The van renders 159 tall at 260 wide. */
.segue__van {
  position: absolute;
  left: 50%;
  top: 50%;
  width: 260px;
  margin: -80px 0 0 -130px;
  filter: drop-shadow(0 16px 30px rgba(0, 0, 0, 0.32));
}
.segue__van svg { width: 100%; height: auto; }
/* Recoloured for a dark ground: the roadtrip van is navy-bodied, which would be
   a silhouette against this panel. */
.segue__van-body { fill: var(--white); }
.segue__van-stripe { fill: var(--pink); }
.segue__van-glass { fill: var(--arctic); }
.segue__van-lamp { fill: var(--amber-signal); }
/* A slate distinctly darker than the van but lighter than the panel. Filling the
   tyres with --deep-navy made them the same colour as the background, so at this
   size they read as hollow rings rather than as wheels. */
.segue__tyre { fill: var(--segue-tyre); stroke: rgba(255, 255, 255, 0.28); stroke-width: 1.5; }
.segue__hub { fill: var(--white); }
.segue__spoke { stroke: var(--segue-tyre); stroke-width: 1.5; }

/* Exhaust, at the van's rear. Positioned in percentages of the van's own box so
   it stays on the tailpipe if the van is ever resized: the pipe is at roughly
   (6, 70) of the 150x92 artwork. */
.segue__smoke {
  position: absolute;
  left: 3%;
  top: 76%;
  width: 1px;
  height: 1px;
}
.segue__smoke i {
  position: absolute;
  left: 0;
  top: 0;
  width: 18px;
  height: 18px;
  margin: -9px 0 0 -9px;
  border-radius: 50%;
  background: rgba(255, 255, 255, 0.34);
  opacity: 0;
}

/* Three separate loops on purpose: one keyframe set would tie the bump rhythm to
   the wheel speed, and they should not match. */
.segue-waiting.segue-truck .segue__van {
  animation: segue-bump 320ms ease-in-out infinite;
}
.segue-waiting.segue-truck .segue__wheel {
  animation: segue-roll 400ms linear infinite;
  transform-box: fill-box;
  transform-origin: center;
}
/* 620ms on a 200ms stagger, so all three puffs complete a cycle inside the 880ms
   the scene runs for. */
.segue-waiting.segue-truck .segue__smoke i {
  animation: segue-puff 620ms ease-out infinite;
}
.segue-waiting.segue-truck .segue__smoke i:nth-child(2) { animation-delay: 200ms; }
.segue-waiting.segue-truck .segue__smoke i:nth-child(3) { animation-delay: 400ms; }

/* Two bumps and a settle, not a sine wave: it should read as road surface rather
   than as floating. */
@keyframes segue-bump {
  0%   { transform: translateY(0) rotate(0deg); }
  22%  { transform: translateY(-5px) rotate(-0.6deg); }
  45%  { transform: translateY(2px) rotate(0.35deg); }
  70%  { transform: translateY(-3px) rotate(-0.25deg); }
  100% { transform: translateY(0) rotate(0deg); }
}
@keyframes segue-roll {
  from { transform: rotate(0deg); }
  to   { transform: rotate(360deg); }
}
@keyframes segue-puff {
  0%   { transform: translate(0, 0) scale(0.4); opacity: 0; }
  18%  { opacity: 0.5; }
  100% { transform: translate(-58px, -20px) scale(1.7); opacity: 0; }
}

/* ---- Letter: into the envelope, then sealed ----------------------------- */

.segue__sheet-paper { fill: var(--white); }
.segue__sheet-rule { fill: rgba(11, 59, 96, 0.28); }
.segue__sheet-accent { fill: var(--pink); }

.segue__env-front { fill: var(--segue-env-front); }
.segue__env-crease { fill: none; stroke: rgba(11, 59, 96, 0.22); stroke-width: 2; }
/* Lighter than the front, so the closed flap reads as a separate fold of paper
   rather than merging into the body. */
.segue__env-flap { fill: var(--segue-env-flap); }
.segue__env-seal { fill: var(--pink); }

/* Both flap halves pivot on the same hinge: the triangle's own top edge, which
   is the envelope's top edge. */
.segue__env-flap { transform-box: fill-box; transform-origin: center top; }
/* Raised. Drawn behind the sheet, so the sheet drops in front of it. */
.segue-waiting.segue-letter .segue__env-flap--back { transform: scaleY(-1); }
/* Edge-on until its half of the fold begins, so it cannot show through the
   raised half. */
.segue-waiting.segue-letter .segue__env-flap--front { transform: scaleY(0); }

.segue-waiting.segue-letter .segue__sheet {
  animation: segue-post 420ms cubic-bezier(0.4, 0, 0.7, 1) forwards;
}
/* The fold, in two halves that hand over at edge-on. Ease-in on the way down and
   ease-out on the way over, so it reads as one continuous swing. */
.segue-waiting.segue-letter .segue__env-flap--back {
  animation: segue-fold-back 150ms cubic-bezier(0.5, 0, 0.9, 0.6) 420ms forwards;
}
.segue-waiting.segue-letter .segue__env-flap--front {
  animation: segue-fold-front 150ms cubic-bezier(0.1, 0.4, 0.5, 1) 570ms forwards;
}
.segue-waiting.segue-letter .segue__env-seal {
  transform: scale(0);
  transform-box: fill-box;
  transform-origin: center;
  /* Lands at 700 + 165 = 865ms. The letter is a one-shot: it posts, folds and
     seals once and then holds, since the cover stays up for however long the
     load takes rather than for a fixed run. The plane and truck loop instead. */
  animation: segue-stamp 165ms cubic-bezier(0.2, 1.5, 0.4, 1) 700ms forwards;
}

/* The sheet is 150 tall and starts at y=-42, so it spans -42..108. Dropping it
   202px lands it on 160..310, which sits inside the envelope's 150..320 interior
   with 10px to spare at each end. It was 196 tall dropping 224 before, which is
   taller than the envelope's interior: it could not physically be hidden and
   protruded below the bottom edge once the flap had closed over it. */
@keyframes segue-post {
  from { transform: translateY(0); }
  to   { transform: translateY(202px); }
}
@keyframes segue-fold-back {
  from { transform: scaleY(-1); }
  to   { transform: scaleY(0); }
}
@keyframes segue-fold-front {
  from { transform: scaleY(0); }
  to   { transform: scaleY(1); }
}
@keyframes segue-stamp {
  from { transform: scale(0); }
  to   { transform: scale(1); }
}

/* ---- The fade ----------------------------------------------------------- */

/* Fades up over the page being left once a navigation has proven slow, then
   holds. There is no fade-out: the arriving document swaps the whole overlay
   out the instant it is ready, so the cover leaves with the old page. Kept
   short so a load that only just crosses the threshold does not flash a
   half-raised panel before the next page paints. */
.segue-waiting .segue { animation: segue-fade-in 140ms linear forwards; }

@keyframes segue-fade-in {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .segue { display: none !important; }
}
