/* The connecting screen: meeples dying until the stream arrives.
 *
 * --death is how long one death takes, and the only speed setting here: the
 * cycle, the scene switching and the hold are all worked out from it.
 *
 * Fourteen scenes share a cycle of fourteen --death. Only .connecting-scene runs on that
 * cycle: --at is the slot a scene sits in, --from is the slot this page load opens
 * on, and every delay comes out negative so the opening scene is showing from the
 * first frame. Everything inside a scene loops once per --death, which lines up
 * with its slot, and adding a death changes the cycle without touching any other
 * death's keyframes. A death must be over by 90%, the end of the hold.
 *
 * Without the motion block below this is the boulder scene, still, with the rock
 * about to land: the reduced-motion screen.
 *
 * The hold: the stream opens with the page, so the board can arrive at any time.
 * For 90% of a --death the page is hidden and the screen shown, which covers a
 * whole death and ends before the next starts. After that the screen stays only while
 * .connecting-pending does, so a connection slower than the hold waits no longer
 * than it would have. These animations sit on .game-page and the screen because
 * neither is patched: one on #game-content would restart on every re-render.
 * The screen's scenes set their own visibility, so it goes by opacity.
 *
 * SVG transforms here are in user units, and an origin in px is a point in the
 * element's own coordinates: a figure's feet are 6px 15px. */

.game-page {
    --death: 2.5s;
}

.connecting-state {
    --from: 0;
    position: fixed;
    inset: 0;
    display: flex;
    align-items: center;
    justify-content: center;
    visibility: visible;
    opacity: 0;
    pointer-events: none;
}

.game-page:has(.connecting-pending) .connecting-state {
    opacity: 1 !important;
}

.connecting-state--from-boulder { --from: 0; }
.connecting-state--from-arrows { --from: 1; }
.connecting-state--from-plank { --from: 2; }
.connecting-state--from-ambush { --from: 3; }
.connecting-state--from-troll { --from: 4; }
.connecting-state--from-kraken { --from: 5; }
.connecting-state--from-serpent { --from: 6; }
.connecting-state--from-dragon { --from: 7; }
.connecting-state--from-fort { --from: 8; }
.connecting-state--from-barracks { --from: 9; }
.connecting-state--from-griffin { --from: 10; }
.connecting-state--from-siege { --from: 11; }
.connecting-state--from-ceasefire { --from: 12; }
.connecting-state--from-palisade { --from: 13; }

.connecting-status {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
}

.connecting-stage {
    display: grid;
    width: min(16rem, 80vw);
}

.connecting-scene {
    grid-area: 1 / 1;
    visibility: hidden;
}

.connecting-scene--boulder { --at: 0; visibility: visible; }
.connecting-scene--arrows { --at: 1; }
.connecting-scene--plank { --at: 2; }
.connecting-scene--ambush { --at: 3; }
.connecting-scene--troll { --at: 4; }
.connecting-scene--kraken { --at: 5; }
.connecting-scene--serpent { --at: 6; }
.connecting-scene--dragon { --at: 7; }
.connecting-scene--fort { --at: 8; }
.connecting-scene--barracks { --at: 9; }
.connecting-scene--griffin { --at: 10; }
.connecting-scene--siege { --at: 11; }
.connecting-scene--ceasefire { --at: 12; }
.connecting-scene--palisade { --at: 13; }

.connecting-art {
    display: block;
    width: 100%;
    height: auto;
    overflow: visible;
}

/* The sea swallows what falls in, rather than letting it drop over the caption. */
.connecting-scene--plank .connecting-art {
    overflow: hidden;
}

.connecting-text {
    font-style: italic;
}

/* The cast. */
.connecting-victim { fill: var(--text-1); }
.connecting-foe { fill: var(--text-4); }

/* Beasts wear the brass rim they have on the board. */
.connecting-beast {
    fill: var(--text-4);
    stroke: var(--color-accent);
    stroke-width: 0.35;
    stroke-linejoin: round;
    paint-order: stroke;
}

.connecting-beast .connecting-eye { stroke: none; }

.connecting-eye { fill: var(--surface-1); }
.connecting-ground { fill: var(--border-2); }
.connecting-hull { fill: var(--border-2); }
.connecting-water { fill: var(--color-info-bg); }
.connecting-arrow { fill: var(--text-2); opacity: 0; }
.connecting-splat { fill: var(--color-destructive); opacity: 0; }
.connecting-splash { fill: var(--color-info); opacity: 0; }

.connecting-boulder {
    fill: var(--text-3);
    stroke: var(--border-2);
    stroke-width: 1;
}

.connecting-cloud {
    fill: var(--surface-4);
    stroke: var(--border-2);
    stroke-width: 0.5;
    opacity: 0;
}

/* Each moving part pivots on its feet. */
.connecting-boulder-victim,
.connecting-arrows-victim,
.connecting-plank-victim,
.connecting-ambush-victim,
.connecting-ambush-victim,
.connecting-troll,
.connecting-troll-victim,
.connecting-kraken,
.connecting-serpent,
.connecting-serpent-victim,
.connecting-dragon,
.connecting-dragon-victim,
.connecting-barracks-victim,
.connecting-griffin-victim,
.connecting-ceasefire-victim,
.connecting-ceasefire-foe,
.connecting-palisade-victim {
    transform-origin: 6px 15px;
}

/* The wing beats from the shoulder. */
.connecting-griffin-wing {
    transform-origin: 7px 9.6px;
}

/* The thrown meeple tumbles about its middle. */
.connecting-siege-victim {
    transform-origin: 6px 8px;
}

/* The arm's own origin is its pivot. The smear runs down from where the meeple hit. */
.connecting-siege-arm {
    transform-origin: 0 0;
}

.connecting-siege-smear {
    transform-origin: 64px 24px;
}

.connecting-palisade-drip {
    transform-origin: 62px 15px;
}

/* The fort goes over on the corner nearest the meeple. */
.connecting-fort {
    transform-origin: 22px 21px;
}

.connecting-longship { fill: var(--color-accent); }
.connecting-flame { fill: var(--color-destructive); opacity: 0; }
.connecting-flame--core { fill: var(--color-accent); }
.connecting-fort { fill: var(--text-3); }
.connecting-barracks { fill: var(--text-3); }
.connecting-troop { fill: var(--color-info); opacity: 0; }

.connecting-dust {
    fill: var(--surface-4);
    stroke: var(--border-2);
    stroke-width: 0.5;
    opacity: 0;
}

.connecting-griffin { opacity: 0; }
.connecting-stake { fill: var(--text-3); }
.connecting-palisade-drip { fill: var(--color-destructive); opacity: 0; }
.connecting-griffin-wing { fill: var(--text-3); }
.connecting-engine { fill: var(--text-4); }
.connecting-siege-wall { fill: var(--text-3); }
.connecting-siege-smear { fill: var(--color-destructive); opacity: 0; }

.connecting-ship {
    transform-origin: 52px 38px;
}

.connecting-arrows-spear,
.connecting-ambush-spear,
.connecting-serpent-spear,
.connecting-troll-spear,
.connecting-ceasefire-flag,
.connecting-palisade-spear {
    transform-origin: 0.25px 15px;
}

@media (prefers-reduced-motion: no-preference) {
    .game-page {
        animation: connecting-hold-page calc(var(--death) * 0.9) step-end;
    }

    .connecting-state {
        animation: connecting-hold-screen calc(var(--death) * 0.9) step-end;
    }

    .connecting-scene {
        animation: connecting-scene-on calc(var(--death) * 14) step-end infinite;
        animation-delay: calc((var(--at) - var(--from) - 14) * var(--death));
    }

    .connecting-scene * {
        animation-duration: var(--death);
        animation-timing-function: linear;
        animation-iteration-count: infinite;
    }

    /* Boulder. */
    .connecting-boulder { animation-name: connecting-boulder-fall; }
    .connecting-boulder-victim { animation-name: connecting-boulder-squash; }
    .connecting-boulder-splat { animation-name: connecting-splat-at-7; }

    /* Arrows. */
    .connecting-arrow--1 { animation-name: connecting-arrow-1; }
    .connecting-arrow--2 { animation-name: connecting-arrow-2; }
    .connecting-arrow--3 { animation-name: connecting-arrow-3; }
    .connecting-arrows-victim { animation-name: connecting-arrows-topple; }
    .connecting-arrows-spear { animation-name: connecting-arrows-drop; }
    .connecting-arrows-splat { animation-name: connecting-splat-at-17; }

    /* The plank. */
    .connecting-plank-victim { animation-name: connecting-plank-walk; }
    .connecting-plank-pirate { animation-name: connecting-plank-follow; }
    .connecting-splash--1 { animation-name: connecting-splash-1; }
    .connecting-splash--2 { animation-name: connecting-splash-2; }
    .connecting-splash--3 { animation-name: connecting-splash-3; }

    /* Ambush. */
    .connecting-ambush-foe--left { animation-name: connecting-foe-left; }
    .connecting-ambush-foe--right { animation-name: connecting-foe-right; }
    .connecting-ambush-victim { animation-name: connecting-ambush-down; }
    .connecting-ambush-spear { animation-name: connecting-ambush-fly; }
    .connecting-cloud { animation-name: connecting-cloud-scuffle; }
    .connecting-ambush-splat { animation-name: connecting-splat-at-17; }

    /* Troll. */
    .connecting-troll { animation-name: connecting-troll-swing; }
    .connecting-troll-victim { animation-name: connecting-troll-launch; }
    .connecting-troll-spear { animation-name: connecting-troll-drop; }
    .connecting-troll-splat { animation-name: connecting-splat-at-72; }

    /* Kraken. */
    .connecting-kraken { animation-name: connecting-kraken-rise; }
    .connecting-ship { animation-name: connecting-ship-sink; }

    /* Serpent. */
    .connecting-serpent { animation-name: connecting-serpent-strike; }
    .connecting-serpent-victim { animation-name: connecting-serpent-gone; }
    .connecting-serpent-spear { animation-name: connecting-serpent-drop; }
    .connecting-serpent-splat { animation-name: connecting-splat-at-44; }

    /* Dragon. */
    .connecting-dragon { animation-name: connecting-dragon-swoop; }
    .connecting-flame { animation-name: connecting-flame-jet; }
    .connecting-dragon-victim { animation-name: connecting-dragon-char; }
    .connecting-dragon-victim .connecting-eye { animation-name: connecting-dragon-blink; }

    /* Fort. */
    .connecting-fort { animation-name: connecting-fort-fall; }
    .connecting-fort-victim { animation-name: connecting-fort-gone; }
    .connecting-fort-splat { animation-name: connecting-splat-at-58; }
    .connecting-dust { animation-name: connecting-dust-puff; }

    /* Barracks. They set off one after another, each behind the building. */
    .connecting-troop { animation-name: connecting-troop-charge; }
    .connecting-troop--1 { animation-delay: calc(var(--death) * 0.1); }
    .connecting-troop--2 { animation-delay: calc(var(--death) * 0.175); }
    .connecting-troop--3 { animation-delay: calc(var(--death) * 0.25); }
    .connecting-troop--4 { animation-delay: calc(var(--death) * 0.325); }
    .connecting-barracks-victim { animation-name: connecting-barracks-trample; }
    .connecting-barracks-splat { animation-name: connecting-splat-at-33; }

    /* Griffin. The wing beats eight times a death, so it stays in step with the loop. */
    .connecting-griffin { animation-name: connecting-griffin-fly; }
    .connecting-griffin-wing {
        animation-name: connecting-griffin-flap;
        animation-duration: calc(var(--death) / 8);
    }
    .connecting-griffin-victim { animation-name: connecting-griffin-carried; }
    .connecting-griffin-splat { animation-name: connecting-splat-at-72; }

    /* Siege engine. */
    .connecting-siege-arm { animation-name: connecting-siege-swing; }
    .connecting-siege-victim { animation-name: connecting-siege-flight; }
    .connecting-siege-smear { animation-name: connecting-siege-streak; }
    .connecting-siege-splat { animation-name: connecting-splat-at-72; }

    /* Ceasefire. */
    .connecting-ceasefire-victim { animation-name: connecting-ceasefire-hammered; }
    .connecting-ceasefire-victim .connecting-eye { animation-name: connecting-late-blink; }
    .connecting-ceasefire-flag { animation-name: connecting-ceasefire-flag-falls; }
    .connecting-ceasefire-foe { animation-name: connecting-ceasefire-bonk; }

    /* Palisade. */
    .connecting-stake--1 { animation-name: connecting-stake-1; }
    .connecting-stake--2 { animation-name: connecting-stake-2; }
    .connecting-stake--3 { animation-name: connecting-stake-3; }
    .connecting-stake--4 { animation-name: connecting-stake-4; }
    .connecting-stake--5 { animation-name: connecting-stake-5; }
    .connecting-palisade-victim { animation-name: connecting-palisade-lifted; }
    .connecting-palisade-victim .connecting-eye { animation-name: connecting-late-blink; }
    .connecting-palisade-spear { animation-name: connecting-palisade-drop; }
    .connecting-palisade-drip { animation-name: connecting-palisade-run; }
}

@keyframes connecting-hold-page {
    from, to { visibility: hidden; }
}

@keyframes connecting-hold-screen {
    from, to { opacity: 1; }
}

/* Off just before its fourteenth of the cycle is up, so two scenes never show at once. */
@keyframes connecting-scene-on {
    0% { visibility: visible; }
    7.14%, 100% { visibility: hidden; }
}

@keyframes connecting-boulder-fall {
    0% { transform: none; animation-timing-function: ease-in; }
    28% { transform: translateY(22px); }
    34% { transform: translateY(19px); }
    40%, 100% { transform: translateY(22px); }
}

@keyframes connecting-boulder-squash {
    0%, 27.6% { transform: none; }
    28%, 100% { transform: scale(1.5, 0.14); }
}

@keyframes connecting-splat-at-7 {
    0%, 27.6% { opacity: 0; transform: scale(0.2); }
    32% { opacity: 1; transform: scale(1.15); }
    36%, 100% { opacity: 1; transform: none; }
}

@keyframes connecting-splat-at-17 {
    0%, 67.6% { opacity: 0; transform: scale(0.2); }
    72% { opacity: 1; transform: scale(1.15); }
    76%, 100% { opacity: 1; transform: none; }
}

@keyframes connecting-arrow-1 {
    0%, 4% { opacity: 0; transform: translate(-40px, -5px); }
    4.4% { opacity: 1; transform: translate(-40px, -5px); }
    16%, 100% { opacity: 1; transform: none; }
}

@keyframes connecting-arrow-2 {
    0%, 24% { opacity: 0; transform: translate(-40px, -3px); }
    24.4% { opacity: 1; transform: translate(-40px, -3px); }
    36%, 100% { opacity: 1; transform: none; }
}

@keyframes connecting-arrow-3 {
    0%, 44% { opacity: 0; transform: translate(-40px, -7px); }
    44.4% { opacity: 1; transform: translate(-40px, -7px); }
    56%, 100% { opacity: 1; transform: none; }
}

@keyframes connecting-arrows-topple {
    0%, 15.6% { transform: none; }
    18% { transform: rotate(5deg); }
    24%, 35.6% { transform: none; }
    38% { transform: rotate(8deg); }
    44%, 55.6% { transform: rotate(2deg); }
    58% { transform: rotate(12deg); animation-timing-function: ease-in; }
    68%, 100% { transform: translateY(-6px) rotate(90deg); }
}

@keyframes connecting-arrows-drop {
    0%, 58% { transform: none; animation-timing-function: ease-in; }
    68%, 100% { transform: rotate(-90deg); }
}

@keyframes connecting-plank-walk {
    0% { transform: none; }
    8% { transform: translate(4px, -1px); }
    16% { transform: translate(8px, 0); }
    24% { transform: translate(12px, -1px); }
    32% { transform: translate(16px, 0); }
    40% { transform: translate(20px, -1px); }
    48% { transform: translate(24px, 0); animation-timing-function: ease-in; }
    68% { transform: translate(29px, 26px) rotate(130deg); opacity: 1; }
    68.4%, 100% { transform: translate(29px, 26px) rotate(130deg); opacity: 0; }
}

@keyframes connecting-plank-follow {
    0% { transform: none; }
    48%, 100% { transform: translateX(12px); }
}

@keyframes connecting-splash-1 {
    0%, 63.6% { opacity: 0; transform: none; }
    64% { opacity: 1; transform: none; }
    76% { opacity: 1; transform: translate(-5px, -11px); }
    92%, 100% { opacity: 0; transform: translate(-7px, 0); }
}

@keyframes connecting-splash-2 {
    0%, 63.6% { opacity: 0; transform: none; }
    64% { opacity: 1; transform: none; }
    76% { opacity: 1; transform: translateY(-15px); }
    92%, 100% { opacity: 0; transform: translateY(0); }
}

@keyframes connecting-splash-3 {
    0%, 63.6% { opacity: 0; transform: none; }
    64% { opacity: 1; transform: none; }
    76% { opacity: 1; transform: translate(5px, -11px); }
    92%, 100% { opacity: 0; transform: translate(7px, 0); }
}

@keyframes connecting-foe-left {
    0% { transform: translateX(-30px); }
    24%, 100% { transform: none; }
}

@keyframes connecting-foe-right {
    0% { transform: translateX(30px); }
    24%, 100% { transform: none; }
}

@keyframes connecting-ambush-down {
    0%, 27.6% { transform: none; }
    28%, 100% { transform: translateY(-6px) rotate(90deg); }
}

@keyframes connecting-ambush-fly {
    0%, 32% { transform: none; }
    48% { transform: translate(-5px, -14px) rotate(-200deg); }
    64% { transform: translate(-13px, 0) rotate(-450deg); }
    70% { transform: translate(-14px, -2px) rotate(-450deg); }
    76%, 100% { transform: translate(-14px, 0) rotate(-450deg); }
}

@keyframes connecting-cloud-scuffle {
    0%, 23.6% { opacity: 0; transform: scale(0.3); }
    26% { opacity: 1; transform: scale(1); }
    32% { opacity: 1; transform: translate(-2px, 1px) rotate(-6deg); }
    40% { opacity: 1; transform: translate(2px, -1px) rotate(5deg) scale(1.05); }
    48% { opacity: 1; transform: translate(-1px, -1px) rotate(-4deg); }
    56% { opacity: 1; transform: translate(2px, 1px) rotate(6deg) scale(1.05); }
    67.6% { opacity: 1; transform: none; }
    68%, 100% { opacity: 0; transform: scale(1.3); }
}

@keyframes connecting-splat-at-72 {
    0%, 71.9% { opacity: 0; transform: scale(0.2); }
    76% { opacity: 1; transform: scale(1.15); }
    80%, 100% { opacity: 1; transform: none; }
}

@keyframes connecting-splat-at-44 {
    0%, 43.9% { opacity: 0; transform: scale(0.2); }
    48% { opacity: 1; transform: scale(1.15); }
    52%, 100% { opacity: 1; transform: none; }
}

/* The toll: it rises from under the deck and sends the meeple down the bridge. */
@keyframes connecting-troll-swing {
    0%, 10% { transform: translateY(15px); animation-timing-function: ease-out; }
    26% { transform: translateY(-1px); }
    30%, 36% { transform: none; }
    42% { transform: rotate(-8deg); animation-timing-function: ease-in; }
    48%, 56% { transform: rotate(16deg); }
    70%, 100% { transform: none; }
}

@keyframes connecting-troll-launch {
    0% { transform: none; }
    8% { transform: translate(-3px, -1px); }
    16% { transform: translate(-6px, 0); }
    24% { transform: translate(-9px, -1px); }
    32% { transform: translate(-12px, 0); }
    40% { transform: translate(-15px, -1px); }
    47% { transform: translate(-16px, 0); animation-timing-function: ease-out; }
    60% { transform: translate(-8px, -15px) rotate(300deg); animation-timing-function: ease-in; }
    72% { transform: translate(1px, -6px) rotate(630deg); }
    74% { transform: translate(1px, -7px) rotate(630deg); }
    76%, 100% { transform: translate(1px, -6px) rotate(630deg); }
}

@keyframes connecting-troll-drop {
    0% { transform: none; }
    8% { transform: translate(-3px, -1px); }
    16% { transform: translate(-6px, 0); }
    24% { transform: translate(-9px, -1px); }
    32% { transform: translate(-12px, 0); }
    40% { transform: translate(-15px, -1px); }
    47% { transform: translate(-16px, 0); animation-timing-function: ease-in; }
    62% { transform: translate(-16px, 0) rotate(90deg); }
    66% { transform: translate(-16px, -1px) rotate(90deg); }
    70%, 100% { transform: translate(-16px, 0) rotate(90deg); }
}

/* The fleet: it comes up beside the longship and takes it down. */
@keyframes connecting-kraken-rise {
    0% { transform: translateY(15px); animation-timing-function: ease-out; }
    30% { transform: translateY(-1px); }
    36%, 66% { transform: none; }
    100% { transform: translateY(1px); }
}

@keyframes connecting-ship-sink {
    0% { transform: none; }
    14% { transform: translateY(-1px); }
    28% { transform: translateY(1px) rotate(4deg); }
    34% { transform: rotate(-14deg); animation-timing-function: ease-in; }
    64%, 100% { transform: translate(-8px, 44px) rotate(-32deg); }
}

/* The wildlife: it rears out of the sea and strikes across the shore. */
@keyframes connecting-serpent-strike {
    0% { transform: translateY(15px); animation-timing-function: ease-out; }
    20% { transform: none; }
    32% { transform: translateX(-2px) rotate(-7deg); animation-timing-function: ease-in; }
    44% { transform: translateX(12px) rotate(6deg); }
    50% { transform: translateX(12px) rotate(6deg); }
    64% { transform: rotate(-3deg); }
    70% { transform: scale(1.06, 0.95); }
    76%, 100% { transform: none; }
}

@keyframes connecting-serpent-gone {
    0%, 43.9% { opacity: 1; }
    44%, 100% { opacity: 0; }
}

@keyframes connecting-serpent-drop {
    0%, 50% { transform: none; animation-timing-function: ease-in; }
    64% { transform: rotate(90deg); }
    68% { transform: translateY(-1px) rotate(90deg); }
    72%, 100% { transform: rotate(90deg); }
}

@keyframes connecting-splat-at-33 {
    0%, 32.9% { opacity: 0; transform: scale(0.2); }
    37% { opacity: 1; transform: scale(1.15); }
    41%, 100% { opacity: 1; transform: none; }
}

@keyframes connecting-splat-at-58 {
    0%, 57.9% { opacity: 0; transform: scale(0.2); }
    62% { opacity: 1; transform: scale(1.15); }
    66%, 100% { opacity: 1; transform: none; }
}

/* The beacons: it swoops in, rears, and the meeple chars, blinks and crumbles. */
@keyframes connecting-dragon-swoop {
    0% { transform: translate(-30px, -10px); animation-timing-function: ease-out; }
    22% { transform: none; }
    30% { transform: rotate(-8deg); }
    36%, 100% { transform: none; }
}

@keyframes connecting-flame-jet {
    0%, 33.9% { opacity: 0; transform: scale(0.2); }
    38% { opacity: 1; transform: none; }
    44% { opacity: 1; transform: scale(1.05, 0.9); }
    50% { opacity: 1; transform: scale(0.97, 1.08); }
    56% { opacity: 1; transform: scale(1.04, 0.95); }
    62% { opacity: 1; transform: none; }
    64%, 100% { opacity: 0; transform: none; }
}

@keyframes connecting-dragon-char {
    0%, 40% { fill: var(--text-1); transform: none; }
    48%, 72% { fill: var(--border-2); transform: none; }
    84%, 100% { fill: var(--border-2); transform: scale(1.4, 0.1); }
}

@keyframes connecting-dragon-blink {
    0%, 40% { fill: var(--surface-1); opacity: 1; }
    48%, 65.9% { fill: var(--text-1); opacity: 1; }
    66%, 67.9% { fill: var(--text-1); opacity: 0; }
    68%, 100% { fill: var(--text-1); opacity: 1; }
}

/* The walls: they wobble, think about it, and go over on the inspector. */
@keyframes connecting-fort-fall {
    0%, 12% { transform: none; }
    16% { transform: rotate(3deg); }
    22% { transform: rotate(-1.5deg); }
    30% { transform: rotate(5deg); }
    36% { transform: none; }
    42% { transform: rotate(7deg); animation-timing-function: ease-in; }
    58% { transform: rotate(90deg); }
    61% { transform: rotate(86deg); }
    64%, 100% { transform: rotate(90deg); }
}

@keyframes connecting-fort-gone {
    0%, 57.9% { opacity: 1; }
    58%, 100% { opacity: 0; }
}

@keyframes connecting-dust-puff {
    0%, 57.9% { opacity: 0; transform: scale(0.4); }
    60% { opacity: 1; transform: none; }
    74% { opacity: 1; transform: scale(1.2); }
    84%, 100% { opacity: 0; transform: scale(1.4); }
}

/* The reserves: they come out at a run, and do not stop for anyone. */
@keyframes connecting-troop-charge {
    0% { opacity: 1; transform: none; }
    8% { opacity: 1; transform: translate(8.4px, -1.5px); }
    16% { opacity: 1; transform: translate(16.7px, 0); }
    24% { opacity: 1; transform: translate(25px, -1.5px); }
    32% { opacity: 1; transform: translate(33.4px, 0); }
    40% { opacity: 1; transform: translate(41.8px, -1.5px); }
    46% { opacity: 1; transform: translate(46px, 0); }
    46.1%, 100% { opacity: 0; transform: translate(46px, 0); }
}

@keyframes connecting-barracks-trample {
    0%, 32.9% { transform: none; }
    33%, 100% { transform: scale(1.5, 0.14); }
}

/* The riders: it takes its hire by the head, climbs, thinks better of it, and the
 * meeple goes in head first and kicks. Griffin and meeple climb on the same stops,
 * and the griffin's units are 2 to the meeple's 1.6, so the talons keep their grip. */
@keyframes connecting-griffin-fly {
    0%, 3% { opacity: 0; transform: translate(-26px, -12px); }
    4% { opacity: 1; transform: translate(-26px, -12px); animation-timing-function: ease-out; }
    28%, 32% { opacity: 1; transform: none; }
    56%, 58% { opacity: 1; transform: translate(6px, -11px); animation-timing-function: ease-in; }
    76% { opacity: 1; transform: translate(26px, -18px); }
    82%, 100% { opacity: 0; transform: translate(32px, -20px); }
}

@keyframes connecting-griffin-flap {
    0%, 100% { transform: none; }
    50% { transform: scaleY(0.3); }
}

@keyframes connecting-griffin-carried {
    0%, 32% { transform: none; }
    56%, 58% { transform: translate(7.5px, -13.75px); animation-timing-function: ease-in; }
    72% { transform: translate(8px, -9px) rotate(540deg); }
    76% { transform: translate(8px, -9px) rotate(534deg); }
    80% { transform: translate(8px, -9px) rotate(546deg); }
    84% { transform: translate(8px, -9px) rotate(535deg); }
    88%, 100% { transform: translate(8px, -9px) rotate(540deg); }
}

/* The engines: the arm goes over, the meeple goes on, and the wall stops it. The
 * meeple rides the cup on the arm's arc until the arm stops, then flies free. */
@keyframes connecting-siege-swing {
    0%, 6% { transform: none; }
    9% { transform: rotate(1deg); }
    12% { transform: rotate(-1.5deg); }
    16% { transform: none; }
    18.5% { transform: rotate(45deg); }
    20.5% { transform: rotate(90deg); }
    21.5% { transform: rotate(105deg); }
    25% { transform: rotate(88deg); }
    29% { transform: rotate(98deg); }
    33%, 100% { transform: rotate(95deg); }
}

@keyframes connecting-siege-flight {
    0%, 16% { transform: none; }
    18.5% { transform: translate(1.75px, -11.7px); }
    20.5% { transform: translate(11.25px, -18.75px); }
    21.5% { transform: translate(15.25px, -19.2px); }
    26% { transform: translate(19.7px, -25px) rotate(140deg); }
    30% { transform: translate(23.6px, -28px) rotate(270deg); }
    34% { transform: translate(27.5px, -26px) rotate(400deg); }
    38% { transform: translate(31.4px, -20px) rotate(530deg); }
    41% { transform: translate(34.4px, -11.5px) rotate(630deg); }
    44% { transform: translate(37.3px, -1.1px) rotate(720deg) scale(0.15, 1.15); }
    47%, 54% { transform: translate(37.3px, -1.1px) rotate(720deg) scale(0.22, 1); animation-timing-function: ease-in; }
    70% { transform: translate(37.3px, 7.1px) rotate(720deg) scale(0.22, 0.4); }
    76%, 100% { transform: translate(34px, 9px) rotate(720deg) scale(1.4, 0.16); }
}

@keyframes connecting-siege-streak {
    0%, 53.9% { opacity: 0; transform: scaleY(0); }
    54% { opacity: 1; transform: scaleY(0); animation-timing-function: ease-in; }
    70%, 100% { opacity: 1; transform: none; }
}

/* The ceasefire: both sides walk up, one nods, and the flag was never going to
 * help. Three blows drive the envoy into the ground, up to the eyes. */
@keyframes connecting-ceasefire-hammered {
    0% { transform: none; }
    4% { transform: translate(2.3px, -1px); }
    8% { transform: translate(4.7px, 0); }
    12% { transform: translate(7px, -1px); }
    16% { transform: translate(9.3px, 0); }
    20% { transform: translate(11.7px, -1px); }
    24%, 37.9% { transform: translate(14px, 0); }
    38%, 47.9% { transform: translate(14px, 4px); }
    48%, 57.9% { transform: translate(14px, 7.5px); }
    58%, 100% { transform: translate(14px, 10.5px); }
}

@keyframes connecting-late-blink {
    0%, 71.9% { opacity: 1; }
    72%, 73.9% { opacity: 0; }
    74%, 79.9% { opacity: 1; }
    80%, 81.9% { opacity: 0; }
    82%, 100% { opacity: 1; }
}

@keyframes connecting-ceasefire-flag-falls {
    0% { transform: none; }
    4% { transform: translate(2.3px, -1px) rotate(4deg); }
    8% { transform: translate(4.7px, 0) rotate(-4deg); }
    12% { transform: translate(7px, -1px) rotate(4deg); }
    16% { transform: translate(9.3px, 0) rotate(-4deg); }
    20% { transform: translate(11.7px, -1px) rotate(4deg); }
    24%, 38% { transform: translate(14px, 0); animation-timing-function: ease-in; }
    50% { transform: translate(14px, 0) rotate(-90deg); }
    53% { transform: translate(14px, -1px) rotate(-90deg); }
    56%, 100% { transform: translate(14px, 0) rotate(-90deg); }
}

@keyframes connecting-ceasefire-bonk {
    0% { transform: none; }
    8% { transform: translate(-0.5px, -1px); }
    16% { transform: translate(-1px, 0); }
    24% { transform: translate(-1.5px, 0); }
    28% { transform: translate(-1.5px, 0) rotate(-4deg); }
    31% { transform: translate(-1.5px, 0); }
    34% { transform: translate(-1.5px, 0) rotate(10deg); animation-timing-function: ease-in; }
    38% { transform: translate(-1.5px, 0) rotate(-20deg); }
    44% { transform: translate(-1.5px, 0) rotate(8deg); animation-timing-function: ease-in; }
    48% { transform: translate(-1.5px, 0) rotate(-28deg); }
    54% { transform: translate(-1.5px, 0) rotate(6deg); animation-timing-function: ease-in; }
    58% { transform: translate(-1.5px, 0) rotate(-36deg); }
    68%, 100% { transform: translate(-1.5px, 0); }
}

/* The palisade: the stakes come up in a line towards the meeple, each one making it
 * jump, and stop one short. It relaxes. Then the last comes up underneath. */
@keyframes connecting-stake-1 {
    0%, 6% { transform: translateY(34px); }
    9% { transform: translateY(-1px); }
    11%, 100% { transform: none; }
}

@keyframes connecting-stake-2 {
    0%, 14% { transform: translateY(34px); }
    17% { transform: translateY(-1px); }
    19%, 100% { transform: none; }
}

@keyframes connecting-stake-3 {
    0%, 22% { transform: translateY(34px); }
    25% { transform: translateY(-1px); }
    27%, 100% { transform: none; }
}

@keyframes connecting-stake-4 {
    0%, 30% { transform: translateY(34px); }
    33% { transform: translateY(-1px); }
    35%, 100% { transform: none; }
}

@keyframes connecting-stake-5 {
    0%, 46% { transform: translateY(34px); }
    50%, 100% { transform: none; }
}

@keyframes connecting-palisade-lifted {
    0%, 7% { transform: none; }
    9% { transform: translateY(-1.5px); }
    11%, 15% { transform: none; }
    17% { transform: translateY(-2px); }
    19%, 23% { transform: none; }
    25% { transform: translateY(-3px); }
    27%, 31% { transform: none; }
    33% { transform: translateY(-5px); }
    36%, 46% { transform: none; }
    50%, 100% { transform: translate(-7px, -16.9px) rotate(90deg); }
}

@keyframes connecting-palisade-drop {
    0%, 7% { transform: none; }
    9% { transform: translateY(-1.5px); }
    11%, 15% { transform: none; }
    17% { transform: translateY(-2px); }
    19%, 23% { transform: none; }
    25% { transform: translateY(-3px); }
    27%, 31% { transform: none; }
    33% { transform: translateY(-5px); }
    36%, 47% { transform: none; animation-timing-function: ease-in; }
    60%, 100% { transform: translateX(8.5px) rotate(90deg); }
}

@keyframes connecting-palisade-run {
    0%, 55.9% { opacity: 0; transform: scaleY(0); }
    56% { opacity: 1; transform: scaleY(0); }
    78%, 100% { opacity: 1; transform: none; }
}
