/* keyping — animated logo.
 * generated by scripts/build_anim.py
 * Motion: KP-921 stab + KP-941 projected turn. Total 2.1s.
 * Pair with keyping-logo.js, which attaches the mask for the turn only
 * and removes the animations when the sequence ends — see the comments
 * there for why both of those matter for sharpness.
 */

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

  /* the stab: hard ease-in with no cushion, compression continuing 0.06s past
     contact, then a 0.24s release. 4:1 asymmetry is what stops the recovery
     reading as a bounce. translateY is never positive after contact. */
  @keyframes kp-stab {
    0%    { transform: translateY(-30px) scaleY(1.08);
            animation-timing-function: cubic-bezier(.5,0,.78,.34); }
    53.1% { transform: translateY(0) scaleY(0.99);
            animation-timing-function: cubic-bezier(.3,.55,.55,1); }
    62.5% { transform: translateY(0) scaleY(0.84);
            animation-timing-function: cubic-bezier(.12,.78,.3,1); }
    100%  { transform: translateY(0) scaleY(1); }
  }

  /* the ping fires on first contact, not at deepest compression */
  @keyframes kp-dot {
    0%   { transform: scale(0.15); opacity: 0; }
    28%  { transform: scale(1.32); opacity: 1; }
    60%  { transform: scale(0.93); }
    82%  { transform: scale(1.03); }
    100% { transform: scale(1); }
  }

  @keyframes kp-ring {
    0%   { transform: scale(0.86); opacity: 0.80; }
    60%  { opacity: 0.18; }
    100% { transform: scale(1.75); opacity: 0; }
  }

/* generated by scripts/build_anim.py — cylinder r=6 at x=48, plates
   r 6->16 and 6->11, thickness 3.5, camera 110 units out.
   x = 48 + r*cos(t) * D/(D + r*sin(t)), 72 frames. do not hand-edit. */
  @keyframes kp-tooth-a {
    0% { transform: translateX(0px) scaleX(1); }
    1.3889% { transform: translateX(0px) scaleX(1.0006); }
    2.7778% { transform: translateX(-0.001px) scaleX(1.0023); }
    4.1667% { transform: translateX(-0.003px) scaleX(1.005); }
    5.5556% { transform: translateX(-0.008px) scaleX(1.0085); }
    6.9444% { transform: translateX(-0.017px) scaleX(1.0122); }
    8.3333% { transform: translateX(-0.031px) scaleX(1.0159); }
    9.7222% { transform: translateX(-0.054px) scaleX(1.0188); }
    11.1111% { transform: translateX(-0.087px) scaleX(1.0202); }
    12.5% { transform: translateX(-0.133px) scaleX(1.0194); }
    13.8889% { transform: translateX(-0.196px) scaleX(1.0152); }
    15.2778% { transform: translateX(-0.279px) scaleX(1.0067); }
    16.6667% { transform: translateX(-0.384px) scaleX(0.9925); }
    18.0556% { transform: translateX(-0.515px) scaleX(0.9715); }
    19.4444% { transform: translateX(-0.674px) scaleX(0.9421); }
    20.8333% { transform: translateX(-0.864px) scaleX(0.9031); }
    22.2222% { transform: translateX(-1.086px) scaleX(0.8532); }
    23.6111% { transform: translateX(-1.343px) scaleX(0.7912); }
    25% { transform: translateX(-1.633px) scaleX(0.7161); }
    26.3889% { transform: translateX(-1.957px) scaleX(0.6273); }
    27.7778% { transform: translateX(-2.313px) scaleX(0.5249); }
    29.1667% { transform: translateX(-2.699px) scaleX(0.4093); }
    30.5556% { transform: translateX(-3.524px) scaleX(0.3407); }
    31.9444% { transform: translateX(-4.969px) scaleX(0.3476); }
    33.3333% { transform: translateX(-6.45px) scaleX(0.35); }
    34.7222% { transform: translateX(-3.069px) scaleX(-0.3475); }
    36.1111% { transform: translateX(-4.627px) scaleX(-0.3397); }
    37.5% { transform: translateX(-5.399px) scaleX(-0.4392); }
    38.8889% { transform: translateX(-5.854px) scaleX(-0.5735); }
    40.2778% { transform: translateX(-6.289px) scaleX(-0.695); }
    41.6667% { transform: translateX(-6.693px) scaleX(-0.8001); }
    43.0556% { transform: translateX(-7.059px) scaleX(-0.8863); }
    44.4444% { transform: translateX(-7.375px) scaleX(-0.9516); }
    45.8333% { transform: translateX(-7.635px) scaleX(-0.9952); }
    47.2222% { transform: translateX(-7.829px) scaleX(-1.0172); }
    48.6111% { transform: translateX(-7.952px) scaleX(-1.0183); }
    50% { transform: translateX(-8px) scaleX(-1); }
    51.3889% { transform: translateX(-7.972px) scaleX(-0.9641); }
    52.7778% { transform: translateX(-7.867px) scaleX(-0.9129); }
    54.1667% { transform: translateX(-7.689px) scaleX(-0.8484); }
    55.5556% { transform: translateX(-7.442px) scaleX(-0.773); }
    56.9444% { transform: translateX(-7.133px) scaleX(-0.6888); }
    58.3333% { transform: translateX(-6.771px) scaleX(-0.5979); }
    59.7222% { transform: translateX(-6.363px) scaleX(-0.502); }
    61.1111% { transform: translateX(-5.921px) scaleX(-0.4029); }
    62.5% { transform: translateX(-5.281px) scaleX(-0.3265); }
    63.8889% { transform: translateX(-3.995px) scaleX(-0.3397); }
    65.2778% { transform: translateX(-2.747px) scaleX(-0.3475); }
    66.6667% { transform: translateX(-1.55px) scaleX(-0.35); }
    68.0556% { transform: translateX(-5.283px) scaleX(0.3476); }
    69.4444% { transform: translateX(-4.127px) scaleX(0.3407); }
    70.8333% { transform: translateX(-2.995px) scaleX(0.3298); }
    72.2222% { transform: translateX(-2.251px) scaleX(0.3655); }
    73.6111% { transform: translateX(-1.886px) scaleX(0.4456); }
    75% { transform: translateX(-1.558px) scaleX(0.5203); }
    76.3889% { transform: translateX(-1.265px) scaleX(0.5892); }
    77.7778% { transform: translateX(-1.01px) scaleX(0.6521); }
    79.1667% { transform: translateX(-0.791px) scaleX(0.7088); }
    80.5556% { transform: translateX(-0.606px) scaleX(0.7594); }
    81.9444% { transform: translateX(-0.453px) scaleX(0.804); }
    83.3333% { transform: translateX(-0.329px) scaleX(0.8427); }
    84.7222% { transform: translateX(-0.231px) scaleX(0.8759); }
    86.1111% { transform: translateX(-0.155px) scaleX(0.9039); }
    87.5% { transform: translateX(-0.1px) scaleX(0.9272); }
    88.8889% { transform: translateX(-0.06px) scaleX(0.9462); }
    90.2778% { transform: translateX(-0.033px) scaleX(0.9614); }
    91.6667% { transform: translateX(-0.016px) scaleX(0.9734); }
    93.0556% { transform: translateX(-0.006px) scaleX(0.9826); }
    94.4444% { transform: translateX(-0.001px) scaleX(0.9894); }
    95.8333% { transform: translateX(0px) scaleX(0.9943); }
    97.2222% { transform: translateX(0.001px) scaleX(0.9975); }
    98.6111% { transform: translateX(0px) scaleX(0.9994); }
    100% { transform: translateX(0px) scaleX(1); }
  }
  @keyframes kp-seam-a {
    0% { opacity: 0; }
    1.3889% { opacity: 0; }
    2.7778% { opacity: 0; }
    4.1667% { opacity: 0; }
    5.5556% { opacity: 0; }
    6.9444% { opacity: 0; }
    8.3333% { opacity: 0; }
    9.7222% { opacity: 0.008; }
    11.1111% { opacity: 0.0231; }
    12.5% { opacity: 0.0472; }
    13.8889% { opacity: 0.0829; }
    15.2778% { opacity: 0.1327; }
    16.6667% { opacity: 0.1994; }
    18.0556% { opacity: 0.2859; }
    19.4444% { opacity: 0.3948; }
    20.8333% { opacity: 0.5288; }
    22.2222% { opacity: 0.69; }
    23.6111% { opacity: 0.8802; }
    25% { opacity: 1; }
    26.3889% { opacity: 1; }
    27.7778% { opacity: 1; }
    29.1667% { opacity: 1; }
    30.5556% { opacity: 1; }
    31.9444% { opacity: 1; }
    33.3333% { opacity: 1; }
    34.7222% { opacity: 1; }
    36.1111% { opacity: 1; }
    37.5% { opacity: 1; }
    38.8889% { opacity: 1; }
    40.2778% { opacity: 1; }
    41.6667% { opacity: 0.8532; }
    43.0556% { opacity: 0.5844; }
    44.4444% { opacity: 0.3607; }
    45.8333% { opacity: 0.1876; }
    47.2222% { opacity: 0.0685; }
    48.6111% { opacity: 0.0058; }
    50% { opacity: 0; }
    51.3889% { opacity: 0; }
    52.7778% { opacity: 0; }
    54.1667% { opacity: 0; }
    55.5556% { opacity: 0; }
    56.9444% { opacity: 0; }
    58.3333% { opacity: 0; }
    59.7222% { opacity: 0; }
    61.1111% { opacity: 0; }
    62.5% { opacity: 0; }
    63.8889% { opacity: 0; }
    65.2778% { opacity: 0; }
    66.6667% { opacity: 0; }
    68.0556% { opacity: 0; }
    69.4444% { opacity: 0; }
    70.8333% { opacity: 0; }
    72.2222% { opacity: 0; }
    73.6111% { opacity: 0; }
    75% { opacity: 0; }
    76.3889% { opacity: 0; }
    77.7778% { opacity: 0; }
    79.1667% { opacity: 0; }
    80.5556% { opacity: 0; }
    81.9444% { opacity: 0; }
    83.3333% { opacity: 0; }
    84.7222% { opacity: 0; }
    86.1111% { opacity: 0; }
    87.5% { opacity: 0; }
    88.8889% { opacity: 0; }
    90.2778% { opacity: 0; }
    91.6667% { opacity: 0; }
    93.0556% { opacity: 0; }
    94.4444% { opacity: 0; }
    95.8333% { opacity: 0; }
    97.2222% { opacity: 0; }
    98.6111% { opacity: 0; }
    100% { opacity: 0; }
  }
  @keyframes kp-tooth-b {
    0% { transform: translateX(0px) scaleX(1); }
    1.3889% { transform: translateX(0px) scaleX(1.0005); }
    2.7778% { transform: translateX(0px) scaleX(1.0018); }
    4.1667% { transform: translateX(-0.001px) scaleX(1.0038); }
    5.5556% { transform: translateX(-0.004px) scaleX(1.0063); }
    6.9444% { transform: translateX(-0.01px) scaleX(1.0088); }
    8.3333% { transform: translateX(-0.021px) scaleX(1.0109); }
    9.7222% { transform: translateX(-0.04px) scaleX(1.012); }
    11.1111% { transform: translateX(-0.069px) scaleX(1.0113); }
    12.5% { transform: translateX(-0.111px) scaleX(1.0081); }
    13.8889% { transform: translateX(-0.169px) scaleX(1.0014); }
    15.2778% { transform: translateX(-0.246px) scaleX(0.9902); }
    16.6667% { transform: translateX(-0.345px) scaleX(0.9733); }
    18.0556% { transform: translateX(-0.471px) scaleX(0.9496); }
    19.4444% { transform: translateX(-0.625px) scaleX(0.9178); }
    20.8333% { transform: translateX(-0.811px) scaleX(0.8767); }
    22.2222% { transform: translateX(-1.031px) scaleX(0.8253); }
    23.6111% { transform: translateX(-1.286px) scaleX(0.7626); }
    25% { transform: translateX(-1.577px) scaleX(0.6879); }
    26.3889% { transform: translateX(-1.904px) scaleX(0.6008); }
    27.7778% { transform: translateX(-2.849px) scaleX(0.6307); }
    29.1667% { transform: translateX(-3.874px) scaleX(0.6595); }
    30.5556% { transform: translateX(-4.945px) scaleX(0.6814); }
    31.9444% { transform: translateX(-6.044px) scaleX(0.6952); }
    33.3333% { transform: translateX(-7.15px) scaleX(0.7); }
    34.7222% { transform: translateX(-1.985px) scaleX(-0.695); }
    36.1111% { transform: translateX(-3.171px) scaleX(-0.6794); }
    37.5% { transform: translateX(-4.386px) scaleX(-0.653); }
    38.8889% { transform: translateX(-5.602px) scaleX(-0.6155); }
    40.2778% { transform: translateX(-6.344px) scaleX(-0.6671); }
    41.6667% { transform: translateX(-6.75px) scaleX(-0.7717); }
    43.0556% { transform: translateX(-7.113px) scaleX(-0.8592); }
    44.4444% { transform: translateX(-7.423px) scaleX(-0.9279); }
    45.8333% { transform: translateX(-7.672px) scaleX(-0.9764); }
    47.2222% { transform: translateX(-7.854px) scaleX(-1.0043); }
    48.6111% { transform: translateX(-7.965px) scaleX(-1.0119); }
    50% { transform: translateX(-8px) scaleX(-1); }
    51.3889% { transform: translateX(-7.96px) scaleX(-0.97); }
    52.7778% { transform: translateX(-7.845px) scaleX(-0.9238); }
    54.1667% { transform: translateX(-7.659px) scaleX(-0.8632); }
    55.5556% { transform: translateX(-7.407px) scaleX(-0.7903); }
    56.9444% { transform: translateX(-7.096px) scaleX(-0.7073); }
    58.3333% { transform: translateX(-6.734px) scaleX(-0.6163); }
    59.7222% { transform: translateX(-6.112px) scaleX(-0.5672); }
    61.1111% { transform: translateX(-5.001px) scaleX(-0.6155); }
    62.5% { transform: translateX(-3.902px) scaleX(-0.653); }
    63.8889% { transform: translateX(-2.835px) scaleX(-0.6794); }
    65.2778% { transform: translateX(-1.814px) scaleX(-0.695); }
    66.6667% { transform: translateX(-0.85px) scaleX(-0.7); }
    68.0556% { transform: translateX(-6.211px) scaleX(0.6952); }
    69.4444% { transform: translateX(-5.265px) scaleX(0.6814); }
    70.8333% { transform: translateX(-4.328px) scaleX(0.6595); }
    72.2222% { transform: translateX(-3.41px) scaleX(0.6307); }
    73.6111% { transform: translateX(-2.524px) scaleX(0.5962); }
    75% { transform: translateX(-1.679px) scaleX(0.557); }
    76.3889% { transform: translateX(-1.302px) scaleX(0.6075); }
    77.7778% { transform: translateX(-1.047px) scaleX(0.6707); }
    79.1667% { transform: translateX(-0.828px) scaleX(0.7272); }
    80.5556% { transform: translateX(-0.641px) scaleX(0.7771); }
    81.9444% { transform: translateX(-0.485px) scaleX(0.8205); }
    83.3333% { transform: translateX(-0.359px) scaleX(0.8578); }
    84.7222% { transform: translateX(-0.257px) scaleX(0.8893); }
    86.1111% { transform: translateX(-0.179px) scaleX(0.9155); }
    87.5% { transform: translateX(-0.119px) scaleX(0.937); }
    88.8889% { transform: translateX(-0.076px) scaleX(0.9542); }
    90.2778% { transform: translateX(-0.045px) scaleX(0.9677); }
    91.6667% { transform: translateX(-0.025px) scaleX(0.9781); }
    93.0556% { transform: translateX(-0.013px) scaleX(0.9859); }
    94.4444% { transform: translateX(-0.005px) scaleX(0.9915); }
    95.8333% { transform: translateX(-0.002px) scaleX(0.9955); }
    97.2222% { transform: translateX(-0.001px) scaleX(0.9981); }
    98.6111% { transform: translateX(0px) scaleX(0.9995); }
    100% { transform: translateX(0px) scaleX(1); }
  }
  @keyframes kp-seam-b {
    0% { opacity: 0; }
    1.3889% { opacity: 0; }
    2.7778% { opacity: 0; }
    4.1667% { opacity: 0; }
    5.5556% { opacity: 0; }
    6.9444% { opacity: 0; }
    8.3333% { opacity: 0; }
    9.7222% { opacity: 0.008; }
    11.1111% { opacity: 0.0231; }
    12.5% { opacity: 0.0472; }
    13.8889% { opacity: 0.0829; }
    15.2778% { opacity: 0.1327; }
    16.6667% { opacity: 0.1994; }
    18.0556% { opacity: 0.2859; }
    19.4444% { opacity: 0.3948; }
    20.8333% { opacity: 0.5288; }
    22.2222% { opacity: 0.69; }
    23.6111% { opacity: 0.8802; }
    25% { opacity: 1; }
    26.3889% { opacity: 1; }
    27.7778% { opacity: 1; }
    29.1667% { opacity: 1; }
    30.5556% { opacity: 1; }
    31.9444% { opacity: 1; }
    33.3333% { opacity: 1; }
    34.7222% { opacity: 1; }
    36.1111% { opacity: 1; }
    37.5% { opacity: 1; }
    38.8889% { opacity: 1; }
    40.2778% { opacity: 1; }
    41.6667% { opacity: 0.8532; }
    43.0556% { opacity: 0.5844; }
    44.4444% { opacity: 0.3607; }
    45.8333% { opacity: 0.1876; }
    47.2222% { opacity: 0.0685; }
    48.6111% { opacity: 0.0058; }
    50% { opacity: 0; }
    51.3889% { opacity: 0; }
    52.7778% { opacity: 0; }
    54.1667% { opacity: 0; }
    55.5556% { opacity: 0; }
    56.9444% { opacity: 0; }
    58.3333% { opacity: 0; }
    59.7222% { opacity: 0; }
    61.1111% { opacity: 0; }
    62.5% { opacity: 0; }
    63.8889% { opacity: 0; }
    65.2778% { opacity: 0; }
    66.6667% { opacity: 0; }
    68.0556% { opacity: 0; }
    69.4444% { opacity: 0; }
    70.8333% { opacity: 0; }
    72.2222% { opacity: 0; }
    73.6111% { opacity: 0; }
    75% { opacity: 0; }
    76.3889% { opacity: 0; }
    77.7778% { opacity: 0; }
    79.1667% { opacity: 0; }
    80.5556% { opacity: 0; }
    81.9444% { opacity: 0; }
    83.3333% { opacity: 0; }
    84.7222% { opacity: 0; }
    86.1111% { opacity: 0; }
    87.5% { opacity: 0; }
    88.8889% { opacity: 0; }
    90.2778% { opacity: 0; }
    91.6667% { opacity: 0; }
    93.0556% { opacity: 0; }
    94.4444% { opacity: 0; }
    95.8333% { opacity: 0; }
    97.2222% { opacity: 0; }
    98.6111% { opacity: 0; }
    100% { opacity: 0; }
  }
/* --kp-t scales the whole sequence (2 = half speed).

   --kp-seam is the knockout hairline, and it is in DEVICE PIXELS on purpose.
   The stroke sits on the tooth, and the tooth is being scaled on x down to
   almost nothing during the crossing — a stroke in user units would be squashed
   with it and the root line, which is the thing that reads as rotation, would
   vanish exactly when it matters. vector-effect: non-scaling-stroke keeps it
   constant, at the cost of not scaling with the logo either.

   2.4px is the signed-off value, tuned around 70-240px tall. Well outside that
   range, drive it from the size instead:

     .kp { --kp-height: 480px; height: var(--kp-height);
            --kp-seam: calc(var(--kp-height) * 0.015); }

   It only ever shows during the 1.3s turn, so this is a nicety, not a defect.

   overflow: the viewBox is the mark AT REST, and the motion overdraws it: the
   fall enters ~36 units above the top edge, and the rings reach ~11 units past
   the left and bottom ones. SVG defaults to overflow: hidden, which crops the
   falling body flat at the top and cuts the rings at bottom-left. Visible only
   helps the mark when it is inline or <object> — an <img> always clips at its
   own viewport, so the full animation needs the inline route (or the Lottie,
   whose canvas bakes the headroom in). */
.kp { --kp-t: 1; --kp-seam: 2.4px; overflow: visible; }

.kp .kp-drop  { transform-box: view-box; transform-origin: 12.0px 80.9px; }
.kp .kp-dot   { transform-box: fill-box; transform-origin: center; }
.kp .kp-ring  { transform-box: fill-box; transform-origin: center; opacity: 0; }
/* the teeth are welded to the shank's surface, so a tooth's origin is its own
   root edge. .kp-in is the outline copy, whose path starts at the true root
   (54.0) rather than buried at 52.0, so its origin is pulled back to match and
   one keyframe set drives both copies. */
.kp .kp-a, .kp .kp-b { transform-box: fill-box; transform-origin: left center; }
.kp .kp-in { transform-origin: -2.0px 50%; }
.kp .kp-sa, .kp .kp-sb { opacity: 0; }

@media (prefers-reduced-motion: no-preference) {
  .kp-play .kp-drop {
    animation: kp-stab calc(0.64s * var(--kp-t)) linear calc(0.06s * var(--kp-t)) both,
               kp-fade calc(.16s * var(--kp-t)) linear calc(0.06s * var(--kp-t)) both;
  }
  .kp-play .kp-dot {
    animation: kp-dot calc(0.44s * var(--kp-t)) cubic-bezier(.25,.9,.3,1)
                      calc(0.4s * var(--kp-t)) both;
  }
  .kp-play .kp-r1 { animation: kp-ring calc(0.76s * var(--kp-t))
      cubic-bezier(.15,.6,.35,1) calc(0.4s * var(--kp-t)) forwards; }
  .kp-play .kp-r2 { animation: kp-ring calc(0.76s * var(--kp-t))
      cubic-bezier(.15,.6,.35,1) calc(0.5s * var(--kp-t)) forwards; }

  /* linear: the easing is baked into the generated frames */
  .kp-play .kp-a  { animation: kp-tooth-a calc(1.3s * var(--kp-t)) linear calc(0.8s * var(--kp-t)) both; }
  .kp-play .kp-b  { animation: kp-tooth-b calc(1.3s * var(--kp-t)) linear calc(0.8s * var(--kp-t)) both; }
  .kp-play .kp-sa { animation: kp-seam-a  calc(1.3s * var(--kp-t)) linear calc(0.8s * var(--kp-t)) both; }
  .kp-play .kp-sb { animation: kp-seam-b  calc(1.3s * var(--kp-t)) linear calc(0.8s * var(--kp-t)) both; }

  .kp-loop .kp-a, .kp-loop .kp-b,
  .kp-loop .kp-sa, .kp-loop .kp-sb { animation-iteration-count: infinite; }
}
