chesspal 0.0.1 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (273) hide show
  1. package/README.md +99 -2
  2. package/dist/cli/chat-hook.d.ts +2 -0
  3. package/dist/cli/chat-hook.js +33 -0
  4. package/dist/cli/cli.d.ts +2 -0
  5. package/dist/cli/cli.js +28 -0
  6. package/dist/cli/command-line.d.ts +7 -0
  7. package/dist/cli/command-line.js +177 -0
  8. package/dist/commands/agents.d.ts +26 -0
  9. package/dist/commands/agents.js +72 -0
  10. package/dist/commands/ai-move.d.ts +28 -0
  11. package/dist/commands/ai-move.js +85 -0
  12. package/dist/commands/ai-say.d.ts +22 -0
  13. package/dist/commands/ai-say.js +58 -0
  14. package/dist/commands/analyse-game.d.ts +65 -0
  15. package/dist/commands/analyse-game.js +265 -0
  16. package/dist/commands/annotate-game.d.ts +39 -0
  17. package/dist/commands/annotate-game.js +131 -0
  18. package/dist/commands/answer-lines.d.ts +28 -0
  19. package/dist/commands/answer-lines.js +144 -0
  20. package/dist/commands/clock.d.ts +15 -0
  21. package/dist/commands/clock.js +45 -0
  22. package/dist/commands/coach.d.ts +45 -0
  23. package/dist/commands/coach.js +344 -0
  24. package/dist/commands/context.d.ts +67 -0
  25. package/dist/commands/context.js +37 -0
  26. package/dist/commands/delete-game.d.ts +7 -0
  27. package/dist/commands/delete-game.js +30 -0
  28. package/dist/commands/delete-review.d.ts +8 -0
  29. package/dist/commands/delete-review.js +34 -0
  30. package/dist/commands/draw-offer.d.ts +10 -0
  31. package/dist/commands/draw-offer.js +43 -0
  32. package/dist/commands/drawings.d.ts +13 -0
  33. package/dist/commands/drawings.js +25 -0
  34. package/dist/commands/end-game.d.ts +23 -0
  35. package/dist/commands/end-game.js +67 -0
  36. package/dist/commands/export-game.d.ts +14 -0
  37. package/dist/commands/export-game.js +27 -0
  38. package/dist/commands/game-summary.d.ts +78 -0
  39. package/dist/commands/game-summary.js +102 -0
  40. package/dist/commands/index.d.ts +18 -0
  41. package/dist/commands/index.js +64 -0
  42. package/dist/commands/init.d.ts +15 -0
  43. package/dist/commands/init.js +73 -0
  44. package/dist/commands/lead-input.d.ts +8 -0
  45. package/dist/commands/lead-input.js +21 -0
  46. package/dist/commands/levels.d.ts +9 -0
  47. package/dist/commands/levels.js +25 -0
  48. package/dist/commands/look.d.ts +31 -0
  49. package/dist/commands/look.js +64 -0
  50. package/dist/commands/new-game.d.ts +68 -0
  51. package/dist/commands/new-game.js +218 -0
  52. package/dist/commands/opening-book.d.ts +13 -0
  53. package/dist/commands/opening-book.js +84 -0
  54. package/dist/commands/page-link.d.ts +11 -0
  55. package/dist/commands/page-link.js +28 -0
  56. package/dist/commands/past-games.d.ts +11 -0
  57. package/dist/commands/past-games.js +47 -0
  58. package/dist/commands/play-move.d.ts +37 -0
  59. package/dist/commands/play-move.js +94 -0
  60. package/dist/commands/player-move.d.ts +13 -0
  61. package/dist/commands/player-move.js +51 -0
  62. package/dist/commands/player-say.d.ts +5 -0
  63. package/dist/commands/player-say.js +16 -0
  64. package/dist/commands/ponder.d.ts +52 -0
  65. package/dist/commands/ponder.js +179 -0
  66. package/dist/commands/position.d.ts +14 -0
  67. package/dist/commands/position.js +130 -0
  68. package/dist/commands/profiles.d.ts +69 -0
  69. package/dist/commands/profiles.js +163 -0
  70. package/dist/commands/registry.d.ts +59 -0
  71. package/dist/commands/registry.js +62 -0
  72. package/dist/commands/say.d.ts +16 -0
  73. package/dist/commands/say.js +84 -0
  74. package/dist/commands/speech.d.ts +8 -0
  75. package/dist/commands/speech.js +20 -0
  76. package/dist/commands/status.d.ts +35 -0
  77. package/dist/commands/status.js +128 -0
  78. package/dist/commands/study-games.d.ts +5 -0
  79. package/dist/commands/study-games.js +40 -0
  80. package/dist/commands/study.d.ts +38 -0
  81. package/dist/commands/study.js +106 -0
  82. package/dist/commands/view.d.ts +14 -0
  83. package/dist/commands/view.js +125 -0
  84. package/dist/core/board-text.d.ts +7 -0
  85. package/dist/core/board-text.js +18 -0
  86. package/dist/core/book.json +1 -0
  87. package/dist/core/clock.d.ts +45 -0
  88. package/dist/core/clock.js +109 -0
  89. package/dist/core/facts.d.ts +55 -0
  90. package/dist/core/facts.js +175 -0
  91. package/dist/core/game.d.ts +95 -0
  92. package/dist/core/game.js +352 -0
  93. package/dist/core/illegal.d.ts +19 -0
  94. package/dist/core/illegal.js +141 -0
  95. package/dist/core/index.d.ts +20 -0
  96. package/dist/core/index.js +12 -0
  97. package/dist/core/lead.d.ts +15 -0
  98. package/dist/core/lead.js +40 -0
  99. package/dist/core/levels.d.ts +20 -0
  100. package/dist/core/levels.js +33 -0
  101. package/dist/core/material.d.ts +9 -0
  102. package/dist/core/material.js +12 -0
  103. package/dist/core/moods.d.ts +21 -0
  104. package/dist/core/moods.js +45 -0
  105. package/dist/core/openings.d.ts +35 -0
  106. package/dist/core/openings.js +98 -0
  107. package/dist/core/openings.json +1 -0
  108. package/dist/core/pgn.d.ts +10 -0
  109. package/dist/core/pgn.js +213 -0
  110. package/dist/core/profile.d.ts +117 -0
  111. package/dist/core/profile.js +232 -0
  112. package/dist/core/record.d.ts +21 -0
  113. package/dist/core/record.js +25 -0
  114. package/dist/core/review.d.ts +80 -0
  115. package/dist/core/review.js +146 -0
  116. package/dist/games/ai-model.d.ts +5 -0
  117. package/dist/games/ai-model.js +15 -0
  118. package/dist/games/analysis.d.ts +12 -0
  119. package/dist/games/analysis.js +26 -0
  120. package/dist/games/archive.d.ts +63 -0
  121. package/dist/games/archive.js +242 -0
  122. package/dist/games/chat-start.d.ts +16 -0
  123. package/dist/games/chat-start.js +26 -0
  124. package/dist/games/coach.d.ts +57 -0
  125. package/dist/games/coach.js +144 -0
  126. package/dist/games/current-game.d.ts +56 -0
  127. package/dist/games/current-game.js +134 -0
  128. package/dist/games/files.d.ts +9 -0
  129. package/dist/games/files.js +53 -0
  130. package/dist/games/index.d.ts +11 -0
  131. package/dist/games/index.js +12 -0
  132. package/dist/games/plans.d.ts +9 -0
  133. package/dist/games/plans.js +23 -0
  134. package/dist/games/table-talk.d.ts +12 -0
  135. package/dist/games/table-talk.js +25 -0
  136. package/dist/games/talk.d.ts +36 -0
  137. package/dist/games/talk.js +131 -0
  138. package/dist/games/thoughts.d.ts +15 -0
  139. package/dist/games/thoughts.js +60 -0
  140. package/dist/home/data-folder.d.ts +12 -0
  141. package/dist/home/data-folder.js +55 -0
  142. package/dist/home/index.d.ts +2 -0
  143. package/dist/home/index.js +3 -0
  144. package/dist/home/open-folder.d.ts +2 -0
  145. package/dist/home/open-folder.js +14 -0
  146. package/dist/host-files/agents.d.ts +20 -0
  147. package/dist/host-files/agents.js +53 -0
  148. package/dist/host-files/host-files.d.ts +15 -0
  149. package/dist/host-files/host-files.js +182 -0
  150. package/dist/host-files/index.d.ts +2 -0
  151. package/dist/host-files/index.js +3 -0
  152. package/dist/index.d.ts +2 -0
  153. package/dist/index.js +3 -0
  154. package/dist/page/app/anchor.js +88 -0
  155. package/dist/page/app/app.js +458 -0
  156. package/dist/page/app/keys.js +94 -0
  157. package/dist/page/app/phase.js +30 -0
  158. package/dist/page/app/served.js +73 -0
  159. package/dist/page/avatars/avatars.js +19 -0
  160. package/dist/page/avatars/beanie.webp +0 -0
  161. package/dist/page/avatars/beard.webp +0 -0
  162. package/dist/page/avatars/bun.webp +0 -0
  163. package/dist/page/avatars/cat.webp +0 -0
  164. package/dist/page/avatars/curls.webp +0 -0
  165. package/dist/page/avatars/glasses.webp +0 -0
  166. package/dist/page/avatars/moustache.webp +0 -0
  167. package/dist/page/avatars/owl.webp +0 -0
  168. package/dist/page/avatars/short-hair.webp +0 -0
  169. package/dist/page/avatars/silver-bob.webp +0 -0
  170. package/dist/page/avatars/thought.js +10 -0
  171. package/dist/page/avatars/torretta.js +38 -0
  172. package/dist/page/base.css +160 -0
  173. package/dist/page/board/board.css +959 -0
  174. package/dist/page/board/board.js +125 -0
  175. package/dist/page/board/explain.js +58 -0
  176. package/dist/page/board/marks.js +160 -0
  177. package/dist/page/board/motion.js +71 -0
  178. package/dist/page/board/pointer.js +239 -0
  179. package/dist/page/board/squares.js +26 -0
  180. package/dist/page/dialogs/analyses.js +195 -0
  181. package/dist/page/dialogs/dialog.js +26 -0
  182. package/dist/page/dialogs/dialogs.css +1228 -0
  183. package/dist/page/dialogs/new-game.js +42 -0
  184. package/dist/page/dialogs/profiles.js +152 -0
  185. package/dist/page/dialogs/search.js +31 -0
  186. package/dist/page/dialogs/settings.js +42 -0
  187. package/dist/page/dialogs/studied.js +39 -0
  188. package/dist/page/dialogs/study.js +51 -0
  189. package/dist/page/game/badges.js +50 -0
  190. package/dist/page/game/browse.js +17 -0
  191. package/dist/page/game/card-fold.js +40 -0
  192. package/dist/page/game/column.js +51 -0
  193. package/dist/page/game/ending.js +73 -0
  194. package/dist/page/game/game.css +577 -0
  195. package/dist/page/game/move-list.js +72 -0
  196. package/dist/page/game/opening-book.js +14 -0
  197. package/dist/page/game/plies.js +82 -0
  198. package/dist/page/game/result-tile.js +14 -0
  199. package/dist/page/helps/helps.css +689 -0
  200. package/dist/page/helps/helps.js +220 -0
  201. package/dist/page/helps/view.js +127 -0
  202. package/dist/page/layout/fold-tab.js +11 -0
  203. package/dist/page/layout/folds.js +43 -0
  204. package/dist/page/layout/header.js +28 -0
  205. package/dist/page/layout/layout.css +834 -0
  206. package/dist/page/lead/lead.js +82 -0
  207. package/dist/page/lead/view.js +38 -0
  208. package/dist/page/main.js +8 -0
  209. package/dist/page/pieces/CREDITS.md +19 -0
  210. package/dist/page/pieces/capablanca-small.svg +20 -0
  211. package/dist/page/pieces/capablanca.svg +22 -0
  212. package/dist/page/pieces/nitida-small.svg +20 -0
  213. package/dist/page/pieces/nitida.svg +21 -0
  214. package/dist/page/pieces/pieces.js +28 -0
  215. package/dist/page/pieces/torretta-small.svg +20 -0
  216. package/dist/page/pieces/torretta.svg +21 -0
  217. package/dist/page/players/card.js +56 -0
  218. package/dist/page/players/clock-time.js +12 -0
  219. package/dist/page/players/model-name.js +20 -0
  220. package/dist/page/profile/profile.js +99 -0
  221. package/dist/page/profile/settings.js +77 -0
  222. package/dist/page/sounds/capture.ogg +0 -0
  223. package/dist/page/sounds/check.ogg +0 -0
  224. package/dist/page/sounds/draw.ogg +0 -0
  225. package/dist/page/sounds/flag.ogg +0 -0
  226. package/dist/page/sounds/mate.ogg +0 -0
  227. package/dist/page/sounds/move.ogg +0 -0
  228. package/dist/page/sounds/offer.ogg +0 -0
  229. package/dist/page/sounds/resign.ogg +0 -0
  230. package/dist/page/sounds/sounds.js +105 -0
  231. package/dist/page/style.css +9 -0
  232. package/dist/page/talk/talk.css +958 -0
  233. package/dist/page/talk/talk.js +165 -0
  234. package/dist/page/talk/voice.js +38 -0
  235. package/dist/page/text.js +711 -0
  236. package/dist/page/theme.css +292 -0
  237. package/dist/profiles/avatar.d.ts +23 -0
  238. package/dist/profiles/avatar.js +84 -0
  239. package/dist/profiles/front-matter.d.ts +9 -0
  240. package/dist/profiles/front-matter.js +33 -0
  241. package/dist/profiles/index.d.ts +3 -0
  242. package/dist/profiles/index.js +5 -0
  243. package/dist/profiles/profiles.d.ts +69 -0
  244. package/dist/profiles/profiles.js +254 -0
  245. package/dist/profiles/settings.d.ts +12 -0
  246. package/dist/profiles/settings.js +46 -0
  247. package/dist/server/bell.d.ts +68 -0
  248. package/dist/server/bell.js +225 -0
  249. package/dist/server/caller.d.ts +21 -0
  250. package/dist/server/caller.js +132 -0
  251. package/dist/server/mcp.d.ts +13 -0
  252. package/dist/server/mcp.js +207 -0
  253. package/dist/server/page/analyses.d.ts +23 -0
  254. package/dist/server/page/analyses.js +65 -0
  255. package/dist/server/page/files.d.ts +5 -0
  256. package/dist/server/page/files.js +92 -0
  257. package/dist/server/page/game.d.ts +6 -0
  258. package/dist/server/page/game.js +381 -0
  259. package/dist/server/page/http.d.ts +29 -0
  260. package/dist/server/page/http.js +68 -0
  261. package/dist/server/page/page.d.ts +41 -0
  262. package/dist/server/page/page.js +229 -0
  263. package/dist/server/page/profiles.d.ts +19 -0
  264. package/dist/server/page/profiles.js +215 -0
  265. package/dist/version.d.ts +2 -0
  266. package/dist/version.js +3 -0
  267. package/instructions/analysis.md +4 -0
  268. package/instructions/analyst-agent.md +8 -0
  269. package/instructions/analyst.md +4 -0
  270. package/instructions/coach.md +11 -0
  271. package/instructions/opponent.md +15 -0
  272. package/instructions/ponder.md +9 -0
  273. package/package.json +66 -5
@@ -0,0 +1,959 @@
1
+ /* The board: squares, pieces and how they move, the marks, the drawings, the notices and the end of a game */
2
+
3
+ /* The board, above the columns' tabs, which slide under it */
4
+ .board {
5
+ -webkit-tap-highlight-color: transparent;
6
+ -webkit-touch-callout: none;
7
+ position: relative;
8
+ z-index: 1;
9
+ display: grid;
10
+ grid-template-columns: repeat(8, 1fr);
11
+ grid-template-rows: repeat(8, 1fr);
12
+ aspect-ratio: 1;
13
+ border-radius: var(--radius);
14
+ overflow: hidden;
15
+ box-shadow: var(--board-shadow);
16
+ user-select: none;
17
+ container-type: inline-size;
18
+ }
19
+
20
+ .square {
21
+ position: relative;
22
+ }
23
+
24
+ .square.light {
25
+ background-color: var(--light-square);
26
+ }
27
+
28
+ .square.dark {
29
+ background-color: var(--dark-square);
30
+ }
31
+
32
+ .square.last {
33
+ background-image: linear-gradient(var(--last-move), var(--last-move));
34
+ }
35
+
36
+ .square.selected {
37
+ background-image: linear-gradient(var(--selected), var(--selected));
38
+ }
39
+
40
+ /* The king checkmated: its square red, over the last move's colour */
41
+ .square.mated {
42
+ background-image: linear-gradient(var(--mated), var(--mated));
43
+ }
44
+
45
+ /* The player's marks with the right button: orange squares and arrows, gone at a left click */
46
+ .square.marked {
47
+ background-image: linear-gradient(var(--mark-square), var(--mark-square));
48
+ }
49
+
50
+ /* The square the player points at in the AI's words: a white ring with a dark edge, which reads on every board and over the rings the AI draws */
51
+ .square.pointed {
52
+ outline: max(4px, 0.8cqw) solid #fff;
53
+ outline-offset: calc(-1 * max(4px, 0.8cqw));
54
+ box-shadow: inset 0 0 0 calc(max(4px, 0.8cqw) + 2px) rgba(0, 0, 0, 0.55);
55
+ }
56
+
57
+ /* On a square the AI marked, the pointer's ring takes the place of the AI's while it lasts */
58
+ .square.noted.pointed::after {
59
+ box-shadow: inset 0 0 0 max(4px, 0.8cqw) #fff, inset 0 0 0 calc(max(4px, 0.8cqw) + 2px) rgba(0, 0, 0, 0.55);
60
+ }
61
+
62
+ .arrows {
63
+ position: absolute;
64
+ left: 0;
65
+ top: 0;
66
+ width: 100%;
67
+ aspect-ratio: 1;
68
+ /* Over every square, the sliding piece's too (z-index 1) */
69
+ z-index: 2;
70
+ pointer-events: none;
71
+ opacity: 0.7;
72
+ }
73
+
74
+ /* The helps' arrows, seen while the player plays, show the pieces through them more (Stefano) */
75
+ .arrows.drawn {
76
+ opacity: 0.5;
77
+ }
78
+
79
+ /* The round cap closes a shaft round the square's centre, as on Lichess: arrows off one square meet in one round knot, at any angle (Stefano) */
80
+ .arrows path {
81
+ fill: none;
82
+ stroke: var(--mark);
83
+ stroke-width: 0.2;
84
+ stroke-linecap: round;
85
+ }
86
+
87
+ /* The helps' arrows, the AI's and the coach's, thinner than the player's own, on a thin edge: they read as the helps' (Stefano) */
88
+ .arrows.drawn path {
89
+ stroke-width: 0.14;
90
+ }
91
+
92
+ .arrows polygon {
93
+ fill: var(--mark);
94
+ }
95
+
96
+ /* What the AI draws with its note in an analysis (D-69): arrows and squares in the four colours of PGN's %cal and %csl */
97
+ .arrows g.green {
98
+ --mark: var(--drawn-green);
99
+ }
100
+
101
+ .arrows g.red {
102
+ --mark: var(--drawn-red);
103
+ }
104
+
105
+ .arrows g.yellow {
106
+ --mark: var(--drawn-yellow);
107
+ }
108
+
109
+ .arrows g.blue {
110
+ --mark: var(--drawn-blue);
111
+ }
112
+
113
+ /* The AI's arrows have a white edge, as its rings do (D-78): they read as its own whatever colour the player marks in */
114
+ .arrows .edge {
115
+ stroke: #fff;
116
+ }
117
+
118
+ .arrows path.edge {
119
+ stroke-width: 0.2;
120
+ }
121
+
122
+ .arrows polygon.edge {
123
+ fill: #fff;
124
+ stroke-width: 0.06;
125
+ stroke-linejoin: round;
126
+ }
127
+
128
+ /* The AI's squares are rings (Zoe): the last move's fill and the player's marks stay seen under them */
129
+ .square.noted::after {
130
+ content: "";
131
+ position: absolute;
132
+ inset: 0;
133
+ pointer-events: none;
134
+ box-shadow: inset 0 0 0 max(3px, 0.6cqw) var(--ring), inset 0 0 0 calc(max(3px, 0.6cqw) + 1.5px) rgba(255, 255, 255, 0.8);
135
+ }
136
+
137
+ .square.noted.green {
138
+ --ring: var(--drawn-green);
139
+ }
140
+
141
+ .square.noted.red {
142
+ --ring: var(--drawn-red);
143
+ }
144
+
145
+ .square.noted.yellow {
146
+ --ring: var(--drawn-yellow);
147
+ }
148
+
149
+ .square.noted.blue {
150
+ --ring: var(--drawn-blue);
151
+ }
152
+
153
+ /* Moving a piece (Zoe's design): dots for the legal moves, rings on captures, a gold edge on the square a dragged piece would land on */
154
+ .square.hint::before {
155
+ content: "";
156
+ position: absolute;
157
+ inset: 0;
158
+ margin: auto;
159
+ width: 33%;
160
+ height: 33%;
161
+ border-radius: 50%;
162
+ background: var(--hint);
163
+ pointer-events: none;
164
+ }
165
+
166
+ .square.hint.capture::before {
167
+ inset: 4%;
168
+ width: auto;
169
+ height: auto;
170
+ margin: 0;
171
+ background: none;
172
+ border: 1cqw solid var(--hint);
173
+ }
174
+
175
+ .square.over {
176
+ box-shadow: inset 0 0 0 max(2px, 0.625cqw) var(--hover-edge);
177
+ }
178
+
179
+ @media (hover: hover) {
180
+ .board.playable .square.hint:hover,
181
+ .board.playable .square.target:hover {
182
+ box-shadow: inset 0 0 0 max(2px, 0.625cqw) var(--hover-edge);
183
+ }
184
+ }
185
+
186
+ .square.from .piece {
187
+ opacity: var(--ghost);
188
+ }
189
+
190
+ .board.playable .square.own {
191
+ cursor: grab;
192
+ touch-action: none;
193
+ }
194
+
195
+ .board.playable .square.hint,
196
+ .board.playable .square.target {
197
+ cursor: pointer;
198
+ }
199
+
200
+ /* Over every element's own cursor, the hand's while a piece is in the air */
201
+ :root.dragging,
202
+ :root.dragging * {
203
+ cursor: grabbing !important;
204
+ }
205
+
206
+ /* The lifted piece follows the pointer without a transition; the piece inside it rises. Fixed to the window, it never gives the page a scrollbar */
207
+ .lifted {
208
+ position: fixed;
209
+ left: 0;
210
+ top: 0;
211
+ width: 12.5cqw;
212
+ height: 12.5cqw;
213
+ z-index: 10;
214
+ pointer-events: none;
215
+ translate: var(--x) var(--y);
216
+ }
217
+
218
+ .lifted .piece {
219
+ display: block;
220
+ width: 100%;
221
+ height: 100%;
222
+ transition: transform 120ms cubic-bezier(0.2, 0.8, 0.2, 1), filter 120ms cubic-bezier(0.2, 0.8, 0.2, 1);
223
+ filter: drop-shadow(0 0 0 transparent);
224
+ }
225
+
226
+ .lifted.up .piece {
227
+ transform: scale(1.1);
228
+ filter: drop-shadow(0.375cqw 1cqw 0.75cqw var(--lift-shadow));
229
+ }
230
+
231
+ .lifted.up.touch .piece {
232
+ transform: translateY(-75%) scale(1.25);
233
+ }
234
+
235
+ .lifted.back {
236
+ transition: translate 180ms cubic-bezier(0.2, 0.8, 0.2, 1);
237
+ }
238
+
239
+ .lifted.back .piece {
240
+ transform: none;
241
+ filter: drop-shadow(0 0 0 transparent);
242
+ transition-duration: 180ms;
243
+ }
244
+
245
+ /* A move not dragged slides from its square, in 12.5cqw a square, and a king in check rings like a bell struck */
246
+ .square.slide {
247
+ z-index: 1;
248
+ }
249
+
250
+ /* The square's own piece only: one taken or fading on it keeps its own animation */
251
+ .square.slide > .piece {
252
+ animation: slide 220ms cubic-bezier(0.25, 0.8, 0.35, 1);
253
+ }
254
+
255
+ .square.shake > .piece {
256
+ animation: ring 320ms cubic-bezier(0.3, 0, 0.2, 1) 200ms;
257
+ }
258
+
259
+ /* The piece taken stays under the one that takes it until it arrives */
260
+ .square .taken-piece .piece {
261
+ animation: taken 220ms step-end forwards;
262
+ }
263
+
264
+ /* A jump through the game: a piece no longer there fades, one that was not there appears */
265
+ .square .gone-piece .piece {
266
+ animation: taken 220ms forwards;
267
+ }
268
+
269
+ .square.appear > .piece {
270
+ animation: appear 220ms;
271
+ }
272
+
273
+ @keyframes appear {
274
+ from {
275
+ opacity: 0;
276
+ }
277
+ }
278
+
279
+ @keyframes taken {
280
+ to {
281
+ opacity: 0;
282
+ }
283
+ }
284
+
285
+ @keyframes slide {
286
+ from {
287
+ translate: calc(var(--dx) * 12.5cqw) calc(var(--dy) * 12.5cqw);
288
+ }
289
+ }
290
+
291
+ /* The hammer's blow, then a vibration that dies away: each swing smaller, eased at both ends */
292
+ @keyframes ring {
293
+ 12% {
294
+ translate: -2.5% 0;
295
+ }
296
+
297
+ 34% {
298
+ translate: 1.8% 0;
299
+ }
300
+
301
+ 56% {
302
+ translate: -1.1% 0;
303
+ }
304
+
305
+ 78% {
306
+ translate: 0.5% 0;
307
+ }
308
+ }
309
+
310
+ @media (prefers-reduced-motion: reduce) {
311
+ .lifted .piece,
312
+ .lifted.back {
313
+ transition: none;
314
+ }
315
+
316
+ .square.slide > .piece,
317
+ .square.shake > .piece,
318
+ .square.appear > .piece {
319
+ animation: none;
320
+ }
321
+
322
+ .square .taken-piece,
323
+ .square .gone-piece {
324
+ display: none;
325
+ }
326
+ }
327
+
328
+ /* The end of the game: the kings' badges and the loser's king laid down stay; the effects play once, when the game ends under the player's eyes */
329
+ /* The king laid down keeps its size and lies on the base the other pieces stand on, in the middle of its square: it turns about the point that puts it
330
+ there, which each set's drawing places */
331
+ .square.fallen .piece {
332
+ rotate: -90deg;
333
+ transform-origin: var(--fallen-pivot, 50% 50%);
334
+ }
335
+
336
+ :root[data-set="nitida"] {
337
+ --fallen-pivot: 53.7% 54.8%;
338
+ }
339
+
340
+ :root[data-set="torretta"] {
341
+ --fallen-pivot: 52% 50.8%;
342
+ }
343
+
344
+ :root[data-set="capablanca"] {
345
+ --fallen-pivot: 50% 52.5%;
346
+ }
347
+
348
+ .badge {
349
+ position: absolute;
350
+ top: 2.5%;
351
+ right: 2.5%;
352
+ z-index: 2;
353
+ display: grid;
354
+ place-items: center;
355
+ width: clamp(14px, 3.8cqw, 30px);
356
+ height: clamp(14px, 3.8cqw, 30px);
357
+ border-radius: 50%;
358
+ color: #fff;
359
+ font-size: clamp(9px, 2.5cqw, 19px);
360
+ font-weight: 800;
361
+ line-height: 1;
362
+ /* Edged in white, to stand out on any square, the red one too */
363
+ box-shadow: 0 0 0 max(1.5px, 0.25cqw) #fff, 0 0.2cqw 0.6cqw rgba(0, 0, 0, 0.4);
364
+ pointer-events: none;
365
+ }
366
+
367
+ .badge svg {
368
+ width: 66%;
369
+ height: 66%;
370
+ fill: currentColor;
371
+ stroke: currentColor;
372
+ stroke-width: 2;
373
+ stroke-linejoin: round;
374
+ stroke-linecap: round;
375
+ }
376
+
377
+ .badge.crown {
378
+ background: #e0a526;
379
+ color: #3b2a00;
380
+ }
381
+
382
+ /* The mate white on the red square, edged in red */
383
+ .badge.mate {
384
+ background: #fff;
385
+ color: #c81e1e;
386
+ box-shadow: 0 0 0 max(1.5px, 0.25cqw) #c81e1e, 0 0.2cqw 0.6cqw rgba(0, 0, 0, 0.4);
387
+ }
388
+
389
+ .badge.flag {
390
+ background: #4a4f57;
391
+ }
392
+
393
+ .badge.half {
394
+ background: #69727d;
395
+ }
396
+
397
+ /* A move's badge (D-69): 16px beside a move, a third of a square in the corner of its arrival, edged in white as the kings' badges */
398
+ .move-badge {
399
+ flex: none;
400
+ width: 16px;
401
+ height: 16px;
402
+ vertical-align: -3px;
403
+ }
404
+
405
+ .square .move-badge {
406
+ position: absolute;
407
+ top: 2.5%;
408
+ right: 2.5%;
409
+ z-index: 2;
410
+ width: clamp(16px, 4.25cqw, 30px);
411
+ height: clamp(16px, 4.25cqw, 30px);
412
+ border-radius: 50%;
413
+ box-shadow: 0 0 0 max(1.5px, 0.25cqw) #fff, 0 0.2cqw 0.6cqw rgba(0, 0, 0, 0.4);
414
+ pointer-events: none;
415
+ }
416
+
417
+ /* With the king's badge on the same square, at the end, the move's stands beside it (Zoe) */
418
+ .square:has(.badge) .move-badge {
419
+ right: calc(2.5% + clamp(14px, 3.8cqw, 30px) + max(4px, 0.6cqw));
420
+ }
421
+
422
+ /* Checkmate: the king rings with the check, its square fills with red from the edges, then it falls */
423
+ @media (prefers-reduced-motion: no-preference) {
424
+ .board.fresh.by-checkmate .square.mated {
425
+ background-image: none;
426
+ animation: mate-in 300ms cubic-bezier(0.2, 0.8, 0.2, 1) 450ms forwards;
427
+ }
428
+ }
429
+
430
+ .board.fresh.by-checkmate .square.fallen .piece {
431
+ animation: ring 320ms cubic-bezier(0.3, 0, 0.2, 1) 200ms, fall 450ms 800ms backwards;
432
+ }
433
+
434
+ /* A resignation: the king is laid down slowly, as a player lays theirs */
435
+ .board.fresh.by-resignation .square.fallen .piece {
436
+ animation: lay 1000ms cubic-bezier(0.45, 0, 0.55, 1) 250ms backwards;
437
+ }
438
+
439
+ /* Stalemate: the king darts one way and the other, and finds no square */
440
+ .board.fresh.by-stalemate .square.stalled .piece {
441
+ animation: dart 700ms cubic-bezier(0.4, 0, 0.2, 1) 250ms;
442
+ }
443
+
444
+ /* The other draws: both kings light up together */
445
+ .board.fresh:not(.by-stalemate) .square.drawn {
446
+ animation: draw-glow 1000ms ease-in-out 200ms;
447
+ }
448
+
449
+ /* The badges come once the kings have taken the end */
450
+ .board.fresh .badge {
451
+ animation: badge-in 320ms cubic-bezier(0.3, 1.4, 0.5, 1) 800ms backwards;
452
+ }
453
+
454
+ .board.fresh.by-checkmate .badge {
455
+ animation-delay: 1350ms;
456
+ }
457
+
458
+ .board.fresh.by-resignation .badge {
459
+ animation-delay: 1200ms;
460
+ }
461
+
462
+ .board.fresh.by-stalemate .badge {
463
+ animation-delay: 950ms;
464
+ }
465
+
466
+ /* The player won: a narrow golden sheen crosses the board once, after the badges; a loss needs no more than the king and its badge */
467
+ .board.fresh.player-won::after {
468
+ content: "";
469
+ position: absolute;
470
+ inset: 0;
471
+ z-index: 3;
472
+ pointer-events: none;
473
+ background: linear-gradient(115deg, transparent 44%, rgba(255, 230, 150, 0.6) 50%, transparent 56%) 0 0 / 300% 100% no-repeat;
474
+ mix-blend-mode: screen;
475
+ animation: sheen 900ms cubic-bezier(0.45, 0, 0.55, 1) 1600ms backwards;
476
+ }
477
+
478
+ @keyframes mate-in {
479
+ from {
480
+ box-shadow: inset 0 0 0 0 var(--mated);
481
+ }
482
+
483
+ to {
484
+ box-shadow: inset 0 0 0 7cqw var(--mated);
485
+ }
486
+ }
487
+
488
+ /* Falling, the king drops fast, bounces once off the board and settles */
489
+ @keyframes fall {
490
+ from {
491
+ rotate: 0deg;
492
+ animation-timing-function: cubic-bezier(0.55, 0, 1, 0.45);
493
+ }
494
+
495
+ 45% {
496
+ rotate: -90deg;
497
+ animation-timing-function: cubic-bezier(0, 0.55, 0.45, 1);
498
+ }
499
+
500
+ 68% {
501
+ rotate: -74deg;
502
+ animation-timing-function: cubic-bezier(0.55, 0, 1, 0.45);
503
+ }
504
+
505
+ 86% {
506
+ rotate: -90deg;
507
+ animation-timing-function: cubic-bezier(0, 0.55, 0.45, 1);
508
+ }
509
+
510
+ 94% {
511
+ rotate: -86deg;
512
+ animation-timing-function: ease-in;
513
+ }
514
+ }
515
+
516
+ @keyframes lay {
517
+ from {
518
+ rotate: 0deg;
519
+ }
520
+ }
521
+
522
+ @keyframes dart {
523
+ 20% {
524
+ translate: 6% -3%;
525
+ }
526
+
527
+ 40%,
528
+ 80% {
529
+ translate: 0 0;
530
+ }
531
+
532
+ 60% {
533
+ translate: -6% -3%;
534
+ }
535
+ }
536
+
537
+ @keyframes draw-glow {
538
+ 40%,
539
+ 60% {
540
+ /* A ring in the skin's colour, much darker than its squares, to show on the light and the dark ones */
541
+ box-shadow: inset 0 0 0 0.6cqw color-mix(in oklab, var(--dark-square) 50%, var(--fixed-ink)), inset 0 0 2.5cqw 0.4cqw color-mix(in oklab, var(--dark-square) 40%, transparent);
542
+ }
543
+ }
544
+
545
+ @keyframes badge-in {
546
+ from {
547
+ scale: 0;
548
+ opacity: 0;
549
+ }
550
+ }
551
+
552
+ @keyframes sheen {
553
+ from {
554
+ background-position: 100% 0;
555
+ }
556
+
557
+ to {
558
+ background-position: 0 0;
559
+ }
560
+ }
561
+
562
+ @media (prefers-reduced-motion: reduce) {
563
+ .board.fresh,
564
+ .board.fresh .square,
565
+ .board.fresh .piece,
566
+ .board.fresh .badge {
567
+ animation: none;
568
+ }
569
+
570
+ .board.fresh::after {
571
+ display: none;
572
+ }
573
+ }
574
+
575
+ /* The ranks and files grow with the board, as 13 points on 800, tucked into the corners the pieces leave free */
576
+ .coordinate {
577
+ position: absolute;
578
+ font-size: clamp(9px, 1.6cqw, 13px);
579
+ font-weight: 700;
580
+ line-height: 1;
581
+ pointer-events: none;
582
+ }
583
+
584
+ .coordinate.rank {
585
+ top: 3%;
586
+ left: 4%;
587
+ }
588
+
589
+ .coordinate.file {
590
+ right: 4%;
591
+ bottom: 2.5%;
592
+ }
593
+
594
+ .square.light .coordinate,
595
+ .square.light .square-name {
596
+ color: var(--dark-square);
597
+ }
598
+
599
+ .square.dark .coordinate,
600
+ .square.dark .square-name {
601
+ color: var(--light-square);
602
+ }
603
+
604
+ /* The square's name in the corner of the file, under the pointer or the piece dragged, in its place on the bottom rank, when the player turns it on: bottom
605
+ right, where no badge sits (Stefano); the help rows' names stay in line */
606
+ .square .square-name {
607
+ position: absolute;
608
+ right: 4%;
609
+ bottom: 2.5%;
610
+ z-index: 2;
611
+ display: none;
612
+ font-size: clamp(9px, 1.6cqw, 13px);
613
+ font-weight: 700;
614
+ line-height: 1;
615
+ pointer-events: none;
616
+ }
617
+
618
+ .board.named .square.over .square-name {
619
+ display: block;
620
+ }
621
+
622
+ .board.named .square.over .coordinate.file {
623
+ visibility: hidden;
624
+ }
625
+
626
+ @media (hover: hover) {
627
+ .board.named .square:hover .square-name {
628
+ display: block;
629
+ }
630
+
631
+ .board.named .square:hover .coordinate.file {
632
+ visibility: hidden;
633
+ }
634
+ }
635
+
636
+ .square .piece {
637
+ position: absolute;
638
+ inset: 0;
639
+ width: 100%;
640
+ height: 100%;
641
+ }
642
+
643
+ /* The pieces: one shape each, in the colour of its side with a black edge (D-49) */
644
+ .piece {
645
+ --piece-edge-width: 6.5px;
646
+ }
647
+
648
+ /* How much of its colours a piece keeps against the page: all of them, but on a board that is off (D-77). Opaque, so no square shows through it */
649
+ @property --kept {
650
+ syntax: "<percentage>";
651
+ inherits: true;
652
+ initial-value: 100%;
653
+ }
654
+
655
+ .piece.white {
656
+ --piece-fill: color-mix(in oklab, var(--white-piece) var(--kept), var(--page));
657
+ --piece-edge: color-mix(in oklab, #000 var(--kept), var(--page));
658
+ }
659
+
660
+ .piece.black {
661
+ --piece-fill: color-mix(in oklab, var(--black-piece) var(--kept), var(--page));
662
+ --piece-edge: color-mix(in oklab, #000 var(--kept), var(--page));
663
+ --piece-detail: color-mix(in oklab, var(--white-piece) var(--kept), var(--page));
664
+ }
665
+
666
+ /* Why a move was refused: a bubble over the square it went to, its tail on the square; a ring on the piece that forbids it */
667
+ .notice {
668
+ --shift: -50%;
669
+ position: absolute;
670
+ z-index: 3;
671
+ left: calc((var(--column) + 0.5) * 12.5cqw);
672
+ top: calc(var(--row) * 12.5cqw);
673
+ transform: translate(var(--shift), calc(-100% - 8px));
674
+ display: flex;
675
+ align-items: center;
676
+ gap: 9px;
677
+ width: max-content;
678
+ max-width: min(78cqw, 340px);
679
+ margin: 0;
680
+ padding: 8px 12px;
681
+ border-radius: 7px;
682
+ /* Ink in both themes, as the arrows that explain */
683
+ background: var(--explain-ink);
684
+ color: #f4f6f9;
685
+ font-size: 13.5px;
686
+ line-height: 1.35;
687
+ box-shadow: var(--shadow);
688
+ pointer-events: none;
689
+ animation: notice-in 160ms ease-out;
690
+ }
691
+
692
+ .notice::after {
693
+ content: "";
694
+ position: absolute;
695
+ left: calc(-1 * var(--shift) - 6px);
696
+ top: 100%;
697
+ border: 6px solid transparent;
698
+ border-top-color: var(--explain-ink);
699
+ }
700
+
701
+ /* The kind of reason: a light tile with the piece, a pin, a bar, castling; the player's king on red */
702
+ .explain-icon {
703
+ flex: none;
704
+ display: grid;
705
+ place-items: center;
706
+ width: 28px;
707
+ height: 28px;
708
+ padding: 2px;
709
+ border-radius: 6px;
710
+ background: #e3e9f0;
711
+ }
712
+
713
+ .explain-icon.king {
714
+ background: #f3c4c4;
715
+ }
716
+
717
+ .explain-icon .piece {
718
+ width: 100%;
719
+ height: 100%;
720
+ }
721
+
722
+ .explain-icon svg {
723
+ width: 20px;
724
+ height: 20px;
725
+ fill: none;
726
+ stroke: var(--explain-ink);
727
+ stroke-width: 2.2;
728
+ stroke-linecap: round;
729
+ stroke-linejoin: round;
730
+ }
731
+
732
+ .explain-icon .bar {
733
+ stroke-width: 3;
734
+ }
735
+
736
+ /* The arrow that explains a refused move: ink, apart from the player's orange ones */
737
+ .arrows.explain {
738
+ opacity: 0.78;
739
+ }
740
+
741
+ /* An explaining arrow starts off the culprit's piece, and its cap would carry it back onto it */
742
+ .arrows.explain path {
743
+ stroke: var(--explain-ink);
744
+ stroke-linecap: butt;
745
+ }
746
+
747
+ .arrows.explain polygon {
748
+ fill: var(--explain-ink);
749
+ }
750
+
751
+ .notice.start {
752
+ --shift: 0%;
753
+ left: calc(var(--column) * 12.5cqw + 4px);
754
+ }
755
+
756
+ .notice.start::after {
757
+ left: calc(6.25cqw - 10px);
758
+ }
759
+
760
+ .notice.end {
761
+ --shift: -100%;
762
+ left: calc((var(--column) + 1) * 12.5cqw - 4px);
763
+ }
764
+
765
+ .notice.end::after {
766
+ left: auto;
767
+ right: calc(6.25cqw - 10px);
768
+ }
769
+
770
+ .notice.below {
771
+ top: calc((var(--row) + 1) * 12.5cqw);
772
+ transform: translate(var(--shift), 8px);
773
+ }
774
+
775
+ /* Where the bubble hides neither the culprit nor the arrow's point (Zoe): under the whole board on a phone, beside the piece on a wide board */
776
+ .notice.under-board {
777
+ top: 100cqw;
778
+ transform: translate(var(--shift), 8px);
779
+ }
780
+
781
+ .notice.below::after,
782
+ .notice.under-board::after {
783
+ top: auto;
784
+ bottom: 100%;
785
+ border-top-color: transparent;
786
+ border-bottom-color: var(--explain-ink);
787
+ }
788
+
789
+ .notice.right,
790
+ .notice.left {
791
+ top: calc((var(--row) + 0.5) * 12.5cqw);
792
+ max-width: min(calc((var(--room) * 12.5cqw) - 12px), 340px);
793
+ }
794
+
795
+ .notice.right {
796
+ --room: calc(7 - var(--column));
797
+ left: calc((var(--column) + 1) * 12.5cqw + 8px);
798
+ transform: translateY(-50%);
799
+ }
800
+
801
+ .notice.left {
802
+ --room: var(--column);
803
+ left: calc(var(--column) * 12.5cqw - 8px);
804
+ transform: translate(-100%, -50%);
805
+ }
806
+
807
+ .notice.right::after,
808
+ .notice.left::after {
809
+ top: calc(50% - 6px);
810
+ }
811
+
812
+ .notice.right::after {
813
+ left: auto;
814
+ right: 100%;
815
+ border-top-color: transparent;
816
+ border-right-color: var(--explain-ink);
817
+ }
818
+
819
+ .notice.left::after {
820
+ left: 100%;
821
+ right: auto;
822
+ border-top-color: transparent;
823
+ border-left-color: var(--explain-ink);
824
+ }
825
+
826
+ /* On a phone's board the bubble takes its width, in two lines, its tail on the piece's column. The width is board/board.tsx's narrowBoard too */
827
+ @container (max-width: 519px) {
828
+ .board-wrap .notice:not(.centre) {
829
+ left: 4px;
830
+ width: calc(100cqw - 8px);
831
+ max-width: none;
832
+ --shift: 0%;
833
+ }
834
+
835
+ .board-wrap .notice:not(.centre)::after {
836
+ left: calc((var(--column) + 0.5) * 12.5cqw - 10px);
837
+ right: auto;
838
+ }
839
+ }
840
+
841
+ .notice.centre {
842
+ left: 50%;
843
+ top: 50%;
844
+ transform: translate(-50%, -50%);
845
+ }
846
+
847
+ .notice.centre::after {
848
+ content: none;
849
+ }
850
+
851
+ @keyframes notice-in {
852
+ from {
853
+ opacity: 0;
854
+ }
855
+ }
856
+
857
+ /* The choice of the piece a pawn becomes: four round buttons in a column from its square, over a shade */
858
+ .promotion {
859
+ position: absolute;
860
+ left: 0;
861
+ top: 0;
862
+ z-index: 4;
863
+ width: 100%;
864
+ aspect-ratio: 1;
865
+ }
866
+
867
+ .promotion-shade {
868
+ position: absolute;
869
+ inset: 0;
870
+ border-radius: var(--radius);
871
+ background: rgba(0, 0, 0, 0.35);
872
+ }
873
+
874
+ .promotion-piece {
875
+ position: absolute;
876
+ left: calc(var(--column) * 12.5cqw);
877
+ top: calc(var(--row) * 12.5cqw);
878
+ width: 12.5cqw;
879
+ height: 12.5cqw;
880
+ padding: 0.6cqw;
881
+ border: 0;
882
+ border-radius: 50%;
883
+ background: var(--light-square);
884
+ box-shadow: var(--shadow);
885
+ cursor: pointer;
886
+ }
887
+
888
+ .promotion-piece:hover {
889
+ box-shadow: var(--shadow), inset 0 0 0 max(2px, 0.625cqw) var(--hover-edge);
890
+ }
891
+
892
+ /* White inside dark reads on every square and every theme. */
893
+ .promotion-piece:focus-visible,
894
+ .square:focus-visible {
895
+ outline: none;
896
+ box-shadow: inset 0 0 0 2px #fff, inset 0 0 0 4.5px var(--fixed-ink);
897
+ }
898
+
899
+ .promotion-piece .piece {
900
+ display: block;
901
+ width: 100%;
902
+ height: 100%;
903
+ }
904
+
905
+ .square.culprit {
906
+ box-shadow: inset 0 0 0 max(2.5px, 0.5cqw) var(--explain-ink), inset 0 0 0 calc(max(2.5px, 0.5cqw) + 1.5px) rgba(255, 255, 255, 0.85);
907
+ }
908
+
909
+ /* Between games the board goes off (D-77): the squares sink toward the page and the pieces fade, the table set and nobody playing. A game the page saw end
910
+ keeps its final position less off, once its effects are over, with the kings' badges and the last move in full. A thin line keeps its edge, which the
911
+ light squares would lose in the page */
912
+ .board-wrap.off > .board {
913
+ box-shadow: 0 0 0 1px var(--line);
914
+ }
915
+
916
+ .board-wrap.off .square.light {
917
+ background-color: color-mix(in oklab, var(--light-square) 40%, var(--page));
918
+ }
919
+
920
+ .board-wrap.off .square.dark {
921
+ background-color: color-mix(in oklab, var(--dark-square) 40%, var(--page));
922
+ }
923
+
924
+ /* The pieces fade toward the page and never turn see-through, so each side keeps one colour on every square */
925
+ .board-wrap.off .piece {
926
+ --kept: 45%;
927
+ }
928
+
929
+ .board-wrap.off .coordinate {
930
+ opacity: 0.4;
931
+ }
932
+
933
+ .board-wrap.off > .ended .square.light {
934
+ background-color: color-mix(in oklab, var(--light-square) 62%, var(--page));
935
+ }
936
+
937
+ .board-wrap.off > .ended .square.dark {
938
+ background-color: color-mix(in oklab, var(--dark-square) 62%, var(--page));
939
+ }
940
+
941
+ .board-wrap.off > .ended .piece {
942
+ --kept: 75%;
943
+ }
944
+
945
+ .board-wrap.off > .ended .badge .piece {
946
+ --kept: 100%;
947
+ }
948
+
949
+ /* It goes off gently, and comes back at once with the next game */
950
+ @media (prefers-reduced-motion: no-preference) {
951
+ .board-wrap.off .square {
952
+ transition: background-color 400ms ease;
953
+ }
954
+
955
+ .board-wrap.off .piece,
956
+ .board-wrap.off .coordinate {
957
+ transition: opacity 400ms ease, --kept 400ms ease;
958
+ }
959
+ }