PairingBirds.tsx

9.3 KB · raw

1// Pairing birds — the two illustrated characters used on the pairing screens.2//3// These are all that remains of the removed audio-pairing animation: the flow4// itself went with it, but the birds outlived it and still animate above the5// code-entry, connecting, SAS and paired screens (PairDevice.tsx's bird stage).6// The `-chirping` pose names are kept on purpose — the bird visibly chirps.78import type { Component } from "solid-js";910const FINCH = {11  hi: "#f4a6b6", mid: "#dc8294", lo: "#a85870", shade: "#7a3e54",12  cream: "#f5d8c8", beak: "#f5c764", beakShade: "#c8932e", legs: "#7a3e54",13};14const SWALLOW = {15  hi: "#f4c8a0", mid: "#dc9968", lo: "#a86b3e", shade: "#7a4a26",16  cream: "#f7e0c8", beak: "#f5c764", beakShade: "#b87a22", legs: "#5e3a1f",17};1819export const DesktopBird: Component<{ pose: string }> = (props) => {20  const p = FINCH;21  return (22    <div class={`peach-pair-bird peach-pair-bird-desktop peach-pair-bird-pose-${props.pose}`}>23      <svg viewBox="0 0 160 150" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMax meet">24        <defs>25          <radialGradient id="peach-pair-finch-body" cx="0.42" cy="0.32" r="0.72">26            <stop offset="0%" stop-color={p.hi} />27            <stop offset="55%" stop-color={p.mid} />28            <stop offset="100%" stop-color={p.lo} />29          </radialGradient>30          <radialGradient id="peach-pair-finch-head" cx="0.5" cy="0.4" r="0.7">31            <stop offset="0%" stop-color={p.hi} />32            <stop offset="100%" stop-color={p.mid} />33          </radialGradient>34          <linearGradient id="peach-pair-finch-belly" x1="0" y1="0" x2="0" y2="1">35            <stop offset="0%" stop-color={p.cream} stop-opacity="0" />36            <stop offset="50%" stop-color={p.cream} stop-opacity="0.85" />37            <stop offset="100%" stop-color={p.cream} stop-opacity="0.5" />38          </linearGradient>39        </defs>40        <ellipse class="peach-pair-bird-shadow" cx="78" cy="138" rx="40" ry="3.5" fill="rgba(0,0,0,0.45)" />41        <g class="peach-pair-bird-aura" opacity="0">42          <ellipse cx="76" cy="74" rx="60" ry="46" fill={p.hi} opacity="0.18" style={{ filter: "blur(8px)" }} />43        </g>44        <g class="peach-pair-bird-tail">45          <path d="M 32 92 Q 8 90, 6 80 Q 10 95, 28 100 Z" fill={p.lo} />46          <path d="M 32 95 Q 4 99, 4 92 Q 10 102, 28 102 Z" fill={p.shade} opacity="0.85" />47          <path d="M 32 98 Q 8 108, 12 110 Q 20 110, 30 105 Z" fill={p.lo} opacity="0.9" />48        </g>49        <g class="peach-pair-bird-feet" stroke={p.legs} stroke-width="1.6" stroke-linecap="round" fill="none">50          <line x1="62" y1="118" x2="60" y2="134" />51          <path d="M 60 134 L 55 138 M 60 134 L 60 139 M 60 134 L 65 138" stroke-width="1.2" />52          <line x1="84" y1="118" x2="86" y2="134" />53          <path d="M 86 134 L 81 138 M 86 134 L 86 139 M 86 134 L 91 138" stroke-width="1.2" />54        </g>55        <g class="peach-pair-bird-body">56          <path d="M 30 88 C 24 64, 40 38, 72 36 C 105 34, 124 56, 122 84 C 120 110, 100 122, 70 120 C 44 118, 32 108, 30 88 Z" fill="url(#peach-pair-finch-body)" />57          <path d="M 40 82 C 42 100, 60 112, 80 110 C 100 108, 110 100, 112 86 C 100 102, 80 106, 60 102 C 50 100, 44 92, 40 82 Z" fill="url(#peach-pair-finch-belly)" />58          <g class="peach-pair-bird-wing">59            <path d="M 56 60 C 68 56, 88 60, 102 68 C 108 76, 106 86, 96 90 C 80 92, 64 86, 54 78 C 50 70, 50 64, 56 60 Z" fill={p.lo} />60            <g stroke={p.shade} stroke-width="0.7" stroke-linecap="round" fill="none" opacity="0.55">61              <path d="M 64 64 Q 72 70, 78 80" />62              <path d="M 74 62 Q 82 70, 88 82" />63              <path d="M 84 64 Q 90 72, 94 84" />64            </g>65            <path d="M 56 60 C 68 56, 88 60, 102 68" stroke={p.shade} stroke-width="1" fill="none" opacity="0.4" />66          </g>67        </g>68        <g class="peach-pair-bird-head-group" style={{ "transform-origin": "95px 52px" }}>69          <g class="peach-pair-bird-tuft">70            <path d="M 88 22 Q 86 12, 94 12 Q 94 20, 92 26 Z" fill={p.lo} />71            <path d="M 96 20 Q 96 10, 104 14 Q 102 22, 100 26 Z" fill={p.shade} opacity="0.95" />72            <path d="M 104 24 Q 108 16, 114 20 Q 110 28, 106 30 Z" fill={p.lo} opacity="0.85" />73          </g>74          <circle cx="98" cy="48" r="26" fill="url(#peach-pair-finch-head)" />75          <path d="M 76 46 Q 80 30, 96 26 Q 86 32, 80 48 Z" fill={p.shade} opacity="0.35" />76          <g class="peach-pair-bird-eye-group">77            <circle cx="108" cy="42" r="4.5" fill="#1f0a10" />78            <circle cx="109.4" cy="40.5" r="1.4" fill="rgba(255,240,225,0.95)" />79            <circle cx="106.5" cy="44" r="0.5" fill="rgba(255,240,225,0.6)" />80          </g>81          <g class="peach-pair-bird-beak" style={{ "transform-origin": "120px 52px" }}>82            <path d="M 120 48 L 138 50 L 122 56 Z" fill={p.beak} />83            <path d="M 120 48 L 138 50 L 128 52 Z" fill={p.beakShade} opacity="0.55" />84            <path d="M 122 52 L 134 51" stroke={p.beakShade} stroke-width="0.6" opacity="0.6" />85          </g>86        </g>87      </svg>88    </div>89  );90};9192export const MobileBird: Component<{ pose: string }> = (props) => {93  const p = SWALLOW;94  return (95    <div class={`peach-pair-bird peach-pair-bird-mobile peach-pair-bird-pose-${props.pose}`}>96      <svg viewBox="0 0 160 150" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMax meet" style={{ transform: "scaleX(-1)" }}>97        <defs>98          <radialGradient id="peach-pair-swallow-body" cx="0.55" cy="0.35" r="0.75">99            <stop offset="0%" stop-color={p.hi} />100            <stop offset="50%" stop-color={p.mid} />101            <stop offset="100%" stop-color={p.lo} />102          </radialGradient>103          <radialGradient id="peach-pair-swallow-head" cx="0.45" cy="0.4" r="0.7">104            <stop offset="0%" stop-color={p.hi} />105            <stop offset="100%" stop-color={p.mid} />106          </radialGradient>107          <linearGradient id="peach-pair-swallow-throat" x1="0" y1="0" x2="0" y2="1">108            <stop offset="0%" stop-color={p.cream} stop-opacity="0.95" />109            <stop offset="100%" stop-color={p.cream} stop-opacity="0.2" />110          </linearGradient>111        </defs>112        <ellipse class="peach-pair-bird-shadow" cx="86" cy="140" rx="34" ry="3" fill="rgba(0,0,0,0.45)" />113        <g class="peach-pair-bird-aura" opacity="0">114          <ellipse cx="80" cy="78" rx="60" ry="42" fill={p.hi} opacity="0.18" style={{ filter: "blur(8px)" }} />115        </g>116        <g class="peach-pair-bird-tail">117          <path d="M 38 80 Q 8 70, 0 60 Q 8 78, 28 88 Q 14 90, 6 96 Q 16 94, 32 92 L 38 84 Z" fill={p.lo} />118          <path d="M 30 84 Q 14 78, 6 70 Q 18 84, 30 88 Z" fill={p.shade} opacity="0.7" />119        </g>120        <g class="peach-pair-bird-feet" stroke={p.legs} stroke-width="1.5" stroke-linecap="round" fill="none">121          <line x1="76" y1="118" x2="74" y2="136" />122          <path d="M 74 136 L 69 140 M 74 136 L 74 141 M 74 136 L 79 140" stroke-width="1.1" />123          <line x1="96" y1="118" x2="98" y2="136" />124          <path d="M 98 136 L 93 140 M 98 136 L 98 141 M 98 136 L 103 140" stroke-width="1.1" />125        </g>126        <g class="peach-pair-bird-body">127          <path d="M 36 82 C 32 56, 56 42, 88 44 C 116 46, 134 60, 134 80 C 134 102, 116 118, 90 118 C 60 118, 40 104, 36 82 Z" fill="url(#peach-pair-swallow-body)" />128          <path d="M 50 64 C 60 52, 80 46, 100 48 C 116 50, 126 60, 128 70 C 120 60, 100 56, 80 60 C 64 64, 56 68, 50 64 Z" fill={p.shade} opacity="0.5" />129          <path d="M 64 82 C 66 100, 86 114, 102 112 C 116 110, 124 100, 126 88 C 116 102, 100 108, 84 104 C 74 102, 68 94, 64 82 Z" fill="url(#peach-pair-swallow-throat)" />130          <g class="peach-pair-bird-wing">131            <path d="M 50 70 C 64 64, 84 66, 100 72 C 112 78, 112 90, 100 96 C 84 100, 64 96, 52 88 C 46 80, 46 74, 50 70 Z" fill={p.lo} />132            <g fill={p.shade} opacity="0.85">133              <path d="M 96 88 L 110 92 L 100 96 Z" />134              <path d="M 88 92 L 102 98 L 92 100 Z" />135              <path d="M 80 94 L 92 100 L 84 102 Z" />136            </g>137            <g stroke={p.shade} stroke-width="0.6" stroke-linecap="round" fill="none" opacity="0.5">138              <path d="M 58 76 Q 70 82, 80 90" />139              <path d="M 68 72 Q 80 80, 90 88" />140              <path d="M 80 70 Q 90 78, 100 86" />141            </g>142            <path d="M 50 70 C 64 64, 84 66, 100 72" stroke={p.shade} stroke-width="1" fill="none" opacity="0.55" />143          </g>144        </g>145        <g class="peach-pair-bird-head-group" style={{ "transform-origin": "116px 64px" }}>146          <circle cx="118" cy="64" r="20" fill="url(#peach-pair-swallow-head)" />147          <path d="M 100 60 Q 108 46, 124 46 Q 134 50, 136 60 Q 124 54, 108 60 Z" fill={p.shade} opacity="0.55" />148          <g class="peach-pair-bird-eye-group">149            <circle cx="126" cy="60" r="4" fill="#1f0a10" />150            <circle cx="127.2" cy="58.8" r="1.2" fill="rgba(255,240,225,0.95)" />151          </g>152          <g class="peach-pair-bird-beak" style={{ "transform-origin": "136px 64px" }}>153            <path d="M 136 60 L 154 62 L 138 66 Z" fill={p.beak} />154            <path d="M 136 60 L 154 62 L 145 63 Z" fill={p.beakShade} opacity="0.5" />155            <path d="M 138 63 L 152 63" stroke={p.beakShade} stroke-width="0.5" opacity="0.6" />156          </g>157        </g>158      </svg>159    </div>160  );161};