/* =============================================================================
   Motion blocks — an animated overlay on a project page's block
   -----------------------------------------------------------------------------
   Linked only by project pages that have one (tools/projects.py, `motion`).
   The block's own <img> is the animation's LAST frame and stays underneath, so
   with no JS, or under reduced motion, the block is the finished picture.
   js/block-motion.js adds `.is-armed` (show the overlay at its first frame) and
   then `.is-playing` (run it) once the block is in view and its pixel reveal
   has finished.

   Every layer is placed in the Figma frame's own coordinates, as a share of
   that frame, so the overlay scales with the block at every width.
   ========================================================================== */

.rdk-project__block--motion {
  position: relative;
}

.rdk-motion {
  position: absolute;
  left: 0;
  top: 0;
  opacity: 0;
  pointer-events: none;
  /* Under the pixel reveal's canvas (z-index 2), over the still. */
  z-index: 1;
}

.rdk-project__block--motion.is-armed .rdk-motion,
.rdk-project-hero--motion.is-armed .rdk-motion {
  opacity: 1;
}

.rdk-motion > * {
  position: absolute;
  display: block;
  max-width: none;
}

/* -----------------------------------------------------------------------------
   Uptycs — "Shift your cybersecurity up" (brand file mDqab43PDox7jHkn5waOs9,
   frames "Shift Up 1-3", 1920 x 1080).

   THE STAGE IS THE SLIDE, NOT THE BLOCK. The website frame places the slide at
   1540 x 866 in a 1520 x 864 block, top-left, and clips the rest — so the
   stage overhangs the block by the same amount and every layer below is a
   share of 1920 x 1080.

   The prototype, reproduced:
     frame 1 holds 0.8s
     -> 0.3s smart-animate, Figma ease-out: "up" rises 60 while the purple
        badge grows from a 7-tall bar at y 541 to 141 tall at y 422
     -> 0.8s smart-animate, Figma ease-in-out: the dark ellipse that hides
        the stripe ring shrinks into the top-right corner, uncovering the
        stripes in the border round the panel.
   Figma's ease-out and ease-in-out are cubic-bezier(0, 0, 0.58, 1) and
   (0.42, 0, 0.58, 1). The 8-9 unit sideways drift two stripe layers make in
   the last step is left out.
   ---------------------------------------------------------------------------- */

.rdk-motion--shift-up {
  width: calc(1540 / 1520 * 100%);
  height: calc(866 / 864 * 100%);
  background: #050314;
}

.rdk-motion--shift-up .rdk-motion__ring,
.rdk-motion--shift-up .rdk-motion__panel {
  inset: 0;
  width: 100%;
  height: 100%;
}

/* The ellipse. Its frame-2 box (-399, -361, 2639 x 1736) covers the whole
   slide; its frame-3 box (1691, -361, 549 x 361) is a sliver off the
   top-right corner. */
.rdk-motion--shift-up .rdk-motion__sweep {
  left: calc(-399 / 1920 * 100%);
  top: calc(-361 / 1080 * 100%);
  width: calc(2639 / 1920 * 100%);
  height: calc(1736 / 1080 * 100%);
  border-radius: 50%;
  background: #050314;
}

.is-playing .rdk-motion--shift-up .rdk-motion__sweep {
  left: calc(1691 / 1920 * 100%);
  width: calc(549 / 1920 * 100%);
  height: calc(361 / 1080 * 100%);
  transition: left 0.8s cubic-bezier(0.42, 0, 0.58, 1) 1.1s,
              width 0.8s cubic-bezier(0.42, 0, 0.58, 1) 1.1s,
              height 0.8s cubic-bezier(0.42, 0, 0.58, 1) 1.1s;
}

/* The badge, at its frame-2/3 box (1476, 422, 120 x 141). Frame 1 has it at
   (1476, 541, 120 x 7) and invisible: scaled to 7/141 about its top edge,
   then moved down the 119 between the two tops. */
.rdk-motion--shift-up .rdk-motion__badge {
  left: calc(1476 / 1920 * 100%);
  top: calc(422 / 1080 * 100%);
  width: calc(120 / 1920 * 100%);
  height: calc(141 / 1080 * 100%);
  transform-origin: 50% 0;
  transform: translateY(calc(119 / 141 * 100%)) scaleY(calc(7 / 141));
  opacity: 0;
}

/* "up", at its frame-2/3 box (1468, 386, 132 x 110); 60 lower in frame 1. */
.rdk-motion--shift-up .rdk-motion__up {
  left: calc(1468 / 1920 * 100%);
  top: calc(386 / 1080 * 100%);
  width: calc(132 / 1920 * 100%);
  height: calc(110 / 1080 * 100%);
  transform: translateY(calc(60 / 110 * 100%));
}

.is-playing .rdk-motion--shift-up .rdk-motion__badge,
.is-playing .rdk-motion--shift-up .rdk-motion__up {
  transform: none;
  opacity: 1;
  transition: transform 0.3s cubic-bezier(0, 0, 0.58, 1) 0.8s,
              opacity 0.3s cubic-bezier(0, 0, 0.58, 1) 0.8s;
}

/* -----------------------------------------------------------------------------
   Uptycs — the logo's construction guides (block 1, node 119:5443).
   No prototype in the file for this one; the motion is ours. The base is the
   block with its guides and labels hidden, the lines draw on over it in the
   node's own 1520 x 880 units, and the labels are crops of the finished still
   that fade up last, so the end frame IS the still.
   ---------------------------------------------------------------------------- */

.rdk-motion--guides {
  width: 100%;
  height: 100%;
}

.rdk-motion--guides .rdk-motion__base,
.rdk-motion--guides .rdk-motion__lines,
.rdk-motion--guides .rdk-motion__labels {
  inset: 0;
  width: 100%;
  height: 100%;
}

.rdk-motion__lines {
  fill: none;
  stroke: #00aeef;
  stroke-width: 0.588;
}

/* Every shape carries pathLength="1", so one dash the length of the shape
   hides it at offset 1 and shows it whole at 0, whatever its real length. */
.rdk-motion__lines > * {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
}

.is-playing .rdk-motion__lines > * {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 0.7s cubic-bezier(0.22, 1, 0.36, 1) var(--d, 0s);
}

.rdk-motion__lines .rdk-motion__tick {
  stroke-dasharray: none;
  opacity: 0;
}

.is-playing .rdk-motion__lines .rdk-motion__tick {
  opacity: 1;
  transition: opacity 0.3s ease 0.95s;
}

.rdk-motion--guides .rdk-motion__labels {
  opacity: 0;
}

.is-playing .rdk-motion--guides .rdk-motion__labels {
  opacity: 1;
  transition: opacity 0.4s ease 1.25s;
}

/* -----------------------------------------------------------------------------
   Uptycs hero (node 84:6136) — the glow drifts in like a cloud, then the
   dashed ring sweeps round from its left end. No prototype for this one; the
   ring's sweep borrows the "Shift Up" ease-in-out.

   THE STAGE IS THE ARTWORK'S OWN 1600 x 897 BOX, NOT THE HERO'S. The phone
   crops the hero to a portrait cover, and a mask on a cover-fitted <img> is
   placed against the element, not the picture — so the ring's centre would
   land in the wrong place there. The stage is as tall as the hero and as wide
   as the artwork's aspect makes it, centred: exactly what `cover` does, so
   the layers and the mask share one coordinate system at every width.
   ---------------------------------------------------------------------------- */

@property --sweep {
  syntax: '<angle>';
  inherits: false;
  initial-value: 0deg;
}

.rdk-project-hero--motion .rdk-project-hero__logo {
  z-index: 2;
}

.rdk-motion--hero {
  inset: 0;
  width: 100%;
  height: 100%;
  overflow: clip;
}

.rdk-motion--uptycs {
  background: #050314;
}

.rdk-motion--hero .rdk-motion__stage {
  top: 0;
  bottom: 0;
  left: 50%;
  aspect-ratio: 1600 / 897;
  translate: -50% 0;
}

.rdk-motion--hero .rdk-motion__stage > img,
.rdk-motion--hero .rdk-motion__stage > svg {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
}

/* The glow: up from below and a touch large, settling as it thickens. */
.rdk-motion--hero .rdk-motion__glow {
  opacity: 0;
  translate: 0 8%;
  scale: 1.12;
}

.is-playing .rdk-motion--hero .rdk-motion__glow {
  opacity: 1;
  translate: 0 0;
  scale: 1;
  transition: opacity 2.4s cubic-bezier(0.22, 1, 0.36, 1),
              translate 3s cubic-bezier(0.22, 1, 0.36, 1),
              scale 3s cubic-bezier(0.22, 1, 0.36, 1);
}

/* The ring. A conic mask about the circle's own centre (586, -383 of the
   1600 x 897 frame) opens from 214deg — just past the arc's left end at 211 —
   back round to 104, past its right end at 118. The 6deg ramp at the leading
   edge is what makes it a sweep rather than a wipe. */
.rdk-motion--hero .rdk-motion__ring {
  --mask: conic-gradient(from calc(214deg - var(--sweep)) at 36.625% -42.7%,
          transparent 0deg, #000 6deg, #000 var(--sweep), transparent var(--sweep));
  -webkit-mask-image: var(--mask);
          mask-image: var(--mask);
}

.is-playing .rdk-motion--hero .rdk-motion__ring {
  --sweep: 112deg;
  transition: --sweep 0.9s cubic-bezier(0.42, 0, 0.58, 1) 0.4s;
}

/* The phone (client, 2026-10-29: "fit it better in mobile layout"). The
   hero's portrait cover crop kept only the bottom of the arc, cut off at both
   sides and running into the panel. Here the artwork is drawn a little wider
   than the screen instead and sits just above the panel's shoulders, so the
   whole arc reads from edge to edge. The still gets the same box, so a page
   with no JS, or under reduced motion, shows the same picture; the ground
   above it is the artwork's own. */
.rdk-project-hero:has(.rdk-motion--uptycs) {
  background: #050314;
}

@media (max-width: 767px) {
  .rdk-motion--uptycs .rdk-motion__stage,
  .rdk-project-hero:has(.rdk-motion--uptycs) .rdk-project-hero__shot {
    position: absolute;
    top: auto;
    /* 7%, not less: the artwork's foot carries colour, so it has to end
       under the panel's shoulders or a seam shows above them. */
    bottom: 7%;
    left: 50%;
    width: 115%;
    height: auto;
    aspect-ratio: 1600 / 897;
    translate: -50% 0;
  }
}

/* -----------------------------------------------------------------------------
   VideoElephant (node 84:5366). No prototype; the motion is ours.
   ---------------------------------------------------------------------------- */

/* The hero: the glow drifts in (the shared rule above), then the two white
   lines draw on, the one behind the glow first. Butt caps, as the node has
   them, so a half-drawn line ends square rather than in a dot. */
.rdk-motion--ve-hero {
  background: #78d3ff;
}

.rdk-motion--ve-hero .rdk-motion__lines,
.rdk-motion--curve .rdk-motion__lines {
  stroke-width: 32;
  stroke-linecap: butt;
}

.rdk-motion--ve-hero .rdk-motion__lines {
  stroke: #fff;
}

.is-playing .rdk-motion--ve-hero .rdk-motion__lines > *,
.is-playing .rdk-motion--curve .rdk-motion__lines > * {
  transition-duration: 1.5s;
  transition-timing-function: cubic-bezier(0.65, 0, 0.35, 1);
}

/* Blocks 3 and 6: the light-blue curve draws on behind the screens. The
   ground is the block's own colour (inline), then the curve, then everything
   that sits above it in the node, exported transparent. */
.rdk-motion--curve {
  inset: 0;
  width: 100%;
  height: 100%;
}

.rdk-motion--curve .rdk-motion__lines,
.rdk-motion--curve .rdk-motion__fg,
.rdk-motion--hearts .rdk-motion__base {
  inset: 0;
  width: 100%;
  height: 100%;
  max-width: none;
}

.rdk-motion--curve .rdk-motion__lines {
  stroke: #78d3ff;
}

.is-playing .rdk-motion--curve .rdk-motion__lines > * {
  transition-delay: calc(var(--d, 0s) + 0.15s);
}

/* Block 5, left: the heart badges pop in, one after another. Each is placed
   in the half's own 744 x 730 units. */
.rdk-motion--hearts {
  inset: 0;
  width: 100%;
  height: 100%;
}

.rdk-motion--hearts .rdk-motion__heart {
  left: calc(var(--x) / 744 * 100%);
  top: calc(var(--y) / 730 * 100%);
  width: calc(var(--w) / 744 * 100%);
  height: calc(var(--h) / 730 * 100%);
  max-width: none;
  scale: 0;
  opacity: 0;
}

.is-playing .rdk-motion--hearts .rdk-motion__heart {
  scale: 1;
  opacity: 1;
  transition: scale 0.55s cubic-bezier(0.34, 1.8, 0.64, 1) calc(var(--d) + 0.2s),
              opacity 0.2s ease calc(var(--d) + 0.2s);
}

/* The nav's scrim (node 84:5373's "top blur gradient"): a frosted band over
   the top of the hero, fading out by 293 of the 897, so the nav reads over
   the white line that runs under it. Above the overlay, under the logo. */
.rdk-project-hero--scrim::after {
  content: "";
  position: absolute;
  inset: 0 0 auto;
  height: calc(293 / 897 * 100%);
  z-index: 2;
  pointer-events: none;
  background: rgb(103 205 255 / 0.55);
  -webkit-backdrop-filter: blur(40px);
          backdrop-filter: blur(40px);
  -webkit-mask-image: linear-gradient(#000, #000 25%, transparent);
          mask-image: linear-gradient(#000, #000 25%, transparent);
}

/* The VideoElephant hero on the phone: the portrait cover crop kept almost
   none of either line. The artwork is drawn half as wide again as the screen
   instead, sitting on the panel; the ground around it is flat, so the
   hero's own colour fills the rest. The still takes the same box. */
.rdk-project-hero:has(.rdk-motion--ve-hero) {
  background: #78d3ff;
}

@media (max-width: 767px) {
  .rdk-motion--ve-hero .rdk-motion__stage,
  .rdk-project-hero:has(.rdk-motion--ve-hero) .rdk-project-hero__shot {
    position: absolute;
    /* Its foot under the panel's shoulders (Uptycs' 7%), so neither line
       ends in mid-air; at 150% that leaves the logo point within ~15px of
       the hero's centre. */
    top: auto;
    bottom: 7%;
    left: 50%;
    width: 150%;
    height: auto;
    aspect-ratio: 1600 / 897;
    translate: -50% 0;
  }
}

/* -----------------------------------------------------------------------------
   Collibra — the DFW airport screens (node 1202:123182, 1520 x 1108).

   Three 1080 x 1920 videos over their cards, each 401 x 713 at y 197 and the
   x the markup gives it. Every number is a share of the frame, so the cards
   stay registered to the still at every width. A video only shows once it is
   actually playing (`.is-on`, from js/block-motion.js) — before its first
   frame it is the still's card underneath, never an empty box.
   ---------------------------------------------------------------------------- */
.rdk-motion--videos {
  width: 100%;
  height: 100%;
  opacity: 1;
}

.rdk-motion--videos video {
  left: calc(var(--x) / 1520 * 100%);
  top: calc(197 / 1108 * 100%);
  width: calc(401 / 1520 * 100%);
  height: calc(713 / 1108 * 100%);
  object-fit: cover;
  opacity: 0;
  transition: opacity 0.3s ease;
}

.rdk-motion--videos video.is-on {
  opacity: 1;
}
