/* The two of you: rig, wardrobe, poses and props.
 *
 * A pose is a set of joint angles, nothing more. `public/cast.js` sets one
 * `data-pose` on a character and everything below decides what the shoulders,
 * elbows, hips and knees do about it, which is why sitting on a throne, being
 * dragged down a corridor and lying flat on the floor all come out of the same
 * two drawings.
 *
 * Everything animates transform and opacity only. No filters on anything that
 * moves: a drop shadow on a walking character repaints it every frame, and this
 * room is open on a phone all evening.
 */

.cast {
  position: fixed;
  inset: 0;
  z-index: 38; /* over the room, under the sheets and the toasts */
  overflow: hidden;
  /* Scenery does not take taps. The characters opt back in below, and SVG only
     hit-tests painted pixels, so a tap in the gap between his legs still
     reaches whatever button is under him. */
  pointer-events: none;
  transition: opacity 0.3s ease;

  --skin-1: #edc19b;
  --skin-2: #f8d3b4;
  --hair: #4a3227;
  --hair-hi: #6d4c34;
  --dress: #ff7aa8;
  --dress-dk: #dd4f86;
  --shirt: #7f95cd;
  --jeans: #3f4d6b;
  --shoe: #3a2b33;
  --ink: #3a2b33;
  --line: rgba(58, 43, 51, 0.24);
  --s: 0.9;
}

/* The GIF picker opens exactly where they like to stand. */
body.picking .cast { opacity: 0.16; }

.toon {
  position: absolute;
  left: 0;
  top: 0;
  width: 120px;
  height: 220px;
  /* The soles. Scaling and leaning both happen around this point, so nobody
     ever sinks into the floor or floats above it. */
  transform-origin: 60px 214px;
  transform:
    translate(calc(var(--x, 0px) - 60px), calc(var(--y, 0px) - 214px))
    scale(var(--s))
    scaleX(var(--flip, 1))
    rotate(var(--lean, 0deg));
  will-change: transform;
  transition: opacity 0.3s ease;
}

.t-svg {
  width: 100%;
  height: 100%;
  overflow: visible;
  pointer-events: auto;
}

.toon.t-hidden { opacity: 0; }

/* ------------------------------------------------------------ wardrobe */

.t-shadow { fill: rgba(58, 43, 51, 0.15); }

.t-seg, .t-hand, .t-face, .t-neck, .t-dress, .t-shirt, .t-jeans, .t-shoe, .t-hair, .t-hair-back {
  stroke: var(--line);
  stroke-width: 1.3;
  stroke-linejoin: round;
}

.toon-him .t-seg, .toon-him .t-face, .toon-him .t-neck { fill: var(--skin-1); }
.toon-her .t-seg, .toon-her .t-face, .toon-her .t-neck { fill: var(--skin-2); }

.toon-him [class*="t-leg"] .t-seg { fill: var(--jeans); }
.toon-him .t-arm-b > .t-seg, .toon-him .t-arm-f > .t-seg { fill: var(--shirt); }

.t-shoe { fill: var(--shoe); }
.t-ear { fill: inherit; stroke: var(--line); stroke-width: 1.2; }
.toon-him .t-ear { fill: var(--skin-1); }
.toon-her .t-ear { fill: var(--skin-2); }
.t-dress { fill: var(--dress); }
.t-dress-hem { fill: var(--dress-dk); }
.t-bust { stroke: rgba(58, 43, 51, 0.16); stroke-width: 2; fill: none; stroke-linecap: round; }
.t-jawline { stroke: rgba(58, 43, 51, 0.16); stroke-width: 1.6; fill: none; stroke-linecap: round; }
.t-hair-fringe { fill: var(--hair-hi); }
.t-hair-swoop { fill: var(--hair); }
.t-hair-top { fill: var(--hair-hi); opacity: 0.9; }
.t-stubble { fill: rgba(58, 43, 51, 0.1); }
.t-strap { stroke: var(--dress-dk); stroke-width: 3.5; fill: none; stroke-linecap: round; }
.t-shirt { fill: var(--shirt); }
.t-shirt-hi { fill: #fff; opacity: 0.09; }
.t-collar { stroke: rgba(58, 43, 51, 0.3); stroke-width: 2; fill: none; stroke-linecap: round; }
.t-jeans { fill: var(--jeans); }

/* One soft shade down the far side of the body. Cheap depth, and it is what
   keeps them from reading as flat stickers at the size they are drawn. */
.t-shade { fill: rgba(58, 43, 51, 0.13); }

/* ------------------------------------------------------------ nude */

/* Clothes on by default. Anatomy sits under the wardrobe and only shows
   when someone presses strip. */
.t-nude-bits { display: none; }
.toon.t-nude .t-nude-bits { display: block; }

.toon-her.t-nude .t-dress,
.toon-her.t-nude .t-dress-hem,
.toon-her.t-nude .t-bust,
.toon-her.t-nude .t-strap,
.toon-her.t-nude .t-dress-shade { display: none; }

.toon-him.t-nude .t-shirt,
.toon-him.t-nude .t-jeans,
.toon-him.t-nude .t-collar,
.toon-him.t-nude .t-shirt-shade { display: none; }

/* Arms and legs go skin once the sleeves and jeans are gone. */
.toon-him.t-nude [class*="t-leg"] .t-seg { fill: var(--skin-1); }
.toon-him.t-nude .t-arm-b > .t-seg,
.toon-him.t-nude .t-arm-f > .t-seg { fill: var(--skin-1); }
.toon.t-nude .t-shoe { opacity: 0.55; }

.toon-her .t-torso { fill: var(--skin-2); stroke: var(--line); stroke-width: 1.3; stroke-linejoin: round; }
.toon-him .t-torso { fill: var(--skin-1); stroke: var(--line); stroke-width: 1.3; stroke-linejoin: round; }

.t-boob { fill: var(--skin-2); stroke: var(--line); stroke-width: 1.2; }
.t-boob-shade { fill: rgba(217, 64, 122, 0.12); }
.t-nipple { fill: #e08a9a; stroke: rgba(58, 43, 51, 0.18); stroke-width: 0.8; }
.t-mound { fill: var(--skin-2); stroke: var(--line); stroke-width: 1.1; }
.t-pussy { stroke: #c46b7c; stroke-width: 2.2; fill: none; stroke-linecap: round; }
.t-nude-shade { fill: rgba(58, 43, 51, 0.12); }

.t-balls { fill: var(--skin-1); stroke: var(--line); stroke-width: 1.2; }
.t-dick { fill: var(--skin-1); stroke: var(--line); stroke-width: 1.2; stroke-linejoin: round; }
.t-dick-tip { fill: #e8a896; stroke: var(--line); stroke-width: 1; }

/* Hard / excited: a little more present. */
.toon.t-hard .t-dick { transform: translateY(-4px) scaleY(1.18); transform-box: fill-box; transform-origin: center top; }
.toon.t-hard .t-dick-tip { transform: translateY(-6px); transform-box: fill-box; transform-origin: center; }

/* Half naked: bottoms off, tops stay (his shirt; her dress is one piece so
   she goes bare — still the joke of a poop date, not a full strip show). */
.toon.t-half .t-nude-bits { display: block; }
.toon-him.t-half .t-jeans { display: none; }
.toon-him.t-half [class*="t-leg"] .t-seg { fill: var(--skin-1); }
.toon-her.t-half .t-dress,
.toon-her.t-half .t-dress-hem,
.toon-her.t-half .t-bust,
.toon-her.t-half .t-strap,
.toon-her.t-half .t-dress-shade { display: none; }
/* His shirt stays on for half. Her has no separate top, so torso is bare. */
.toon.t-half .t-shoe { opacity: 0.55; }

/* ------------------------------------------------------------ rear */

/* Turned around: hide the face, show the back of the head, and when nude
   (or half) show the ass instead of front bits. */
.t-rear-bits { display: none; }
.t-hair-back-head { display: none; fill: var(--hair); }

.toon.t-back .t-eyes,
.toon.t-back .t-ko-eyes,
.toon.t-back .t-mouth,
.toon.t-back .t-blush,
.toon.t-back .t-brows,
.toon.t-back .t-shine,
.toon.t-back .t-stubble,
.toon.t-back .t-jawline,
.toon.t-back .t-bruise,
.toon.t-back .t-hair-fringe,
.toon.t-back .t-hair-hi { display: none; }

.toon.t-back .t-hair,
.toon.t-back .t-hair-swoop,
.toon.t-back .t-hair-top { opacity: 0; }
.toon.t-back .t-hair-back-head { display: block; }

/* Front anatomy off; rear on when bare. */
.toon.t-back .t-front-bits { display: none; }
.toon.t-back.t-nude .t-rear-bits,
.toon.t-back.t-half .t-rear-bits { display: block; }

/* Clothed back still reads as turned: hide bust line, keep clothes. */
.toon.t-back .t-bust { display: none; }
.toon.t-back .t-collar { display: none; }

.toon-her .t-ass { fill: var(--skin-2); stroke: var(--line); stroke-width: 1.2; }
.toon-him .t-ass { fill: var(--skin-1); stroke: var(--line); stroke-width: 1.2; }
.t-ass-shade { fill: rgba(58, 43, 51, 0.1); }
.t-ass-cleft { stroke: rgba(58, 43, 51, 0.28); stroke-width: 1.8; fill: none; stroke-linecap: round; }
.t-hole { fill: rgba(58, 43, 51, 0.22); stroke: rgba(58, 43, 51, 0.2); stroke-width: 0.8; }
.toon-her .t-back-torso { fill: var(--skin-2); stroke: var(--line); stroke-width: 1.3; stroke-linejoin: round; }
.toon-him .t-back-torso { fill: var(--skin-1); stroke: var(--line); stroke-width: 1.3; stroke-linejoin: round; }

/* A little wiggle when someone is showing off. */
.toon.t-back[data-pose="present"] .t-ass-l { animation: t-ass-wiggle 0.7s ease-in-out infinite alternate; }
.toon.t-back[data-pose="present"] .t-ass-r { animation: t-ass-wiggle 0.7s ease-in-out infinite alternate-reverse; }

@keyframes t-ass-wiggle {
  from { transform: translateX(-1px); }
  to { transform: translateX(1px); }
}

.t-hair, .t-hair-back, .t-hair-side { fill: var(--hair); }
.t-hair-hi { fill: var(--hair-hi); }

.t-eye { fill: var(--ink); }
.t-shine { fill: #fff; }
.t-brow { stroke: var(--hair); stroke-width: 2.6; fill: none; stroke-linecap: round; }
.t-mouth { stroke: var(--ink); stroke-width: 2.2; fill: none; stroke-linecap: round; }
.t-blush { fill: #ff7fa6; opacity: 0.3; transition: opacity 0.3s ease; }
.t-ko-eyes { display: none; }
.t-ko-eyes path { stroke: var(--ink); stroke-width: 2.6; fill: none; stroke-linecap: round; }
.t-crown { fill: #ffcb47; stroke: #d99b16; stroke-width: 1.2; display: none; }
.t-bruise, .t-sweat, .t-cup, .t-bouquet { display: none; }
.t-bruise ellipse { fill: #b56ad0; opacity: 0.5; }
.t-plaster { fill: #ffdfa8; stroke: var(--line); stroke-width: 1; }
.t-sweat { fill: #8fd3ff; stroke: var(--line); stroke-width: 1; }

/* Coffee mug: ceramic body, dark brew, foam, little heart. Always readable
   (hardcoded colours so dark mode does not swallow the cup into the room). */
.t-mug-body { fill: #fff8f2; stroke: #c4a090; stroke-width: 1.2; stroke-linejoin: round; }
.t-mug-rim { fill: #fff8f2; stroke: #c4a090; stroke-width: 1.1; }
.t-mug-coffee { fill: #5c3317; }
.t-mug-foam { fill: #f5e6d3; opacity: 0.95; }
.t-mug-heart { fill: #ff7aa8; stroke: #e85a8a; stroke-width: 0.6; }
.t-cup-handle { stroke: #c4a090; stroke-width: 2.2; fill: none; stroke-linecap: round; }
.t-steam { stroke: #c8b8b0; stroke-width: 1.4; fill: none; stroke-linecap: round; opacity: 0.85; }
.toon.t-holding .t-steam-1 { animation: t-steam 1.4s ease-in-out infinite; }
.toon.t-holding .t-steam-2 { animation: t-steam 1.4s ease-in-out 0.25s infinite; }
.toon.t-holding .t-steam-3 { animation: t-steam 1.4s ease-in-out 0.5s infinite; }

@keyframes t-steam {
  0%, 100% { opacity: 0.35; transform: translateY(0); }
  50% { opacity: 0.95; transform: translateY(-3px); }
}

/* Bouquet of flowers — big, readable, stays until another scene clears it. */
.toon.t-flowers .t-bouquet { display: block; }
.t-bq-stems path { stroke: #3d7a45; stroke-width: 2.2; fill: none; stroke-linecap: round; }
.t-bq-leafs ellipse { fill: #4caf50; stroke: #2e7d32; stroke-width: 0.8; }
.t-bq-a circle:not(.t-bq-center) { fill: #ff7aa8; stroke: #e85a8a; stroke-width: 0.7; }
.t-bq-b circle:not(.t-bq-center) { fill: #ff9ec0; stroke: #f070a0; stroke-width: 0.7; }
.t-bq-c circle:not(.t-bq-center) { fill: #fff0f5; stroke: #f0b0c8; stroke-width: 0.7; }
.t-bq-d circle:not(.t-bq-center) { fill: #ff5a9a; stroke: #d9407a; stroke-width: 0.7; }
.t-bq-e circle:not(.t-bq-center) { fill: #ffc0d8; stroke: #f090b0; stroke-width: 0.7; }
.t-bq-center { fill: #ffd978; stroke: #e8b840; stroke-width: 0.6; }
.t-bq-wrap { fill: #fff8e8; stroke: #e8d0a8; stroke-width: 1.1; }
.t-bq-ribbon { stroke: #ff7aa8; stroke-width: 1.8; fill: none; stroke-linecap: round; }
.toon.t-flowers .t-bouquet { animation: t-bq-sway 2.8s ease-in-out infinite; transform-box: fill-box; transform-origin: center bottom; }
@keyframes t-bq-sway {
  0%, 100% { transform: rotate(-3deg); }
  50% { transform: rotate(4deg); }
}

/* ---------------------------------------------------------------- rig */

/* Every joint rotates about its own origin, which the static translate on its
   parent group has already placed at the shoulder, elbow, hip or knee. */
.j {
  transform-box: view-box;
  transform-origin: 0 0;
  transition: transform 0.2s cubic-bezier(0.3, 1.2, 0.4, 1);
}

.t-arm-b { transform: rotate(8deg); }
.t-arm-b-2 { transform: rotate(6deg); }
.t-arm-f { transform: rotate(-8deg); }
.t-arm-f-2 { transform: rotate(6deg); }

.t-eyes {
  transform-box: fill-box;
  transform-origin: center;
  animation: t-blink 6.2s ease-in-out infinite;
}

@keyframes t-blink {
  0%, 46%, 52%, 100% { transform: scaleY(1); }
  49% { transform: scaleY(0.08); }
}

.t-say {
  position: absolute;
  left: 50%;
  bottom: 78%;
  transform-origin: center bottom;
  /* Counter-flips the parent, or the words come out mirrored when she is
     walking left. */
  transform: translateX(-50%) scaleX(var(--flip, 1)) scale(0.7);
  opacity: 0;
  padding: 5px 10px;
  border-radius: 999px;
  background: var(--card);
  border: 2px solid var(--dress);
  /* Not `--ink`: that one is defined on `.cast` above as the colour you draw a
     cartoon outline with, and this element is inside it. See app.css. */
  color: var(--say-ink, #4a3540);
  font: 800 11px/1.3 var(--font, sans-serif);
  white-space: nowrap;
  box-shadow: 0 6px 16px rgba(217, 64, 122, 0.28);
  pointer-events: none;
  transition: transform 0.22s cubic-bezier(0.2, 1.6, 0.4, 1), opacity 0.22s;
}

.t-say.on { opacity: 1; transform: translateX(-50%) scaleX(var(--flip, 1)) scale(1); }

/* -------------------------------------------------------------- states */

.toon.t-blushing .t-blush { opacity: 0.85; }
.toon.t-crowned .t-crown { display: block; }
.toon.t-hurt .t-bruise { display: block; }
.toon.t-holding .t-cup { display: block; }
.toon.t-messy .t-hair, .toon.t-messy .t-hair-back { transform: rotate(-4deg) translateY(-2px); transform-box: fill-box; transform-origin: center bottom; }
.toon.t-messy .t-sweat { display: block; }

/* Angry: brows down and in, mouth flat. */
.toon.t-angry .t-brow-l { transform: rotate(22deg) translateY(2px); }
.toon.t-angry .t-brow-r { transform: rotate(-22deg) translateY(2px); }
.toon.t-angry .t-brow-l, .toon.t-angry .t-brow-r { transform-box: fill-box; transform-origin: center; }
.toon.t-angry .t-mouth { d: path("M-6 11 q6 -3 12 0"); }

/* ------------------------------------------------------------- walking */

@keyframes t-legA {
  0%, 100% { transform: rotate(26deg); }
  50% { transform: rotate(-24deg); }
}

@keyframes t-legB {
  0%, 100% { transform: rotate(-24deg); }
  50% { transform: rotate(26deg); }
}

@keyframes t-kneeA {
  0%, 100% { transform: rotate(3deg); }
  30% { transform: rotate(34deg); }
  65% { transform: rotate(6deg); }
}

@keyframes t-kneeB {
  0%, 100% { transform: rotate(30deg); }
  35% { transform: rotate(4deg); }
  70% { transform: rotate(28deg); }
}

@keyframes t-bobY {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-2.5px); }
}

[data-pose="walk"] .t-leg-f, [data-pose="run"] .t-leg-f,
[data-pose="haul"] .t-leg-f, [data-pose="carry"] .t-leg-f { animation: t-legA var(--gait, 0.62s) linear infinite; }
[data-pose="walk"] .t-leg-b, [data-pose="run"] .t-leg-b,
[data-pose="haul"] .t-leg-b, [data-pose="carry"] .t-leg-b { animation: t-legB var(--gait, 0.62s) linear infinite; }
[data-pose="walk"] .t-leg-f-2, [data-pose="run"] .t-leg-f-2,
[data-pose="haul"] .t-leg-f-2, [data-pose="carry"] .t-leg-f-2 { animation: t-kneeA var(--gait, 0.62s) linear infinite; }
[data-pose="walk"] .t-leg-b-2, [data-pose="run"] .t-leg-b-2,
[data-pose="haul"] .t-leg-b-2, [data-pose="carry"] .t-leg-b-2 { animation: t-kneeB var(--gait, 0.62s) linear infinite; }
[data-pose="walk"] .t-arm-f, [data-pose="run"] .t-arm-f { animation: t-legB var(--gait, 0.62s) linear infinite; }
[data-pose="walk"] .t-arm-b, [data-pose="run"] .t-arm-b { animation: t-legA var(--gait, 0.62s) linear infinite; }
[data-pose="walk"] .t-svg, [data-pose="run"] .t-svg,
[data-pose="haul"] .t-svg, [data-pose="carry"] .t-svg { animation: t-bobY calc(var(--gait, 0.62s) / 2) ease-in-out infinite; }

[data-pose="run"] { --gait: 0.38s; --lean: 9deg; }
[data-pose="haul"] { --gait: 0.8s; --lean: 15deg; }
[data-pose="carry"] { --gait: 0.75s; --lean: 7deg; }
[data-pose="haul"] .t-arm-f { transform: rotate(78deg); }
[data-pose="haul"] .t-arm-b { transform: rotate(64deg); }
[data-pose="carry"] .t-arm-f { transform: rotate(52deg); }
[data-pose="carry"] .t-arm-b { transform: rotate(58deg); }

/* --------------------------------------------------------------- poses */

[data-pose="idle"] .t-head-j { animation: t-headbob 4s ease-in-out infinite; }

@keyframes t-headbob {
  0%, 100% { transform: rotate(-1.6deg); }
  50% { transform: rotate(1.6deg); }
}

/* Sitting drops the hips to seat height, since the feet stay where they are. */
[data-pose="sit"] .t-leg-f, [data-pose="sit"] .t-leg-b { transform: rotate(-84deg); }
[data-pose="sit"] .t-leg-f-2, [data-pose="sit"] .t-leg-b-2 { transform: rotate(86deg); }
[data-pose="sit"] .t-arm-f { transform: rotate(-22deg); }
[data-pose="sit"] .t-arm-b { transform: rotate(20deg); }
.toon-him[data-pose="sit"] .t-svg { transform: translateY(42px); }
.toon-her[data-pose="sit"] .t-svg { transform: translateY(34px); }

[data-pose="ko"] { --lean: -76deg; }
[data-pose="ko"] .t-arm-f { transform: rotate(-52deg); }
[data-pose="ko"] .t-arm-b { transform: rotate(28deg); }
[data-pose="ko"] .t-leg-f { transform: rotate(22deg); }
[data-pose="ko"] .t-leg-b { transform: rotate(-16deg); }
[data-pose="ko"] .t-leg-f-2 { transform: rotate(26deg); }
[data-pose="ko"] .t-eyes { display: none; }
[data-pose="ko"] .t-ko-eyes { display: block; }
[data-pose="ko"] .t-mouth { d: path("M-5 12 q5 -5 10 0"); }

[data-pose="recoil"] { --lean: -17deg; }
[data-pose="recoil"] .t-arm-f { transform: rotate(-142deg); }
[data-pose="recoil"] .t-arm-b { transform: rotate(150deg); }
[data-pose="recoil"] .t-head-j { transform: rotate(-14deg); }
[data-pose="recoil"] .t-eyes { transform: scaleY(0.2); }

[data-pose="grab"] .t-arm-f { transform: rotate(-96deg); }
[data-pose="grab"] .t-arm-f-2 { transform: rotate(18deg); }
[data-pose="grab"] { --lean: 6deg; }

[data-pose="grabbed"] .t-arm-f { transform: rotate(-160deg); }
[data-pose="grabbed"] .t-arm-b { transform: rotate(158deg); }
[data-pose="grabbed"] { --lean: -8deg; }

/* Heels in, body tipped back, going nowhere by choice. */
[data-pose="dragged"] { --lean: 52deg; }
[data-pose="dragged"] .t-arm-f { transform: rotate(-172deg); }
[data-pose="dragged"] .t-arm-b { transform: rotate(166deg); }
[data-pose="dragged"] .t-leg-f { transform: rotate(-26deg); }
[data-pose="dragged"] .t-leg-b { transform: rotate(-16deg); }
[data-pose="dragged"] .t-eyes { transform: scaleY(0.25); }

[data-pose="shove"] .t-arm-f { transform: rotate(-94deg); }
[data-pose="shove"] .t-arm-b { transform: rotate(-78deg); }
[data-pose="shove"] { --lean: 10deg; }

[data-pose="reach"] .t-arm-f { transform: rotate(-88deg); }
[data-pose="reach"] .t-arm-f-2 { transform: rotate(12deg); }

[data-pose="swing"] .t-arm-f { transform: rotate(84deg); }
[data-pose="swing"] .t-arm-f-2 { transform: rotate(-58deg); }
[data-pose="swing"] { --lean: -8deg; }

[data-pose="hit"] .t-arm-f { transform: rotate(-108deg); }
[data-pose="hit"] .t-arm-f-2 { transform: rotate(14deg); }
[data-pose="hit"] { --lean: 12deg; }

[data-pose="lean"] { --lean: 11deg; }
[data-pose="lean"] .t-head-j { transform: rotate(6deg); }
[data-pose="lean"] .t-arm-f { transform: rotate(-34deg); }
[data-pose="lean"] .t-arm-b { transform: rotate(26deg); }

[data-pose="hug"] .t-arm-f { transform: rotate(-96deg); }
[data-pose="hug"] .t-arm-b { transform: rotate(-84deg); }
[data-pose="hug"] .t-arm-f-2 { transform: rotate(34deg); }
[data-pose="hug"] .t-arm-b-2 { transform: rotate(30deg); }
[data-pose="hug"] { --lean: 8deg; }

[data-pose="crouch"] .t-leg-f, [data-pose="crouch"] .t-leg-b { transform: rotate(-34deg); }
[data-pose="crouch"] .t-leg-f-2, [data-pose="crouch"] .t-leg-b-2 { transform: rotate(62deg); }
[data-pose="crouch"] .t-svg { transform: translateY(22px); }
[data-pose="crouch"] { --lean: 12deg; }

[data-pose="ride"] .t-leg-f { transform: rotate(-72deg); }
[data-pose="ride"] .t-leg-b { transform: rotate(-64deg); }
[data-pose="ride"] .t-leg-f-2 { transform: rotate(48deg); }
[data-pose="ride"] .t-leg-b-2 { transform: rotate(44deg); }
[data-pose="ride"] .t-arm-f { transform: rotate(-104deg); }
[data-pose="ride"] .t-arm-b { transform: rotate(-96deg); }

[data-pose="climb"] .t-arm-f { animation: t-climbA 0.7s ease-in-out infinite; }
[data-pose="climb"] .t-arm-b { animation: t-climbB 0.7s ease-in-out infinite; }
[data-pose="climb"] .t-leg-f { animation: t-climbLegA 0.7s ease-in-out infinite; }
[data-pose="climb"] .t-leg-b { animation: t-climbLegB 0.7s ease-in-out infinite; }
[data-pose="climb"] .t-leg-f-2, [data-pose="climb"] .t-leg-b-2 { transform: rotate(46deg); }

@keyframes t-climbA {
  0%, 100% { transform: rotate(-168deg); }
  50% { transform: rotate(-122deg); }
}

@keyframes t-climbB {
  0%, 100% { transform: rotate(126deg); }
  50% { transform: rotate(170deg); }
}

@keyframes t-climbLegA {
  0%, 100% { transform: rotate(-34deg); }
  50% { transform: rotate(-8deg); }
}

@keyframes t-climbLegB {
  0%, 100% { transform: rotate(-6deg); }
  50% { transform: rotate(-32deg); }
}

[data-pose="fall"] .t-arm-f { transform: rotate(-166deg); }
[data-pose="fall"] .t-arm-b { transform: rotate(164deg); }
[data-pose="fall"] .t-leg-f { transform: rotate(-14deg); }
[data-pose="fall"] .t-leg-b { transform: rotate(14deg); }
[data-pose="fall"] .t-svg { animation: t-tumble 0.5s linear infinite; }

@keyframes t-tumble {
  0%, 100% { transform: rotate(-4deg); }
  50% { transform: rotate(4deg); }
}

[data-pose="wave"] .t-arm-f { transform: rotate(-146deg); }
[data-pose="wave"] .t-arm-f-2 { animation: t-wave 0.42s ease-in-out infinite alternate; }

@keyframes t-wave {
  from { transform: rotate(-28deg); }
  to { transform: rotate(24deg); }
}

[data-pose="stretch"] .t-arm-f { transform: rotate(-172deg); }
[data-pose="stretch"] .t-arm-b { transform: rotate(170deg); }
[data-pose="stretch"] .t-svg { animation: t-stretch 1.6s ease-in-out; }

@keyframes t-stretch {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-5px); }
}

[data-pose="look"] .t-head-j { transform: rotate(16deg); }

[data-pose="poked"] .t-svg { animation: t-squish 0.42s cubic-bezier(0.3, 1.5, 0.4, 1); }

@keyframes t-squish {
  0% { transform: scale(1, 1); }
  35% { transform: scale(1.12, 0.88); }
  70% { transform: scale(0.96, 1.05); }
  100% { transform: scale(1, 1); }
}

[data-pose="dance"] .t-svg { animation: t-dance 0.52s ease-in-out infinite; }
[data-pose="dance"] .t-arm-f { animation: t-danceArmA 1.04s ease-in-out infinite; }
[data-pose="dance"] .t-arm-b { animation: t-danceArmB 1.04s ease-in-out infinite; }

@keyframes t-dance {
  0%, 100% { transform: translateY(0) rotate(-5deg); }
  50% { transform: translateY(-8px) rotate(5deg); }
}

@keyframes t-danceArmA {
  0%, 100% { transform: rotate(-150deg); }
  50% { transform: rotate(-40deg); }
}

@keyframes t-danceArmB {
  0%, 100% { transform: rotate(40deg); }
  50% { transform: rotate(152deg); }
}

/* Kneeling: hips drop, knees fold. Used for oral and strip beats. */
[data-pose="kneel"] .t-leg-f, [data-pose="kneel"] .t-leg-b { transform: rotate(-78deg); }
[data-pose="kneel"] .t-leg-f-2, [data-pose="kneel"] .t-leg-b-2 { transform: rotate(92deg); }
[data-pose="kneel"] .t-arm-f { transform: rotate(-28deg); }
[data-pose="kneel"] .t-arm-b { transform: rotate(22deg); }
[data-pose="kneel"] .t-svg { transform: translateY(48px); }
[data-pose="kneel"] { --lean: 4deg; }

/* Eating her out: head in, arms around her thighs. */
[data-pose="eat"] .t-leg-f, [data-pose="eat"] .t-leg-b { transform: rotate(-78deg); }
[data-pose="eat"] .t-leg-f-2, [data-pose="eat"] .t-leg-b-2 { transform: rotate(92deg); }
[data-pose="eat"] .t-arm-f { transform: rotate(-110deg); }
[data-pose="eat"] .t-arm-b { transform: rotate(-98deg); }
[data-pose="eat"] .t-arm-f-2 { transform: rotate(28deg); }
[data-pose="eat"] .t-arm-b-2 { transform: rotate(24deg); }
[data-pose="eat"] .t-head-j { transform: rotate(18deg) translateY(6px); }
[data-pose="eat"] .t-svg { transform: translateY(44px); }
[data-pose="eat"] { --lean: 10deg; }

/* Receiving oral: legs open, hands in his hair. */
[data-pose="receive"] .t-leg-f { transform: rotate(-42deg); }
[data-pose="receive"] .t-leg-b { transform: rotate(18deg); }
[data-pose="receive"] .t-leg-f-2 { transform: rotate(18deg); }
[data-pose="receive"] .t-leg-b-2 { transform: rotate(12deg); }
[data-pose="receive"] .t-arm-f { transform: rotate(-72deg); }
[data-pose="receive"] .t-arm-b { transform: rotate(64deg); }
[data-pose="receive"] .t-head-j { transform: rotate(-8deg); }
[data-pose="receive"] { --lean: -4deg; }

/* Blowjob giver: on her knees, head at his hips. */
[data-pose="blow"] .t-leg-f, [data-pose="blow"] .t-leg-b { transform: rotate(-78deg); }
[data-pose="blow"] .t-leg-f-2, [data-pose="blow"] .t-leg-b-2 { transform: rotate(92deg); }
[data-pose="blow"] .t-arm-f { transform: rotate(-96deg); }
[data-pose="blow"] .t-arm-b { transform: rotate(-82deg); }
[data-pose="blow"] .t-arm-f-2 { transform: rotate(28deg); }
[data-pose="blow"] .t-arm-b-2 { transform: rotate(22deg); }
[data-pose="blow"] .t-head-j { transform: rotate(18deg) translateY(8px); }
[data-pose="blow"] .t-svg { transform: translateY(52px); }
[data-pose="blow"] { --lean: 12deg; }

/* Standing for a blowjob: hips out a little, looking down. */
[data-pose="stood"] .t-arm-f { transform: rotate(-36deg); }
[data-pose="stood"] .t-arm-b { transform: rotate(40deg); }
[data-pose="stood"] .t-head-j { transform: rotate(-14deg); }
[data-pose="stood"] { --lean: 4deg; }
.toon-him[data-pose="stood"] .t-svg { transform: translateY(2px); }

/* Sex / grind: her receiving, him thrusting — different leans. */
.toon-her[data-pose="sex"] .t-leg-f { transform: rotate(-22deg); }
.toon-her[data-pose="sex"] .t-leg-b { transform: rotate(20deg); }
.toon-her[data-pose="sex"] .t-leg-f-2 { transform: rotate(30deg); }
.toon-her[data-pose="sex"] .t-arm-f { transform: rotate(-110deg); }
.toon-her[data-pose="sex"] .t-arm-b { transform: rotate(-96deg); }
.toon-her[data-pose="sex"] { --lean: -6deg; }
.toon-her[data-pose="sex"] .t-svg { animation: t-sex-her 0.38s ease-in-out infinite; }

.toon-him[data-pose="sex"] .t-leg-f { transform: rotate(-18deg); }
.toon-him[data-pose="sex"] .t-leg-b { transform: rotate(12deg); }
.toon-him[data-pose="sex"] .t-leg-f-2 { transform: rotate(28deg); }
.toon-him[data-pose="sex"] .t-arm-f { transform: rotate(-100deg); }
.toon-him[data-pose="sex"] .t-arm-b { transform: rotate(-88deg); }
.toon-him[data-pose="sex"] { --lean: 14deg; }
.toon-him[data-pose="sex"] .t-svg { animation: t-sex-him 0.38s ease-in-out infinite; }

@keyframes t-sex-her {
  0%, 100% { transform: translateY(0) rotate(0deg); }
  50% { transform: translateY(-4px) rotate(2deg); }
}
@keyframes t-sex-him {
  0%, 100% { transform: translateY(0) rotate(-2deg); }
  50% { transform: translateY(-9px) rotate(4deg); }
}

/* On her back-ish: hips tipped, legs up for missionary beat. */
[data-pose="spread"] .t-leg-f { transform: rotate(-58deg); }
[data-pose="spread"] .t-leg-b { transform: rotate(42deg); }
[data-pose="spread"] .t-leg-f-2 { transform: rotate(44deg); }
[data-pose="spread"] .t-leg-b-2 { transform: rotate(40deg); }
[data-pose="spread"] .t-arm-f { transform: rotate(-140deg); }
[data-pose="spread"] .t-arm-b { transform: rotate(130deg); }
[data-pose="spread"] .t-svg { transform: translateY(28px); }
[data-pose="spread"] { --lean: -22deg; }

/* Him over her in missionary: lower and closer. */
.toon-him[data-pose="mission"] .t-leg-f { transform: rotate(-30deg); }
.toon-him[data-pose="mission"] .t-leg-b { transform: rotate(18deg); }
.toon-him[data-pose="mission"] .t-leg-f-2 { transform: rotate(40deg); }
.toon-him[data-pose="mission"] .t-arm-f { transform: rotate(-70deg); }
.toon-him[data-pose="mission"] .t-arm-b { transform: rotate(60deg); }
.toon-him[data-pose="mission"] .t-head-j { transform: rotate(8deg); }
.toon-him[data-pose="mission"] { --lean: 22deg; }
.toon-him[data-pose="mission"] .t-svg { animation: t-sex-him 0.36s ease-in-out infinite; transform: translateY(8px); }

/* Straining a little. The green cloud does the rest. */
[data-pose="fart"] { --lean: -12deg; }
[data-pose="fart"] .t-arm-f { transform: rotate(28deg); }
[data-pose="fart"] .t-arm-b { transform: rotate(-22deg); }
[data-pose="fart"] .t-leg-f { transform: rotate(-8deg); }
[data-pose="fart"] .t-leg-b { transform: rotate(10deg); }
[data-pose="fart"] .t-head-j { transform: rotate(-6deg); }
[data-pose="fart"] .t-eyes { transform: scaleY(0.35); }

/* On the toilet: knees folded, hips dropped onto the seat. Feet stay at the
   place() anchor (the seat height); the SVG drop sinks the butt onto the rim. */
[data-pose="poop"] .t-leg-f, [data-pose="poop"] .t-leg-b { transform: rotate(-88deg); }
[data-pose="poop"] .t-leg-f-2, [data-pose="poop"] .t-leg-b-2 { transform: rotate(92deg); }
[data-pose="poop"] .t-arm-f { transform: rotate(-40deg); }
[data-pose="poop"] .t-arm-b { transform: rotate(36deg); }
[data-pose="poop"] .t-arm-f-2 { transform: rotate(48deg); }
[data-pose="poop"] .t-arm-b-2 { transform: rotate(44deg); }
[data-pose="poop"] .t-head-j { transform: rotate(6deg); }
[data-pose="poop"] { --lean: 2deg; }
.toon-him[data-pose="poop"] .t-svg { transform: translateY(38px); }
.toon-her[data-pose="poop"] .t-svg { transform: translateY(32px); }
/* While seated, hide the shoes so they don't stick through the bowl. */
[data-pose="poop"] .t-shoe { opacity: 0; }

/* Under the shower: arms up, rinsing. */
[data-pose="shower"] .t-arm-f { transform: rotate(-150deg); }
[data-pose="shower"] .t-arm-b { transform: rotate(148deg); }
[data-pose="shower"] .t-arm-f-2 { transform: rotate(-20deg); }
[data-pose="shower"] .t-head-j { transform: rotate(-8deg); }
[data-pose="shower"] .t-svg { animation: t-shower 1.1s ease-in-out infinite; }

@keyframes t-shower {
  0%, 100% { transform: translateY(0) rotate(-2deg); }
  50% { transform: translateY(-3px) rotate(2deg); }
}

/* Bent over / doggy: hips out (use with .t-back so ass faces him). */
.toon-her[data-pose="doggy"] { --lean: 32deg; }
.toon-her[data-pose="doggy"] .t-leg-f { transform: rotate(-22deg); }
.toon-her[data-pose="doggy"] .t-leg-b { transform: rotate(16deg); }
.toon-her[data-pose="doggy"] .t-leg-f-2 { transform: rotate(34deg); }
.toon-her[data-pose="doggy"] .t-leg-b-2 { transform: rotate(28deg); }
.toon-her[data-pose="doggy"] .t-arm-f { transform: rotate(78deg); }
.toon-her[data-pose="doggy"] .t-arm-b { transform: rotate(86deg); }
.toon-her[data-pose="doggy"] .t-arm-f-2 { transform: rotate(46deg); }
.toon-her[data-pose="doggy"] .t-head-j { transform: rotate(-10deg); }
.toon-her[data-pose="doggy"] .t-svg { transform: translateY(16px); }

/* Him behind her for doggy — hips into hers, slight thrust. */
.toon-him[data-pose="thrust"] .t-leg-f { transform: rotate(-14deg); }
.toon-him[data-pose="thrust"] .t-leg-b { transform: rotate(10deg); }
.toon-him[data-pose="thrust"] .t-leg-f-2 { transform: rotate(24deg); }
.toon-him[data-pose="thrust"] .t-arm-f { transform: rotate(-70deg); }
.toon-him[data-pose="thrust"] .t-arm-b { transform: rotate(-58deg); }
.toon-him[data-pose="thrust"] .t-head-j { transform: rotate(-6deg); }
.toon-him[data-pose="thrust"] { --lean: 12deg; }
.toon-him[data-pose="thrust"] .t-svg { animation: t-thrust 0.34s ease-in-out infinite; }

@keyframes t-thrust {
  0%, 100% { transform: translateX(0) translateY(0); }
  50% { transform: translateX(-6px) translateY(-3px); }
}

/* Squirt: legs wide, hips tipped, shaking. */
[data-pose="squirt"] .t-leg-f { transform: rotate(-48deg); }
[data-pose="squirt"] .t-leg-b { transform: rotate(28deg); }
[data-pose="squirt"] .t-leg-f-2 { transform: rotate(22deg); }
[data-pose="squirt"] .t-arm-f { transform: rotate(-150deg); }
[data-pose="squirt"] .t-arm-b { transform: rotate(140deg); }
[data-pose="squirt"] .t-head-j { transform: rotate(-10deg); }
[data-pose="squirt"] { --lean: -8deg; }
[data-pose="squirt"] .t-svg { animation: t-squirt 0.28s ease-in-out infinite; }

@keyframes t-squirt {
  0%, 100% { transform: translateY(0) scaleY(1); }
  50% { transform: translateY(-5px) scaleY(1.03); }
}

/* Presenting: hands on knees, ass out. */
[data-pose="present"] { --lean: 22deg; }
[data-pose="present"] .t-arm-f { transform: rotate(48deg); }
[data-pose="present"] .t-arm-b { transform: rotate(54deg); }
[data-pose="present"] .t-leg-f { transform: rotate(-10deg); }
[data-pose="present"] .t-leg-b { transform: rotate(8deg); }
[data-pose="present"] .t-head-j { transform: rotate(-16deg); }

/* Spank reaction: jump. */
[data-pose="spanked"] { --lean: -14deg; }
[data-pose="spanked"] .t-arm-f { transform: rotate(-150deg); }
[data-pose="spanked"] .t-arm-b { transform: rotate(140deg); }
[data-pose="spanked"] .t-svg { animation: t-squish 0.35s cubic-bezier(0.3, 1.5, 0.4, 1); }

/* Handjob: her hands at his hips, him standing. */
[data-pose="stroke"] .t-arm-f { transform: rotate(-70deg); }
[data-pose="stroke"] .t-arm-b { transform: rotate(-58deg); }
[data-pose="stroke"] .t-arm-f-2 { transform: rotate(30deg); }
[data-pose="stroke"] .t-arm-b-2 { transform: rotate(26deg); }
[data-pose="stroke"] { --lean: 6deg; }

/* Cowgirl: her on top (ride-like but sexy). */
[data-pose="cowgirl"] .t-leg-f { transform: rotate(-64deg); }
[data-pose="cowgirl"] .t-leg-b { transform: rotate(-58deg); }
[data-pose="cowgirl"] .t-leg-f-2 { transform: rotate(52deg); }
[data-pose="cowgirl"] .t-leg-b-2 { transform: rotate(48deg); }
[data-pose="cowgirl"] .t-arm-f { transform: rotate(-40deg); }
[data-pose="cowgirl"] .t-arm-b { transform: rotate(36deg); }
[data-pose="cowgirl"] .t-svg { animation: t-sex-her 0.42s ease-in-out infinite; transform: translateY(-8px); }

/* Spoon: both lean same way, close. */
[data-pose="spoon"] { --lean: 8deg; }
[data-pose="spoon"] .t-arm-f { transform: rotate(-90deg); }
[data-pose="spoon"] .t-arm-b { transform: rotate(-70deg); }
[data-pose="spoon"] .t-leg-f { transform: rotate(-20deg); }
[data-pose="spoon"] .t-leg-b { transform: rotate(-12deg); }

/* 69: both kind of inverted lean. */
[data-pose="sixty"] .t-leg-f { transform: rotate(-40deg); }
[data-pose="sixty"] .t-leg-b { transform: rotate(20deg); }
[data-pose="sixty"] .t-arm-f { transform: rotate(-100deg); }
[data-pose="sixty"] .t-head-j { transform: rotate(20deg); }
[data-pose="sixty"] { --lean: 16deg; }

/* Face sit: him flat-ish, her above. */
[data-pose="facesit"] .t-leg-f { transform: rotate(-70deg); }
[data-pose="facesit"] .t-leg-b { transform: rotate(-62deg); }
[data-pose="facesit"] .t-leg-f-2 { transform: rotate(50deg); }
[data-pose="facesit"] .t-arm-f { transform: rotate(-30deg); }
[data-pose="facesit"] .t-svg { transform: translateY(-16px); animation: t-sex-her 0.5s ease-in-out infinite; }

/* Wall: pressed close, arms up. */
[data-pose="wall"] { --lean: 4deg; }
[data-pose="wall"] .t-arm-f { transform: rotate(-160deg); }
[data-pose="wall"] .t-arm-b { transform: rotate(155deg); }
[data-pose="wall"] .t-leg-f { transform: rotate(-12deg); }

/* Finger: him reaches between her legs. */
[data-pose="finger"] .t-arm-f { transform: rotate(-80deg); }
[data-pose="finger"] .t-arm-f-2 { transform: rotate(20deg); }
[data-pose="finger"] { --lean: 10deg; }

/* Cuddle sit: close sit side by side energy. */
[data-pose="cuddle"] .t-arm-f { transform: rotate(-88deg); }
[data-pose="cuddle"] .t-arm-b { transform: rotate(-70deg); }
[data-pose="cuddle"] .t-leg-f { transform: rotate(-20deg); }
[data-pose="cuddle"] { --lean: 6deg; }

/* Big squeeze cuddle. */
[data-pose="squeeze"] .t-arm-f { transform: rotate(-110deg); }
[data-pose="squeeze"] .t-arm-b { transform: rotate(-100deg); }
[data-pose="squeeze"] .t-arm-f-2 { transform: rotate(40deg); }
[data-pose="squeeze"] .t-arm-b-2 { transform: rotate(36deg); }
[data-pose="squeeze"] { --lean: 10deg; }
[data-pose="squeeze"] .t-svg { animation: t-squeeze 1.2s ease-in-out infinite; }

@keyframes t-squeeze {
  0%, 100% { transform: scale(1); }
  50% { transform: scale(1.03) translateY(-2px); }
}

/* Head on chest / shoulder. */
[data-pose="nestle"] { --lean: 14deg; }
[data-pose="nestle"] .t-head-j { transform: rotate(18deg) translateY(4px); }
[data-pose="nestle"] .t-arm-f { transform: rotate(-70deg); }
[data-pose="nestle"] .t-arm-b { transform: rotate(20deg); }

/* Lying down for a cuddlenap. */
[data-pose="nap"] { --lean: -72deg; }
[data-pose="nap"] .t-arm-f { transform: rotate(-40deg); }
[data-pose="nap"] .t-arm-b { transform: rotate(50deg); }
[data-pose="nap"] .t-leg-f { transform: rotate(-8deg); }
[data-pose="nap"] .t-leg-b { transform: rotate(10deg); }
[data-pose="nap"] .t-eyes { transform: scaleY(0.12); }
[data-pose="nap"] .t-mouth { d: path("M-4 10 q4 2 8 0"); }

/* Soft breathing while napping. */
[data-pose="nap"] .t-svg { animation: t-nap-breathe 2.4s ease-in-out infinite; }
@keyframes t-nap-breathe {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-2px); }
}

/* --------------------------------------------------------------- props */

.cast-prop {
  position: absolute;
  left: 0;
  top: 0;
  width: var(--w, 120px);
  transform-origin: 50% 100%;
  transform: translate(calc(var(--x, 0px) - var(--w, 120px) / 2), calc(var(--y, 0px) - 100%));
  opacity: 0;
  visibility: hidden;
  transition: opacity 0.4s ease, visibility 0.4s;
}

.cast-prop svg { display: block; width: 100%; height: auto; }
.cast-prop.on { opacity: 1; visibility: visible; }
.cast-throne.arriving { animation: prop-in 0.6s cubic-bezier(0.2, 1.5, 0.4, 1); }

@keyframes prop-in {
  from { transform: translate(calc(var(--x) - var(--w) / 2), calc(var(--y) - 100% - 60px)) scale(0.6); opacity: 0; }
}

.p-gold { fill: #e8b53f; stroke: #b8811c; stroke-width: 1.5; }
.p-velvet { fill: #b3305c; }
.p-cushion { fill: #d94a7a; stroke: #a52c55; stroke-width: 1.5; }
.p-jewel { fill: #ff7aa8; stroke: #b8811c; stroke-width: 1.5; }

/* Floor-to-ceiling bars across the front, drawn over both of them. The rails
   are what make it a cage rather than a striped wall. */
.c-cage rect { fill: #14262f; stroke: #070d11; stroke-width: 1.5; }
.c-rail { stroke: #14262f; stroke-width: 7; fill: none; stroke-linecap: round; }
.c-lock circle { fill: #22323c; stroke: #070d11; stroke-width: 2; }
.c-lock rect { fill: #0b1318; }

.c-wall { fill: #2b1e28; stroke: #150d13; stroke-width: 2.5; }
.c-brick { stroke: rgba(255, 255, 255, 0.06); stroke-width: 2; fill: none; }
.c-window { fill: #7d97c4; opacity: 0.55; }
.c-bars rect { fill: #100a0e; }
.c-frame { fill: none; stroke: #100a0e; stroke-width: 5; }
.c-floor { fill: #180f16; }
.c-straw { stroke: #6b5a3a; stroke-width: 2; stroke-linecap: round; }

/* One shaft of light from the window. The only nice thing down there. */
.c-beam { fill: #9db6dd; opacity: 0.09; }
.c-spider { fill: #0f0a0e; }
.c-thread, .c-legs { stroke: #5b4c56; stroke-width: 1.2; fill: none; stroke-linecap: round; }

/* Behind the characters: he sits inside it, not in front of it. The bars are a
   separate element appended after them, so they close over him. */
.cast-cell { z-index: -1; }
.cast-bars { z-index: 1; }

/* The duvet. This is as far as it goes, and it is the same joke The Sims
   makes: you see the blanket move and nothing else. */
.cast-bed {
  position: absolute;
  left: 0;
  top: 0;
  width: 190px;
  height: 96px;
  transform: translate(calc(var(--x) - 95px), calc(var(--y) - 96px));
  animation: prop-drop 0.42s cubic-bezier(0.2, 1.4, 0.4, 1);
}

.bed-base {
  position: absolute;
  inset: auto 0 0 0;
  height: 26px;
  border-radius: 8px;
  background: #7a5a48;
  box-shadow: 0 6px 14px rgba(0, 0, 0, 0.25);
}

.bed-duvet {
  position: absolute;
  inset: 22px 0 18px 0;
  border-radius: 26px 26px 12px 12px;
  background: linear-gradient(160deg, #ffd7e6, #ffb0cd);
  border: 2px solid #f191b4;
}

.cast-bed.shaking .bed-duvet { animation: bed-shake 0.26s ease-in-out infinite; }
.cast-bed.gone { animation: prop-out 0.6s ease forwards; }

/* ---- hangouts: movie night + stargaze (persist like the throne) ---- */

/* `display: none` when it is off, and that word is load bearing.
 *
 * These sets were hidden with `visibility: hidden`, which leaves the boxes in
 * the layout and their animations running: twenty-eight twinkling stars and a
 * flickering television, repainting sixty times a second, invisibly, for the
 * whole of every evening. It was the entire idle animation cost of the room —
 * measured on the live page as twenty-nine of the thirty-six running
 * animations, none of them on screen. A skipped display puts them to sleep.
 *
 * The fade-in survives on Safari 17.4+ and Chrome via `@starting-style`;
 * anywhere else the set simply appears, which is what `.arriving` is already
 * doing over the top of it anyway. */
.cast-hangout {
  position: absolute;
  left: 0;
  top: 0;
  pointer-events: none;
  display: none;
  opacity: 0;
  transition: opacity 0.4s ease, display 0.4s allow-discrete;
  z-index: 0;
}

.cast-hangout.on {
  display: block;
  opacity: 1;
}

@starting-style {
  .cast-hangout.on { opacity: 0; }
}
.cast-hangout.arriving { animation: prop-in 0.55s cubic-bezier(0.2, 1.4, 0.4, 1); }

.cast-movie {
  width: 220px;
  height: 160px;
  transform: translate(calc(var(--x) - 110px), calc(var(--y) - 150px));
}
.mv-tv {
  position: absolute;
  left: 50%;
  bottom: 48px;
  transform: translateX(-50%);
  width: 120px;
}
.mv-screen {
  height: 72px;
  border-radius: 8px 8px 4px 4px;
  background: linear-gradient(160deg, #1a2744, #0d1528);
  border: 3px solid #2a3548;
  display: flex;
  align-items: center;
  justify-content: center;
  overflow: hidden;
  position: relative;
}
.mv-flicker {
  position: absolute;
  inset: 0;
  background: linear-gradient(120deg, transparent 40%, rgba(255, 255, 255, 0.08) 50%, transparent 60%);
  animation: mv-flick 2.8s linear infinite;
}
.mv-pop { font-size: 28px; position: relative; z-index: 1; filter: drop-shadow(0 2px 6px rgba(0, 0, 0, 0.4)); }
.mv-bezel {
  height: 8px;
  background: #3a4558;
  border-radius: 0 0 4px 4px;
}
.mv-stand {
  width: 28px;
  height: 14px;
  margin: 0 auto;
  background: #2a3548;
  border-radius: 0 0 6px 6px;
}
.mv-couch {
  position: absolute;
  left: 10%;
  right: 10%;
  bottom: 0;
  height: 36px;
  border-radius: 18px 18px 8px 8px;
  background: linear-gradient(180deg, #6b4a6e, #4a3250);
  border: 2px solid #3a2840;
  box-shadow: 0 6px 14px rgba(0, 0, 0, 0.25);
}

@keyframes mv-flick {
  0%, 100% { transform: translateX(-30%); opacity: 0.4; }
  50% { transform: translateX(30%); opacity: 0.9; }
}

.cast-stars {
  width: 260px;
  height: 180px;
  transform: translate(calc(var(--x) - 130px), calc(var(--y) - 170px));
}
.st-sky {
  position: absolute;
  inset: 0 10px 40px 10px;
  border-radius: 50% 50% 12px 12px;
  background: radial-gradient(ellipse at 50% 20%, #2a3a6a, #0c1020 70%);
  opacity: 0.92;
}
.st-moon {
  position: absolute;
  top: 18px;
  right: 36px;
  font-size: 22px;
  filter: drop-shadow(0 0 10px rgba(255, 240, 180, 0.5));
}
.st-field { position: absolute; inset: 0 10px 40px 10px; }
.st-dot {
  position: absolute;
  color: #fff8d8;
  opacity: 0.85;
  animation: st-twinkle 2.4s ease-in-out infinite;
  line-height: 1;
}
.st-blanket {
  position: absolute;
  left: 18%;
  right: 18%;
  bottom: 4px;
  height: 32px;
  border-radius: 20px 20px 10px 10px;
  background: linear-gradient(160deg, #3a2a50, #2a1e3a);
  border: 2px solid #5a4a70;
}

@keyframes st-twinkle {
  0%, 100% { opacity: 0.45; transform: scale(0.9); }
  50% { opacity: 1; transform: scale(1.15); }
}

/* Soft blanket for cuddlenaps — same anchor idea as the bed. */
.cast-blanket {
  position: absolute;
  left: 0;
  top: 0;
  width: 200px;
  height: 70px;
  transform: translate(calc(var(--x) - 100px), calc(var(--y) - 70px));
  pointer-events: none;
  animation: prop-drop 0.4s cubic-bezier(0.2, 1.4, 0.4, 1);
  z-index: 1;
}
.cast-blanket .bl-sheet {
  position: absolute;
  inset: 10px 8px 8px 8px;
  border-radius: 28px 28px 16px 16px;
  background: linear-gradient(160deg, #e8f0ff, #c8d9ff);
  border: 2px solid #9bb4e8;
  box-shadow: 0 6px 16px rgba(80, 100, 160, 0.25);
}
.cast-blanket .bl-heart {
  position: absolute;
  left: 50%;
  top: 22px;
  transform: translateX(-50%);
  font-size: 18px;
  filter: drop-shadow(0 2px 4px rgba(0, 0, 0, 0.12));
}
.cast-blanket.gone { animation: prop-out 0.55s ease forwards; }

@keyframes bed-shake {
  0%, 100% { transform: translate(-3px, 0) rotate(-1.4deg) scaleY(1); }
  50% { transform: translate(3px, -4px) rotate(1.4deg) scaleY(1.08); }
}

@keyframes prop-drop {
  from { transform: translate(calc(var(--x) - 95px), calc(var(--y) - 200px)); opacity: 0; }
}

@keyframes prop-out {
  to { opacity: 0; transform: translate(calc(var(--x) - 95px), calc(var(--y) - 140px)); }
}

/* Temporary date props (toilet, shower): same anchor as the bed. */
.cast-temp {
  position: absolute;
  left: 0;
  top: 0;
  width: var(--w, 90px);
  height: auto;
  transform: translate(calc(var(--x) - var(--w, 90px) / 2), calc(var(--y) - 100%));
  pointer-events: none;
  animation: prop-drop-temp 0.4s cubic-bezier(0.2, 1.4, 0.4, 1);
  z-index: 0; /* under .toon so they sit on the seat, not behind the tank */
}

.cast-temp svg { display: block; width: 100%; height: auto; }
.cast-temp.gone { animation: prop-out-temp 0.5s ease forwards; }

@keyframes prop-drop-temp {
  from { transform: translate(calc(var(--x) - var(--w, 90px) / 2), calc(var(--y) - 100% - 80px)); opacity: 0; }
}

@keyframes prop-out-temp {
  to { opacity: 0; transform: translate(calc(var(--x) - var(--w, 90px) / 2), calc(var(--y) - 100% - 40px)); }
}

.toilet-bowl { fill: #ebe6ef; stroke: #a89eb0; stroke-width: 1.5; }
.toilet-tank { fill: #e0dae8; stroke: #a89eb0; stroke-width: 1.5; }
.toilet-seat { fill: #fff; stroke: #9a90a4; stroke-width: 1.6; }
.toilet-seat-outer { fill: #f4f0f8; stroke: #a89eb0; stroke-width: 1.2; }
.toilet-seat-hole { fill: #c8c0d0; opacity: 0.55; }
.toilet-base { fill: rgba(58, 43, 51, 0.14); }
.toilet-lid { fill: #d4cedc; stroke: #a89eb0; stroke-width: 1.3; }

/* Toilets sit behind the toons; characters must appear ON the seat. */
.cast-temp { z-index: 0; }
.toon { z-index: 2; }
.cast-fx { z-index: 4; }

.shower-stall { fill: rgba(160, 200, 220, 0.35); stroke: #7aa8c0; stroke-width: 2; }
.shower-head { fill: #8a9aaa; stroke: #5a6a7a; stroke-width: 1.3; }
.shower-pipe { stroke: #8a9aaa; stroke-width: 4; fill: none; stroke-linecap: round; }
.shower-tray { fill: #c5d4de; stroke: #8aa0b0; stroke-width: 1.5; }
.shower-water {
  stroke: #7ec8e8;
  stroke-width: 1.6;
  stroke-linecap: round;
  opacity: 0.75;
  animation: shower-drip 0.55s linear infinite;
}

@keyframes shower-drip {
  0% { stroke-dashoffset: 0; opacity: 0.3; }
  50% { opacity: 0.9; }
  100% { stroke-dashoffset: 12; opacity: 0.3; }
}

/* ------------------------------------------------------------ comic fx */

.cast-fx { position: absolute; inset: 0; }

.cf {
  position: absolute;
  transform: translate(-50%, -100%);
  font-family: var(--font, sans-serif);
  font-weight: 900;
  pointer-events: none;
  white-space: nowrap;
}

.cf-hit {
  font-size: clamp(22px, 6.5vmin, 46px);
  color: #fff;
  -webkit-text-stroke: 3px var(--pink-deep, #d9407a);
  text-shadow: 0 8px 20px rgba(0, 0, 0, 0.35);
  animation: cf-pop 0.95s cubic-bezier(0.2, 1.5, 0.4, 1) both;
}

.cf-soft {
  font-size: clamp(30px, 9vmin, 62px);
  animation: cf-pop 1.1s cubic-bezier(0.2, 1.4, 0.4, 1) both;
}

.cf-bit {
  font-size: 19px;
  animation: cf-float 1.5s ease-out both;
}

@keyframes cf-pop {
  0% { opacity: 0; transform: translate(-50%, -100%) scale(0.3) rotate(-14deg); }
  30% { opacity: 1; transform: translate(-50%, -110%) scale(1.15) rotate(6deg); }
  60% { opacity: 1; transform: translate(-50%, -112%) scale(1) rotate(-2deg); }
  100% { opacity: 0; transform: translate(-50%, -140%) scale(1.05); }
}

@keyframes cf-float {
  0% { opacity: 0; transform: translate(-50%, -100%) scale(0.5); }
  20% { opacity: 1; transform: translate(-50%, -110%) scale(1); }
  100% { opacity: 0; transform: translate(calc(-50% + var(--d, 0px)), calc(-100% - var(--u, 60px))) scale(0.9); }
}

/* A hit lands on the room, not just on him. */
.cast-knock { animation: cast-knock 0.6s cubic-bezier(0.36, 0.07, 0.19, 0.97) both; }

@keyframes cast-knock {
  0%, 100% { transform: translate(0, 0) rotate(0); }
  15% { transform: translate(calc(var(--knock, 1) * -3px), calc(var(--knock, 1) * 1.5px)) rotate(calc(var(--knock, 1) * -0.14deg)); }
  35% { transform: translate(calc(var(--knock, 1) * 3px), calc(var(--knock, 1) * -1.5px)) rotate(calc(var(--knock, 1) * 0.12deg)); }
  60% { transform: translate(calc(var(--knock, 1) * -2px), calc(var(--knock, 1) * 1px)) rotate(calc(var(--knock, 1) * -0.08deg)); }
  85% { transform: translate(calc(var(--knock, 1) * 1px), 0) rotate(0); }
}

/* ------------------------------------------------------------- the room */

/* Keep the newest lines above their heads, but only when they are standing on
   the composer. With a video area to live in they are nowhere near the chat,
   and reserving space under it would just be wasted screen. */
body.cast-on:not(.cast-instage) .chat-log { padding-bottom: 96px; }

@media (max-width: 900px) {
  body.cast-on:not(.cast-instage) .chat-log { padding-bottom: 72px; }
}

@media (max-width: 480px) {
  body.cast-on:not(.cast-instage) .chat-log { padding-bottom: 58px; }
}

/* Walking, climbing and being thrown across the room is precisely what motion
   sensitivity means. The characters stay, they just stop performing: cast.js
   also skips the idle scheduler under this. */
@media (prefers-reduced-motion: reduce) {
  .t-eyes, [data-pose] .t-svg, [data-pose] .j { animation: none !important; }
  .cast-knock, .cast-bed.shaking .bed-duvet { animation: none !important; }
  .mv-flicker, .st-dot { animation: none !important; }
  .cf-bit, .cf-hit, .cf-soft { animation-duration: 0.01s; }
}
