anvilsign in

collin/mahjong

1@import url('https://fonts.googleapis.com/css2?family=Noto+Sans+TC:wght@400;600;900&display=swap');
2
3:root {
4 /* Depth of each player's strip. `--h-len` is floored so a window too narrow
5 to hold two strips shrinks the tiles rather than going negative. */
6 --depth: clamp(232px, 30vh, 330px);
7 --h-len: max(160px, calc(100vw - 2 * var(--depth)));
8 --v-len: 100vh;
9 /* Height the action buttons occupy, held whether or not any are showing.
10 The strip is built from its edge inwards, so a bar that grew when 打出
11 appeared would shove the melds and discards along with it — the reservation
12 is what keeps everything above it still. Sized for the tallest button: the
13 ones that name a tile (打出 五萬, 吃 三萬 四萬) carry a third line. */
14 --bar-h: 52px;
15 /* Largest tile a strip can hold. Edge inwards it has to fit one row of
16 discards (0.88 tiles tall), the melds (0.86), the button bar, the hand
17 (1.71 + 6px) and the nameplate (28px), plus its own padding, gaps and
18 margins — so 3.45 tiles plus 124px.
19
20 Tile size otherwise follows the strip's *length*, which is why a window
21 with less height than F11 kept full-size tiles and left the discards a
22 sliver of a row to be cut off in. */
23 --tile-fit: calc((var(--depth) - 124px) / 3.45);
24
25 --felt: #0f4534;
26 --felt-2: #0a3126;
27 --ivory: #f6f0e2;
28 --ivory-edge: #d9cfb6;
29 --ink: #21262b;
30 --gold: #d9b45c;
31 --red: #b3261e;
32 --green: #1c7a4b;
33 --blue: #1d5aa8;
34}
35
36* {
37 box-sizing: border-box;
38 -webkit-tap-highlight-color: transparent;
39}
40
41html,
42body,
43#root {
44 height: 100%;
45 margin: 0;
46 overflow: hidden;
47 overscroll-behavior: none;
48 touch-action: manipulation;
49 user-select: none;
50}
51
52/* Phone browsers hand out a `100%`/`100vh` viewport that includes the address
53 bar they are about to slide away over the top of it; `dvh` is the height the
54 table can actually have. */
55@supports (height: 100dvh) {
56 html,
57 body,
58 #root {
59 height: 100dvh;
60 }
61}
62
63body {
64 font-family: 'Noto Sans TC', system-ui, sans-serif;
65 color: var(--ivory);
66 background: var(--felt-2);
67}
68
69/* ---------- lobby ---------- */
70.lobby {
71 height: 100%;
72 display: flex;
73 flex-direction: column;
74 align-items: center;
75 justify-content: center;
76 gap: 0.5rem;
77 background: radial-gradient(ellipse at center, var(--felt) 0%, var(--felt-2) 75%);
78}
79.lobby h1 {
80 font-size: clamp(3rem, 9vw, 7rem);
81 font-weight: 900;
82 letter-spacing: 0.1em;
83 margin: 0 0 0.5rem;
84 color: var(--gold);
85}
86.lobby p {
87 margin: 0;
88 opacity: 0.75;
89 letter-spacing: 0.15em;
90}
91.lobby .hint {
92 opacity: 0.45;
93 font-size: 0.9rem;
94 margin-bottom: 1.5rem;
95}
96
97/* ---------- table ---------- */
98.table {
99 position: relative;
100 height: 100%;
101 width: 100%;
102 background: radial-gradient(ellipse at center, var(--felt) 0%, var(--felt-2) 80%);
103}
104
105.slot {
106 position: absolute;
107 overflow: hidden;
108}
109.slot-bottom {
110 bottom: 0;
111 left: var(--depth);
112 right: var(--depth);
113 height: var(--depth);
114}
115.slot-top {
116 top: 0;
117 left: var(--depth);
118 right: var(--depth);
119 height: var(--depth);
120}
121.slot-left {
122 left: 0;
123 top: 0;
124 width: var(--depth);
125 height: 100%;
126}
127.slot-right {
128 right: 0;
129 top: 0;
130 width: var(--depth);
131 height: 100%;
132}
133
134.seat {
135 position: absolute;
136 top: 50%;
137 left: 50%;
138 height: var(--depth);
139 display: flex;
140 flex-direction: column;
141 justify-content: flex-end;
142 align-items: center;
143 gap: 2px;
144 padding: 2px 8px 4px;
145 border-radius: 10px;
146 overflow: hidden;
147 transition: background 0.2s;
148}
149.slot-bottom .seat,
150.slot-top .seat {
151 width: var(--h-len);
152 --tile-w: min(calc((var(--h-len) - 40px) / 18), var(--tile-fit), 72px);
153}
154.slot-left .seat,
155.slot-right .seat {
156 width: var(--v-len);
157 --tile-w: min(calc((var(--v-len) - 40px) / 18), var(--tile-fit), 72px);
158}
159.seat.active {
160 background: linear-gradient(to top, rgba(217, 180, 92, 0.22), transparent 70%);
161}
162.seat.claiming {
163 background: linear-gradient(to top, rgba(179, 38, 30, 0.25), transparent 70%);
164 animation: pulse 1.1s ease-in-out infinite;
165}
166@keyframes pulse {
167 50% {
168 background: linear-gradient(to top, rgba(179, 38, 30, 0.45), transparent 70%);
169 }
170}
171
172/* ---------- tiles ---------- */
173/* Faces come from a 43x2 sprite sheet; the second row is the highlighted
174 variant, which we use for the tile a player has lifted to discard. */
175.tile {
176 position: relative;
177 flex: none;
178 --tw: var(--tile-w);
179 width: var(--tw);
180 height: calc(var(--tw) * 1.375);
181 border-radius: calc(var(--tw) * 0.1);
182 transition: transform 0.12s ease, filter 0.12s ease;
183}
184.tile.t-meld {
185 --tw: calc(var(--tile-w) * 0.62);
186}
187.tile.t-small {
188 --tw: calc(var(--tile-w) * 0.64);
189}
190.tile.face {
191 background-image: url('/tiles/tiles.svg');
192 background-repeat: no-repeat;
193 background-size: calc(var(--tw) * 43) calc(var(--tw) * 2.75);
194 background-position: calc(var(--col) * var(--tw) * -1) 0;
195 filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.45));
196}
197.tile.back {
198 background-image: url('/tiles/back.png');
199 background-size: 100% 100%;
200 filter: drop-shadow(0 2px 3px rgba(0, 0, 0, 0.45));
201}
202.tile.tappable {
203 cursor: pointer;
204}
205.tile.raised {
206 transform: translateY(calc(var(--tw) * -0.28));
207 filter: drop-shadow(0 10px 12px rgba(0, 0, 0, 0.55));
208}
209/* Lifted tiles switch to the sheet's highlighted row. */
210.tile.face.raised {
211 background-position: calc(var(--col) * var(--tw) * -1) calc(var(--tw) * -1.375);
212}
213.tile.hl {
214 outline: 2px solid var(--gold);
215 outline-offset: 1px;
216 border-radius: calc(var(--tw) * 0.1);
217}
218.tile.dim {
219 opacity: 0.45;
220}
221
222/* ---------- seat sections ---------- */
223.hand {
224 display: flex;
225 gap: 2px;
226 /* the extra top room is where the 摸 tag sits */
227 height: calc(var(--tile-w) * 1.71 + 6px);
228 padding-top: calc(var(--tile-w) * 0.34);
229 align-items: flex-end;
230 flex: none;
231}
232/* Every hand tile sits in a draggable cell. */
233.hand-cell {
234 position: relative;
235 display: flex;
236 align-items: flex-end;
237 touch-action: none;
238 cursor: grab;
239}
240.hand-cell.dragging {
241 z-index: 5;
242 cursor: grabbing;
243}
244.hand-cell.dragging .tile {
245 transform: translateY(calc(var(--tile-w) * -0.2)) scale(1.06);
246 filter: drop-shadow(0 14px 18px rgba(0, 0, 0, 0.65));
247}
248.drawn-slot {
249 margin-left: calc(var(--tile-w) * 0.45);
250}
251.drawn-slot .tile {
252 outline: 2px solid var(--gold);
253 outline-offset: 1px;
254 border-radius: calc(var(--tw) * 0.1);
255}
256.drawn-tag {
257 position: absolute;
258 top: calc(var(--tile-w) * -0.3);
259 left: 0;
260 right: 0;
261 text-align: center;
262 font-size: calc(var(--tile-w) * 0.24);
263 font-weight: 600;
264 letter-spacing: 0.1em;
265 color: var(--gold);
266 white-space: nowrap;
267 pointer-events: none;
268}
269
270.melds {
271 display: flex;
272 gap: 10px;
273 flex-wrap: wrap;
274 justify-content: center;
275 min-height: calc(var(--tile-w) * 0.86);
276 flex: none;
277}
278.meld {
279 display: flex;
280 gap: 1px;
281}
282.meld.flowers {
283 margin-left: 12px;
284 padding-left: 12px;
285 border-left: 1px dashed rgba(246, 240, 226, 0.3);
286}
287.discards {
288 display: flex;
289 flex-wrap: wrap;
290 gap: 2px;
291 justify-content: center;
292 align-content: flex-start;
293 max-width: 94%;
294 min-height: 0;
295 margin-bottom: 2px;
296 flex: 1 1 0;
297 overflow-y: auto;
298 scrollbar-width: none;
299}
300
301.nameplate {
302 display: flex;
303 align-items: center;
304 gap: 8px;
305 font-size: 13px;
306 letter-spacing: 0.08em;
307 opacity: 0.9;
308 padding-top: 2px;
309 flex: none;
310}
311.nameplate .wind {
312 display: flex;
313 flex-direction: column;
314 align-items: center;
315 line-height: 1;
316 font-size: 18px;
317 font-weight: 900;
318 color: var(--gold);
319}
320.nameplate .wind em {
321 font-style: normal;
322 font-size: 8px;
323 font-weight: 600;
324 letter-spacing: 0.1em;
325 text-transform: uppercase;
326 opacity: 0.6;
327}
328.nameplate .wind.dealer {
329 color: #ff7a68;
330}
331.nameplate .tag {
332 font-size: 11px;
333 padding: 1px 6px;
334 border-radius: 999px;
335 background: rgba(217, 180, 92, 0.2);
336 border: 1px solid rgba(217, 180, 92, 0.5);
337}
338.nameplate .tag.ting {
339 background: rgba(46, 170, 120, 0.2);
340 border-color: rgba(46, 170, 120, 0.6);
341}
342.nameplate .tag.sacred {
343 background: rgba(179, 38, 30, 0.22);
344 border-color: rgba(224, 108, 96, 0.6);
345 color: #ffc9c2;
346}
347.nameplate .chips {
348 margin-left: 4px;
349 font-weight: 900;
350 font-size: 16px;
351 color: var(--gold);
352}
353
354.actionbar {
355 display: flex;
356 gap: 8px;
357 align-items: center;
358 justify-content: flex-end;
359 padding-right: calc(var(--tile-w) * 0.4);
360 align-self: stretch;
361 /* Reserved even when empty — see --bar-h. */
362 min-height: var(--bar-h);
363 flex-wrap: wrap;
364 flex: none;
365}
366/* Every button on the bar is the same height, so the row is the same height
367 whichever ones are up: without this a 打出 五萬 stands taller than a bare 過
368 and the strip shuffles itself every time you lift a tile. */
369.actionbar .btn {
370 height: var(--bar-h);
371 min-height: 0;
372 padding-top: 4px;
373 padding-bottom: 4px;
374}
375.waiting {
376 font-size: 12px;
377 opacity: 0.5;
378}
379.delta {
380 font-size: 20px;
381 font-weight: 900;
382}
383.delta.up {
384 color: #5fd39a;
385}
386.delta.down {
387 color: #ff8a80;
388}
389
390/* ---------- buttons ---------- */
391.btn {
392 font-family: inherit;
393 font-size: 17px;
394 font-weight: 400;
395 color: #1a1a1a;
396 background: linear-gradient(180deg, #f3e6c4, #d9b45c);
397 border: none;
398 border-radius: 8px;
399 padding: 6px 15px;
400 min-height: 46px;
401 box-shadow: 0 2px 0 #9a7d38;
402 cursor: pointer;
403 display: inline-flex;
404 align-items: center;
405 gap: 6px;
406}
407.btn:active {
408 transform: translateY(2px);
409 box-shadow: none;
410}
411.btn-sub {
412 font-size: 12px;
413 opacity: 0.7;
414}
415
416/* Bilingual label: the called term above, English cue below. */
417.bi {
418 display: flex;
419 flex-direction: column;
420 align-items: center;
421 justify-content: center;
422 line-height: 1.05;
423 gap: 1px;
424}
425.bi .zh {
426 font-size: 19px;
427 font-weight: 400;
428 letter-spacing: 0.06em;
429}
430.bi .en {
431 font-size: 9.5px;
432 font-weight: 600;
433 letter-spacing: 0.11em;
434 text-transform: uppercase;
435 opacity: 0.68;
436 white-space: nowrap;
437}
438.bi .bi-sub {
439 font-size: 11px;
440 opacity: 0.72;
441 white-space: nowrap;
442}
443.big-btn .bi .zh {
444 font-size: 24px;
445}
446.big-btn .bi .en {
447 font-size: 11px;
448}
449.btn.claim.hu {
450 background: linear-gradient(180deg, #ff8a7a, #c0392b);
451 color: #fff;
452 box-shadow: 0 2px 0 #7c231a;
453}
454.btn.claim.kong,
455.btn.claim.pung {
456 background: linear-gradient(180deg, #8fd3ff, #2b7ec0);
457 color: #fff;
458 box-shadow: 0 2px 0 #1c527d;
459}
460.btn.claim.chow {
461 background: linear-gradient(180deg, #9be3b7, #2e9e63);
462 color: #fff;
463 box-shadow: 0 2px 0 #1d6640;
464}
465.btn.sort {
466 min-height: 20px;
467 padding: 1px 8px;
468 font-size: 11px;
469 letter-spacing: 0.06em;
470 border-radius: 999px;
471}
472.btn.proceed {
473 background: linear-gradient(180deg, #f3e6c4, #d9b45c);
474}
475.btn.pass,
476.btn.ghost {
477 background: transparent;
478 color: rgba(246, 240, 226, 0.75);
479 border: 1px solid rgba(246, 240, 226, 0.35);
480 box-shadow: none;
481}
482.btn.discard {
483 background: linear-gradient(180deg, #fff2cf, #e0c070);
484}
485.big-btn {
486 font-size: 22px;
487 padding: 12px 34px;
488 min-height: 56px;
489}
490
491/* ---------- center ---------- */
492.center {
493 position: absolute;
494 inset: var(--depth);
495 display: flex;
496 flex-direction: column;
497 align-items: center;
498 justify-content: center;
499 gap: 10px;
500 pointer-events: none;
501}
502.center > * {
503 pointer-events: auto;
504}
505/* Everything in the centre reads on top of the wall square. */
506.info,
507.last-discard,
508.prompt,
509.result,
510.info {
511 display: flex;
512 flex-direction: column;
513 align-items: center;
514 gap: 2px;
515 opacity: 0.9;
516 text-align: center;
517}
518.info .round,
519.info .wall {
520 display: flex;
521 flex-direction: column;
522 align-items: center;
523 line-height: 1.05;
524}
525.info .wall {
526 margin-top: 10px;
527 padding-top: 10px;
528 border-top: 1px solid rgba(246, 240, 226, 0.14);
529}
530.info .big {
531 font-size: 40px;
532 font-weight: 400;
533 color: var(--gold);
534}
535.info .lbl {
536 font-size: 11px;
537 letter-spacing: 0.06em;
538 opacity: 0.6;
539}
540.info .hand-no {
541 margin-top: 8px;
542 font-size: 11px;
543 opacity: 0.5;
544 letter-spacing: 0.1em;
545}
546/* The wall: a square of four staggered sides, 18 stacks of two each, drawn
547 with the tileset's own blank tile. Sits behind the centre readout. */
548.wall-ring {
549 position: absolute;
550 top: 50%;
551 left: 50%;
552 --ring: min(calc(100vh - 2 * var(--depth) - 16px), calc(100vw - 2 * var(--depth) - 16px), 560px);
553 --ws: calc(var(--ring) / 19.375);
554 --wd: calc(var(--ws) * 1.375);
555 width: var(--ring);
556 height: var(--ring);
557 transform: translate(-50%, -50%);
558 pointer-events: none;
559 z-index: 0;
560}
561.wall-side {
562 position: absolute;
563 display: flex;
564}
565/* Pinwheel: each side starts where the previous one ended. */
566.wall-side.top {
567 top: 0;
568 left: var(--wd);
569 right: 0;
570 height: var(--wd);
571}
572.wall-side.right {
573 top: var(--wd);
574 right: 0;
575 bottom: 0;
576 width: var(--wd);
577 flex-direction: column;
578}
579.wall-side.bottom {
580 bottom: 0;
581 left: 0;
582 right: var(--wd);
583 height: var(--wd);
584 flex-direction: row-reverse;
585}
586.wall-side.left {
587 top: 0;
588 left: 0;
589 bottom: var(--wd);
590 width: var(--wd);
591 flex-direction: column-reverse;
592}
593.wall-stack {
594 position: relative;
595 flex: none;
596 width: var(--ws);
597 height: var(--wd);
598}
599.wall-side.left .wall-stack,
600.wall-side.right .wall-stack {
601 width: var(--wd);
602 height: var(--ws);
603}
604.wall-stack i {
605 position: absolute;
606 top: 50%;
607 left: 50%;
608 width: var(--ws);
609 height: var(--wd);
610 margin: calc(var(--wd) / -2) 0 0 calc(var(--ws) / -2);
611 background-image: url('/tiles/back.png');
612 background-size: 100% 100%;
613 filter: drop-shadow(0 1px 1px rgba(0, 0, 0, 0.45));
614}
615.wall-side.left .wall-stack i,
616.wall-side.right .wall-stack i {
617 transform: rotate(90deg);
618}
619/* One tile left in the stack. */
620.wall-stack.s1 i {
621 opacity: 0.8;
622 transform: scale(0.86);
623}
624.wall-side.left .wall-stack.s1 i,
625.wall-side.right .wall-stack.s1 i {
626 transform: rotate(90deg) scale(0.86);
627}
628/* Spent stacks stay as ghosts so the square keeps its shape. */
629.wall-stack.s0 i {
630 background: none;
631 filter: none;
632 border: 1px dashed rgba(246, 240, 226, 0.11);
633 border-radius: 2px;
634}
635.wall-stack.dead i {
636 opacity: 0.4;
637 filter: grayscale(0.6);
638}
639.wall-stack.next::after {
640 content: '';
641 position: absolute;
642 inset: -2px;
643 border: 2px solid var(--gold);
644 border-radius: 3px;
645 box-shadow: 0 0 12px rgba(217, 180, 92, 0.7);
646}
647
648.facing {
649 position: relative;
650 z-index: 1;
651 display: flex;
652 flex-direction: column;
653 align-items: center;
654 gap: 8px;
655 transition: transform 0.35s ease;
656}
657
658.last-discard {
659 display: flex;
660 flex-direction: column;
661 align-items: center;
662 gap: 6px;
663 --tile-w: 64px;
664}
665.ld-label {
666 font-size: 12px;
667 opacity: 0.6;
668}
669.prompt {
670 font-size: 15px;
671 letter-spacing: 0.1em;
672 opacity: 0.8;
673}
674.prompt.warn {
675 color: #ffb4a2;
676 font-weight: 600;
677}
678.result {
679 background: rgba(10, 30, 24, 0.94);
680 border: 1px solid rgba(217, 180, 92, 0.45);
681 border-radius: 14px;
682 padding: 18px 26px;
683 display: flex;
684 flex-direction: column;
685 align-items: center;
686 gap: 10px;
687 max-height: 90%;
688 overflow: auto;
689}
690.result h2 {
691 margin: 0;
692 font-size: 24px;
693 color: var(--gold);
694}
695.result .wt {
696 color: var(--ivory);
697 opacity: 0.8;
698}
699.tai-list {
700 display: grid;
701 grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
702 gap: 2px 18px;
703 font-size: 14px;
704 width: 100%;
705}
706.tai-item {
707 display: flex;
708 align-items: baseline;
709 justify-content: space-between;
710 gap: 12px;
711 padding: 2px 0;
712 border-bottom: 1px dotted rgba(246, 240, 226, 0.2);
713}
714.tai-name {
715 display: flex;
716 flex-direction: column;
717 line-height: 1.15;
718}
719.tai-name em {
720 font-style: normal;
721 font-size: 10px;
722 letter-spacing: 0.08em;
723 text-transform: uppercase;
724 opacity: 0.55;
725}
726.tai-val {
727 font-weight: 700;
728 color: var(--gold);
729}
730/* 胡牌 — the arrow that lands on the winner's edge of the table. Sits over the
731 strip rather than in it, so the hand underneath keeps the height it had and
732 nothing shifts when it appears. */
733.win-arrow {
734 position: absolute;
735 top: 0;
736 left: 50%;
737 font-size: 52px;
738 line-height: 0.9;
739 color: var(--gold);
740 text-shadow: 0 0 6px #0a3126, 0 0 20px rgba(217, 180, 92, 0.9);
741 pointer-events: none;
742 z-index: 4;
743 animation: arrow-nudge 0.9s ease-in-out infinite;
744}
745@keyframes arrow-nudge {
746 0%,
747 100% {
748 transform: translate(-50%, 0);
749 }
750 50% {
751 transform: translate(-50%, 9px);
752 }
753}
754
755/* 流局 — who was there and what they were sitting on. */
756.ready-list {
757 display: flex;
758 flex-direction: column;
759 gap: 6px;
760 margin-top: 2px;
761}
762.ready-row {
763 display: flex;
764 align-items: center;
765 gap: 8px;
766 font-size: 14px;
767}
768.ready-row.none {
769 opacity: 0.45;
770}
771.ready-name {
772 min-width: 6.5em;
773 text-align: right;
774}
775.ready-row .tag.ting {
776 font-size: 11px;
777 padding: 1px 6px;
778 border-radius: 999px;
779 background: rgba(46, 170, 120, 0.2);
780 border: 1px solid rgba(46, 170, 120, 0.6);
781 white-space: nowrap;
782}
783.ready-tiles {
784 display: flex;
785 flex-wrap: wrap;
786 gap: 2px;
787 /* Tiles size themselves off their strip's --tile-w, and the centre is not in
788 one — without a size of its own here they come out 0 x 0 and vanish. */
789 --tile-w: 46px;
790}
791.ready-no {
792 font-size: 12px;
793 letter-spacing: 0.04em;
794}
795
796.deltas {
797 display: flex;
798 gap: 16px;
799 font-size: 14px;
800 opacity: 0.9;
801}
802.deltas .up {
803 color: #5fd39a;
804}
805.deltas .down {
806 color: #ff8a80;
807}
808
809/* ---------- help panel ---------- */
810.helpbtn {
811 margin-left: 2px;
812 width: 22px;
813 height: 22px;
814 border-radius: 50%;
815 border: 1px solid rgba(246, 240, 226, 0.35);
816 background: transparent;
817 color: rgba(246, 240, 226, 0.75);
818 font-family: inherit;
819 font-size: 12px;
820 line-height: 1;
821 cursor: pointer;
822}
823/* The rules panel gets its own deeper slot so it is comfortable to read. */
824.help-slot {
825 position: absolute;
826 inset: 0;
827 z-index: 30;
828 background: rgba(4, 18, 14, 0.55);
829}
830.help-slot.slot-bottom,
831.help-slot.slot-top,
832.help-slot.slot-left,
833.help-slot.slot-right {
834 inset: 0;
835 width: auto;
836 height: auto;
837}
838.help {
839 position: absolute;
840 top: 50%;
841 left: 50%;
842 height: var(--hd);
843 z-index: 31;
844 display: flex;
845 flex-direction: column;
846 background: rgba(8, 32, 25, 0.985);
847 border: 1px solid rgba(217, 180, 92, 0.4);
848 border-radius: 12px;
849 padding: 10px 18px 14px;
850 box-shadow: 0 18px 50px rgba(0, 0, 0, 0.6);
851}
852/* Anchored to the asking player's own edge, and rotated to face them. The
853 leading translate is applied last, so it shifts the box in screen space. */
854.help-slot.slot-bottom .help,
855.help-slot.slot-top .help {
856 width: calc(100vw - 60px);
857 --hd: min(52vh, 560px);
858 --off: calc(50vh - var(--hd) / 2 - 14px);
859}
860.help-slot.slot-left .help,
861.help-slot.slot-right .help {
862 width: calc(100vh - 60px);
863 --hd: min(52vw, 560px);
864 --off: calc(50vw - var(--hd) / 2 - 14px);
865}
866.help-slot.slot-bottom .help {
867 translate: 0 var(--off);
868}
869.help-slot.slot-top .help {
870 translate: 0 calc(var(--off) * -1);
871}
872.help-slot.slot-left .help {
873 translate: calc(var(--off) * -1) 0;
874}
875.help-slot.slot-right .help {
876 translate: var(--off) 0;
877}
878.help-head {
879 display: flex;
880 align-items: center;
881 justify-content: space-between;
882 gap: 16px;
883 flex: none;
884}
885.help-head h3 {
886 margin: 0;
887 font-size: 15px;
888 font-weight: 400;
889 letter-spacing: 0.08em;
890 color: var(--gold);
891}
892.help-head .btn {
893 min-height: 30px;
894 padding: 2px 12px;
895 font-size: 13px;
896}
897.help-body {
898 flex: 1 1 0;
899 min-height: 0;
900 overflow-y: auto;
901 columns: 4;
902 column-gap: 26px;
903 column-fill: auto;
904 padding-top: 8px;
905 overflow-x: auto;
906 scrollbar-width: thin;
907}
908.help-body section {
909 break-inside: avoid;
910 margin-bottom: 10px;
911}
912.help-body h4 {
913 margin: 0 0 2px;
914 font-size: 13px;
915 font-weight: 400;
916 color: var(--gold);
917 letter-spacing: 0.06em;
918}
919.help-body h4 em {
920 font-style: normal;
921 font-size: 9px;
922 letter-spacing: 0.12em;
923 text-transform: uppercase;
924 opacity: 0.55;
925 margin-left: 4px;
926}
927.help-body p {
928 margin: 0 0 4px;
929 display: flex;
930 flex-direction: column;
931 line-height: 1.35;
932 font-size: 11.5px;
933}
934.help-body p em {
935 font-style: normal;
936 font-size: 10.5px;
937 opacity: 0.6;
938}
939.help-body .credit {
940 opacity: 0.6;
941}
942
943/* ---------- lobby resume ---------- */
944.lobby .sub {
945 font-size: 0.95rem;
946}
947.resume {
948 margin-top: 1.4rem;
949 padding: 12px 26px;
950 border: 1px solid rgba(217, 180, 92, 0.35);
951 border-radius: 12px;
952 background: rgba(0, 0, 0, 0.2);
953 text-align: center;
954 display: flex;
955 flex-direction: column;
956 gap: 6px;
957}
958.resume-line {
959 font-size: 14px;
960 color: var(--gold);
961 letter-spacing: 0.06em;
962}
963.resume-scores {
964 display: flex;
965 gap: 18px;
966 justify-content: center;
967 font-size: 13px;
968 opacity: 0.8;
969}
970.resume-scores b {
971 color: var(--gold);
972 font-weight: 600;
973}
974.resume-when {
975 font-size: 11px;
976 opacity: 0.45;
977 letter-spacing: 0.08em;
978}
979.lobby-buttons {
980 display: flex;
981 flex-direction: column;
982 gap: 12px;
983 margin-top: 1.6rem;
984 align-items: stretch;
985}
986/* The two ways to play carry a line of explanation under the name. */
987.lobby-buttons .big-btn {
988 display: flex;
989 flex-direction: column;
990 gap: 3px;
991 min-width: 360px;
992 padding: 12px 28px;
993}
994.lobby-buttons .big-btn em {
995 font-style: normal;
996 font-size: 12px;
997 font-weight: 400;
998 letter-spacing: 0.03em;
999 opacity: 0.72;
1000}
1001
1002/* ---------- sound ---------- */
1003.lobby-extras {
1004 margin-top: 1.4rem;
1005 display: flex;
1006 gap: 12px;
1007 align-items: center;
1008}
1009/* Small rounded button for the things that sit under the main choices. */
1010.btn.pill {
1011 min-height: 34px;
1012 padding: 2px 14px;
1013 font-size: 13px;
1014 letter-spacing: 0.06em;
1015 border-radius: 999px;
1016 gap: 8px;
1017}
1018.sound-toggle.on {
1019 border-color: rgba(217, 180, 92, 0.7);
1020 color: var(--gold);
1021}
1022.sound-toggle.compact {
1023 min-height: 28px;
1024 padding: 2px 9px;
1025 font-size: 15px;
1026}
1027.sound-toggle .spk {
1028 font-size: 15px;
1029 line-height: 1;
1030}
1031
1032/* ---------- shared table controls ---------- */
1033/* Bottom-right of the centre square, opposite the log. Unrotated on purpose:
1034 it belongs to the table, not to a seat. */
1035.table-controls {
1036 position: absolute;
1037 right: 10px;
1038 bottom: 8px;
1039 display: flex;
1040 align-items: center;
1041 gap: 8px;
1042}
1043.btn.undo {
1044 min-height: 34px;
1045 padding: 3px 14px;
1046 border-radius: 999px;
1047 gap: 9px;
1048 max-width: 40vh;
1049}
1050.undo-arrow {
1051 font-size: 17px;
1052 line-height: 1;
1053 color: var(--gold);
1054}
1055.undo-what {
1056 display: flex;
1057 flex-direction: column;
1058 align-items: flex-start;
1059 line-height: 1.15;
1060 min-width: 0;
1061}
1062.undo-what .zh {
1063 font-size: 13px;
1064 letter-spacing: 0.05em;
1065}
1066.undo-what .en {
1067 font-size: 9px;
1068 letter-spacing: 0.09em;
1069 text-transform: uppercase;
1070 opacity: 0.55;
1071 white-space: nowrap;
1072 overflow: hidden;
1073 text-overflow: ellipsis;
1074 max-width: 100%;
1075}
1076
1077/* ---------- result: one block per winner ---------- */
1078.result .win {
1079 display: flex;
1080 flex-direction: column;
1081 align-items: center;
1082 gap: 8px;
1083 width: 100%;
1084}
1085.result .win + .win {
1086 padding-top: 10px;
1087 border-top: 1px solid rgba(217, 180, 92, 0.3);
1088}
1089.result .liable {
1090 font-size: 12px;
1091 letter-spacing: 0.08em;
1092 color: #ffb4a2;
1093}
1094.result .multi {
1095 font-size: 12px;
1096 letter-spacing: 0.1em;
1097 color: var(--gold);
1098 opacity: 0.85;
1099}
1100
1101/* ---------- settings ---------- */
1102.over-buttons {
1103 display: flex;
1104 gap: 12px;
1105 align-items: center;
1106}
1107.settings-scrim {
1108 position: absolute;
1109 inset: 0;
1110 z-index: 40;
1111 background: rgba(4, 18, 14, 0.72);
1112 display: flex;
1113 align-items: center;
1114 justify-content: center;
1115 padding: 20px;
1116}
1117.settings {
1118 width: min(1180px, 100%);
1119 max-height: 100%;
1120 display: flex;
1121 flex-direction: column;
1122 background: rgba(8, 32, 25, 0.99);
1123 border: 1px solid rgba(217, 180, 92, 0.4);
1124 border-radius: 14px;
1125 box-shadow: 0 18px 50px rgba(0, 0, 0, 0.6);
1126 padding: 16px 28px 22px;
1127}
1128.settings-head {
1129 display: flex;
1130 align-items: center;
1131 justify-content: space-between;
1132 gap: 16px;
1133 flex: none;
1134 padding-bottom: 12px;
1135 border-bottom: 1px solid rgba(246, 240, 226, 0.12);
1136}
1137.settings-head h3 {
1138 margin: 0;
1139 font-size: 22px;
1140 font-weight: 400;
1141 letter-spacing: 0.08em;
1142 color: var(--gold);
1143}
1144.settings-head-btns {
1145 display: flex;
1146 gap: 10px;
1147}
1148.settings-head .btn {
1149 min-height: 46px;
1150 padding: 2px 22px;
1151 font-size: 17px;
1152}
1153.settings-body {
1154 /* `1 1 auto`, not `1 1 0`: the panel's own height comes from this content,
1155 so a zero basis collapses the pair of them to nothing. `max-height` on the
1156 panel is what brings the scrollbar back at the other end. */
1157 flex: 1 1 auto;
1158 min-height: 0;
1159 overflow-y: auto;
1160 scrollbar-width: thin;
1161 padding-top: 14px;
1162 /* Columns rather than a grid: grid rows would size to the tallest section
1163 and leave the shorter one padded with air, which is what pushed the last
1164 house rule off the bottom. */
1165 columns: 2;
1166 column-gap: 44px;
1167}
1168.settings-body section {
1169 break-inside: avoid;
1170 margin-bottom: 22px;
1171}
1172.settings-body h4 {
1173 margin: 0 0 10px;
1174 font-size: 16px;
1175 font-weight: 400;
1176 color: var(--gold);
1177 letter-spacing: 0.06em;
1178}
1179.settings-body h4 em {
1180 font-style: normal;
1181 font-size: 11px;
1182 letter-spacing: 0.12em;
1183 text-transform: uppercase;
1184 opacity: 0.55;
1185 margin-left: 5px;
1186}
1187.settings .field {
1188 display: flex;
1189 align-items: center;
1190 gap: 14px;
1191 padding: 5px 0;
1192}
1193.settings .f-label {
1194 display: flex;
1195 align-items: baseline;
1196 gap: 6px;
1197 font-size: 16px;
1198 /* Wide enough for 起始籌碼 STARTING CHIPS, so every input in a section
1199 starts at the same place. */
1200 min-width: 196px;
1201 opacity: 0.85;
1202}
1203/* ...except the player rows, which are labelled 1–4 and need no such room. */
1204.settings .names .f-label {
1205 min-width: 26px;
1206}
1207.settings .f-label em {
1208 font-style: normal;
1209 font-size: 11px;
1210 letter-spacing: 0.1em;
1211 text-transform: uppercase;
1212 opacity: 0.55;
1213}
1214.settings input[type='text'],
1215.settings input:not([type]),
1216.settings input[type='number'] {
1217 flex: 1 1 0;
1218 min-width: 0;
1219 font-family: inherit;
1220 font-size: 18px;
1221 color: var(--ivory);
1222 background: rgba(0, 0, 0, 0.28);
1223 border: 1px solid rgba(246, 240, 226, 0.25);
1224 border-radius: 8px;
1225 padding: 11px 14px;
1226 min-height: 48px;
1227 user-select: text;
1228}
1229.settings input:focus {
1230 outline: none;
1231 border-color: rgba(217, 180, 92, 0.8);
1232}
1233.settings input[type='number'] {
1234 max-width: 150px;
1235}
1236.settings input[type='range'] {
1237 flex: 1 1 0;
1238 height: 34px;
1239 accent-color: var(--gold);
1240}
1241.settings input:disabled {
1242 opacity: 0.4;
1243}
1244.settings .switch {
1245 display: grid;
1246 grid-template-columns: auto 1fr;
1247 gap: 3px 14px;
1248 align-items: center;
1249 padding: 9px 0;
1250 cursor: pointer;
1251}
1252.settings .switch input {
1253 width: 26px;
1254 height: 26px;
1255 accent-color: var(--gold);
1256 cursor: pointer;
1257}
1258.settings .sw-name {
1259 display: flex;
1260 align-items: baseline;
1261 gap: 8px;
1262 font-size: 17px;
1263}
1264.settings .sw-name em {
1265 font-style: normal;
1266 font-size: 11px;
1267 letter-spacing: 0.1em;
1268 text-transform: uppercase;
1269 opacity: 0.55;
1270}
1271.settings .note {
1272 grid-column: 2;
1273 font-size: 13px;
1274 line-height: 1.4;
1275 opacity: 0.55;
1276}
1277.settings .switch.dead {
1278 opacity: 0.4;
1279 cursor: default;
1280}
1281.settings .note.lock {
1282 grid-column: auto;
1283 margin: 4px 0 0;
1284 color: #ffb4a2;
1285 opacity: 0.75;
1286}
1287
1288/* ============================================================================
1289 compact — a phone
1290
1291 The table layout is four strips of --depth arranged around a centre square,
1292 which wants about 700px each way; a phone has that in neither, and in
1293 landscape the top and bottom strips simply overlap. It also assumes four
1294 people sitting around a screen lying flat, which is not what a phone is.
1295
1296 Compact throws both assumptions out. Nothing is rotated — every label reads
1297 upright for the one person holding the phone — the table becomes a grid, the
1298 three other seats shrink to cards showing what is public about them, and the
1299 depth that frees up goes to the hand, which wraps when it has to.
1300
1301 The `compact` class is put on <html> by useCompact(); the breakpoint itself
1302 lives in ui/compact.ts, so it is written down once.
1303 ============================================================================ */
1304
1305.compact .table {
1306 display: grid;
1307 gap: 2px;
1308 /* Landscape is where a notch eats into the sides. */
1309 padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom)
1310 env(safe-area-inset-left);
1311 grid-template-columns: 1fr 1fr 1fr;
1312 grid-template-rows: minmax(0, 1fr) auto auto;
1313 grid-template-areas:
1314 'left across right'
1315 'bar bar bar'
1316 'mine mine mine';
1317}
1318.compact .slot {
1319 position: relative;
1320 inset: auto;
1321 width: auto;
1322 height: auto;
1323 min-width: 0;
1324 min-height: 0;
1325}
1326/* `.slot` and not the edge class alone: the rules sheet carries an edge class
1327 of its own, and would otherwise be parked in the grid area named here. */
1328.compact .slot.slot-left {
1329 grid-area: left;
1330}
1331.compact .slot.slot-top {
1332 grid-area: across;
1333}
1334.compact .slot.slot-right {
1335 grid-area: right;
1336}
1337.compact .slot.slot-bottom {
1338 grid-area: mine;
1339}
1340.compact .center {
1341 grid-area: bar;
1342}
1343/* The strip no longer floats in the middle of a slot — it is the slot. */
1344.compact .seat {
1345 position: relative;
1346 top: auto;
1347 left: auto;
1348 width: auto;
1349 height: 100%;
1350 padding: 2px 4px 3px;
1351}
1352/* Off to one side rather than dead centre: a card is narrow enough that the
1353 arrow would otherwise land on the name it is pointing out. */
1354.compact .win-arrow {
1355 font-size: 24px;
1356 left: 14px;
1357}
1358
1359/* ---------- the other three seats ---------- */
1360/* Read top-down: who they are, how many tiles they hold, what they have laid
1361 down, what they have thrown. The hand is the least interesting of those and
1362 goes smallest; the discards get whatever room is left, since reading them is
1363 the whole of playing against somebody. */
1364.compact .opp .seat {
1365 --tile-w: clamp(13px, calc((100vw / 3 - 20px) / 8.6), 32px);
1366 justify-content: flex-start;
1367 gap: 2px;
1368 border: 1px solid rgba(246, 240, 226, 0.09);
1369 border-radius: 8px;
1370 background: rgba(0, 0, 0, 0.14);
1371}
1372.compact .opp .nameplate {
1373 order: 0;
1374}
1375.compact .opp .hand {
1376 order: 1;
1377}
1378.compact .opp .melds {
1379 order: 2;
1380}
1381.compact .opp .discards {
1382 order: 3;
1383}
1384.compact .opp .actionbar {
1385 order: 4;
1386}
1387
1388.compact .opp .nameplate {
1389 flex-wrap: wrap;
1390 justify-content: center;
1391 gap: 3px 5px;
1392 font-size: 10px;
1393 padding-top: 0;
1394 row-gap: 1px;
1395}
1396.compact .opp .nameplate .wind {
1397 font-size: 13px;
1398}
1399.compact .opp .nameplate .wind em,
1400.compact .opp .nameplate .helpbtn,
1401.compact .opp .nameplate .sort {
1402 display: none;
1403}
1404.compact .opp .nameplate .chips {
1405 font-size: 13px;
1406 margin-left: 0;
1407}
1408.compact .opp .nameplate .tag {
1409 font-size: 9px;
1410 padding: 0 4px;
1411}
1412/* Face-down backs: a count you can see at a glance, nothing more. */
1413.compact .opp .hand {
1414 flex-wrap: wrap;
1415 justify-content: center;
1416 height: auto;
1417 padding-top: 0;
1418 gap: 1px;
1419 max-width: 100%;
1420}
1421.compact .opp .hand .tile {
1422 --tw: calc(var(--tile-w) * 0.52);
1423}
1424.compact .opp .melds {
1425 gap: 5px;
1426 min-height: 0;
1427 max-width: 100%;
1428}
1429.compact .opp .meld.flowers {
1430 margin-left: 5px;
1431 padding-left: 5px;
1432}
1433.compact .opp .discards {
1434 flex: 1 1 auto;
1435 max-width: 100%;
1436 min-height: 0;
1437 overflow-y: auto;
1438 align-content: flex-start;
1439}
1440/* No height is reserved here: an opponent's bar is empty except in hotseat,
1441 and a card that stays still matters less than the room it costs. */
1442.compact .opp .actionbar {
1443 min-height: 0;
1444 padding-right: 0;
1445 justify-content: center;
1446 gap: 4px;
1447}
1448.compact .opp .actionbar .btn {
1449 height: auto;
1450 min-height: 34px;
1451 padding: 2px 8px;
1452}
1453.compact .opp .actionbar .bi .zh {
1454 font-size: 14px;
1455}
1456.compact .opp .delta {
1457 font-size: 15px;
1458}
1459
1460/* ---------- your own seat ---------- */
1461/* A grid rather than the stack the strips use, so the melds and the discards
1462 share one line and the hand gets the depth that saves. */
1463.compact .mine .seat {
1464 --tile-w: min(calc((100vw - 24px) / 18.3), 54px);
1465 display: grid;
1466 /* `minmax(0, auto)`, so a full set of melds wraps inside its column instead
1467 of shouldering the discards off the end of the strip. */
1468 grid-template-columns: minmax(0, auto) minmax(0, 1fr);
1469 grid-template-areas:
1470 'melds discards'
1471 'bar bar'
1472 'hand hand'
1473 'name name';
1474 align-content: end;
1475 align-items: end;
1476 justify-items: center;
1477 gap: 2px 8px;
1478 padding: 2px 6px 3px;
1479}
1480.compact .mine .melds {
1481 grid-area: melds;
1482 justify-content: flex-start;
1483 min-height: 0;
1484 gap: 6px;
1485}
1486.compact .mine .discards {
1487 grid-area: discards;
1488 /* One line that scrolls, kept pinned to its end by Seat.tsx — wrapping would
1489 cost a second row of depth the hand wants more. Reserved even while empty,
1490 since this row and the melds beside it share it: without that the strip
1491 would jump the first time either one appeared. */
1492 flex: none;
1493 flex-wrap: nowrap;
1494 justify-content: flex-start;
1495 align-self: end;
1496 max-width: 100%;
1497 width: 100%;
1498 min-height: calc(var(--tile-w) * 0.9);
1499 margin-bottom: 0;
1500 overflow-x: auto;
1501 overflow-y: hidden;
1502 /* Melds and discards end up shoulder to shoulder here, and both are rows of
1503 tiles — the rule and the smaller face are what tell them apart. */
1504 border-left: 1px solid rgba(246, 240, 226, 0.16);
1505 padding-left: 8px;
1506 opacity: 0.85;
1507}
1508.compact .mine .discards .tile {
1509 --tw: calc(var(--tile-w) * 0.5);
1510}
1511.compact .mine .actionbar {
1512 grid-area: bar;
1513 --bar-h: 44px;
1514 justify-content: center;
1515 padding-right: 0;
1516 gap: 6px;
1517}
1518.compact .mine .hand {
1519 grid-area: hand;
1520 flex-wrap: wrap;
1521 align-content: flex-end;
1522 justify-content: center;
1523 height: auto;
1524 max-width: 100%;
1525 /* The row gap matches the padding for the same reason: the 摸 tag hangs above
1526 its tile, and on a wrapped hand that is somebody else's row. */
1527 gap: calc(var(--tile-w) * 0.34) 2px;
1528 padding-top: calc(var(--tile-w) * 0.34);
1529}
1530.compact .mine .nameplate {
1531 grid-area: name;
1532 flex-wrap: wrap;
1533 justify-content: center;
1534 gap: 3px 7px;
1535 font-size: 11px;
1536 padding-top: 0;
1537}
1538.compact .mine .nameplate .wind {
1539 font-size: 15px;
1540}
1541.compact .mine .nameplate .wind em {
1542 display: none;
1543}
1544.compact .mine .nameplate .chips {
1545 font-size: 14px;
1546}
1547.compact .mine .nameplate .btn.sort {
1548 min-height: 26px;
1549 padding: 1px 9px;
1550 font-size: 12px;
1551}
1552.compact .mine .nameplate .helpbtn {
1553 width: 26px;
1554 height: 26px;
1555 font-size: 13px;
1556}
1557
1558/* ---------- the middle ---------- */
1559/* One bar across the table rather than a square in the middle of it. */
1560.compact .center {
1561 position: relative;
1562 inset: auto;
1563 flex-direction: row;
1564 align-items: center;
1565 justify-content: space-between;
1566 gap: 8px;
1567 padding: 1px 6px;
1568 min-height: 44px;
1569}
1570.compact .facing {
1571 flex: 1 1 auto;
1572 flex-direction: row;
1573 align-items: center;
1574 justify-content: center;
1575 gap: 10px;
1576 min-width: 0;
1577}
1578.compact .last-discard {
1579 --tile-w: 30px;
1580 flex-direction: row;
1581 align-items: center;
1582 gap: 6px;
1583}
1584.compact .ld-label {
1585 font-size: 10px;
1586}
1587.compact .prompt {
1588 font-size: 12px;
1589 text-align: center;
1590}
1591.compact .table-controls {
1592 position: static;
1593 flex: none;
1594}
1595.compact .btn.undo {
1596 max-width: 34vw;
1597 min-height: 30px;
1598 padding: 2px 10px;
1599}
1600.compact .undo-what .zh {
1601 font-size: 11px;
1602}
1603.compact .undo-what .en {
1604 display: none;
1605}
1606/* What the wall square was telling you, in the room a bar has. */
1607.wall-pill {
1608 display: flex;
1609 align-items: baseline;
1610 gap: 5px;
1611 flex: none;
1612 letter-spacing: 0.06em;
1613 opacity: 0.75;
1614}
1615.wall-pill b {
1616 font-size: 18px;
1617 font-weight: 900;
1618 color: var(--gold);
1619}
1620.wall-pill em {
1621 font-style: normal;
1622 font-size: 9px;
1623 text-transform: uppercase;
1624 opacity: 0.7;
1625}
1626
1627/* The hand result covers the table instead of sitting in a square that is no
1628 longer there. */
1629.compact .result {
1630 position: fixed;
1631 top: 50%;
1632 left: 50%;
1633 transform: translate(-50%, -50%);
1634 z-index: 25;
1635 max-width: min(560px, 94vw);
1636 max-height: 92dvh;
1637 padding: 12px 16px;
1638 gap: 8px;
1639}
1640.compact .result h2 {
1641 font-size: 18px;
1642}
1643.compact .tai-list {
1644 grid-template-columns: repeat(auto-fit, minmax(92px, 1fr));
1645 gap: 1px 12px;
1646 font-size: 12px;
1647}
1648.compact .ready-row {
1649 font-size: 12px;
1650}
1651.compact .ready-name {
1652 min-width: 4.5em;
1653}
1654.compact .ready-tiles {
1655 --tile-w: 26px;
1656}
1657.compact .deltas {
1658 flex-wrap: wrap;
1659 justify-content: center;
1660 gap: 6px 14px;
1661 font-size: 12px;
1662}
1663.compact .big-btn {
1664 font-size: 18px;
1665 padding: 8px 22px;
1666 min-height: 44px;
1667}
1668
1669/* ---------- portrait ---------- */
1670/* Held upright there is no room for three cards side by side, so they stack in
1671 the order play reaches them, and the hand wraps to two rows. */
1672@media (orientation: portrait) {
1673 .compact .table {
1674 grid-template-columns: 1fr;
1675 grid-template-rows: repeat(3, minmax(0, 1fr)) auto auto;
1676 grid-template-areas:
1677 'right'
1678 'across'
1679 'left'
1680 'bar'
1681 'mine';
1682 }
1683 /* A card gets the whole width here, so the tiles can be read rather than
1684 merely counted; the discards wrap to a second line when they need it. */
1685 .compact .opp .seat {
1686 --tile-w: clamp(18px, calc((100vw - 26px) / 13), 34px);
1687 }
1688 .compact .mine .seat {
1689 --tile-w: min(calc((100vw - 26px) / 9.4), 54px);
1690 }
1691 /* A phone's width will not hold the wall count, who discarded what, the claim
1692 prompt and the table's own buttons on one line. The readout — the part that
1693 changes — takes a line of its own underneath. The room comes out of the
1694 other seats, which are the flexible rows, so the hand never moves. */
1695 .compact .center {
1696 flex-wrap: wrap;
1697 row-gap: 2px;
1698 }
1699 .compact .wall-pill {
1700 order: 0;
1701 }
1702 .compact .table-controls {
1703 order: 1;
1704 }
1705 .compact .facing {
1706 order: 2;
1707 flex-basis: 100%;
1708 }
1709}
1710
1711/* ---------- the panels ---------- */
1712.compact .lobby {
1713 justify-content: flex-start;
1714 overflow-y: auto;
1715 gap: 0.25rem;
1716 padding: 12px 14px calc(14px + env(safe-area-inset-bottom));
1717}
1718.compact .lobby > * {
1719 flex: none;
1720}
1721/* Sized off both axes: a phone held sideways has width to spare and no height
1722 at all, and the title was the first thing to push the buttons off. */
1723.compact .lobby h1 {
1724 font-size: clamp(1.9rem, min(9vw, 11vh), 3.2rem);
1725 margin-bottom: 0.2rem;
1726}
1727.compact .lobby .sub {
1728 font-size: 0.78rem;
1729}
1730/* The screen dividers this asks for are a four-round-a-table idea, and nobody
1731 props cards against a phone. */
1732.compact .lobby .hint {
1733 display: none;
1734}
1735.compact .resume {
1736 margin-top: 0.8rem;
1737 padding: 8px 16px;
1738}
1739.compact .resume-scores {
1740 flex-wrap: wrap;
1741 gap: 4px 12px;
1742 font-size: 12px;
1743}
1744.compact .lobby-buttons {
1745 margin-top: 0.9rem;
1746 gap: 8px;
1747 width: min(460px, 100%);
1748}
1749.compact .lobby-buttons .big-btn {
1750 min-width: 0;
1751 width: 100%;
1752 padding: 8px 16px;
1753}
1754.compact .lobby-extras {
1755 margin-top: 0.9rem;
1756}
1757/* Sideways there is barely 390px of height for a title, a saved game, three
1758 ways to start and the settings link. Everything gives a little so the last of
1759 them lands above the fold, rather than behind a scroll nobody looks for. */
1760@media (orientation: landscape) {
1761 .compact .lobby h1 {
1762 font-size: clamp(1.6rem, 9vh, 2.6rem);
1763 }
1764 .compact .lobby .sub,
1765 .compact .resume-when {
1766 display: none;
1767 }
1768 .compact .resume {
1769 margin-top: 0.4rem;
1770 padding: 6px 14px;
1771 }
1772 .compact .resume-line {
1773 font-size: 12px;
1774 }
1775 .compact .lobby-buttons {
1776 margin-top: 0.5rem;
1777 }
1778 .compact .lobby-buttons .big-btn {
1779 min-height: 40px;
1780 padding: 5px 16px;
1781 font-size: 19px;
1782 }
1783 .compact .lobby-extras {
1784 margin-top: 0.5rem;
1785 }
1786}
1787
1788.compact .help-slot .help {
1789 width: calc(100vw - 16px);
1790 --hd: calc(100dvh - 16px);
1791 translate: none;
1792 padding: 8px 12px 10px;
1793}
1794.compact .help-head h3 {
1795 font-size: 13px;
1796}
1797/* The strip version fills four columns and scrolls sideways, which reads fine
1798 across a laptop's width and not at all on a phone. One column, scrolled the
1799 way every other sheet on the device is. */
1800.compact .help-body {
1801 columns: auto;
1802 column-gap: normal;
1803 overflow-x: hidden;
1804}
1805
1806.compact .settings-scrim {
1807 padding: 6px;
1808}
1809.compact .settings {
1810 padding: 10px 14px 14px;
1811}
1812.compact .settings-head h3 {
1813 font-size: 16px;
1814}
1815.compact .settings-head .btn {
1816 min-height: 38px;
1817 padding: 2px 14px;
1818 font-size: 14px;
1819}
1820/* Two columns sideways, where there is width and no height; one held upright,
1821 where it is the other way round. */
1822.compact .settings-body {
1823 columns: 2;
1824 column-gap: 26px;
1825 padding-top: 10px;
1826}
1827@media (orientation: portrait) {
1828 .compact .settings-body {
1829 columns: 1;
1830 }
1831}
1832.compact .settings-body section {
1833 margin-bottom: 14px;
1834}
1835.compact .settings-body h4 {
1836 margin-bottom: 6px;
1837 font-size: 14px;
1838}
1839.compact .settings .f-label {
1840 min-width: 116px;
1841 font-size: 14px;
1842}
1843.compact .settings input[type='text'],
1844.compact .settings input:not([type]),
1845.compact .settings input[type='number'] {
1846 font-size: 15px;
1847 min-height: 40px;
1848 padding: 8px 10px;
1849}
1850.compact .settings .sw-name {
1851 font-size: 14px;
1852}
1853.compact .settings .note {
1854 font-size: 11px;
1855}