@energy8platform/platform-core 0.26.1 → 0.28.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.
- package/dist/index.cjs.js +0 -3615
- package/dist/index.cjs.js.map +1 -1
- package/dist/index.d.ts +2 -400
- package/dist/index.esm.js +1 -3613
- package/dist/index.esm.js.map +1 -1
- package/package.json +3 -9
- package/scripts/build-digit-font.mjs +72 -0
- package/src/index.ts +2 -17
- package/dist/shell.cjs.js +0 -3671
- package/dist/shell.cjs.js.map +0 -1
- package/dist/shell.d.ts +0 -433
- package/dist/shell.esm.js +0 -3664
- package/dist/shell.esm.js.map +0 -1
- package/scripts/gen-version.mjs +0 -21
- package/src/shell/GameShell.ts +0 -412
- package/src/shell/INTER-LICENSE.txt +0 -93
- package/src/shell/colors.ts +0 -32
- package/src/shell/components/BottomBar.ts +0 -237
- package/src/shell/components/BuyBonus.ts +0 -329
- package/src/shell/components/GameInfo.ts +0 -384
- package/src/shell/components/Modal.ts +0 -36
- package/src/shell/components/ReplayModal.ts +0 -57
- package/src/shell/components/Settings.ts +0 -59
- package/src/shell/components/icons.ts +0 -40
- package/src/shell/components/pickers.ts +0 -150
- package/src/shell/components/primitives.ts +0 -85
- package/src/shell/fonts.ts +0 -13
- package/src/shell/format.ts +0 -39
- package/src/shell/i18n.ts +0 -96
- package/src/shell/index.ts +0 -22
- package/src/shell/keyboard.ts +0 -229
- package/src/shell/locales.ts +0 -864
- package/src/shell/motion.ts +0 -43
- package/src/shell/shell.css.ts +0 -449
- package/src/shell/state.ts +0 -31
- package/src/shell/theme.ts +0 -54
- package/src/shell/types.ts +0 -262
- package/src/shell/version.ts +0 -3
package/src/shell/motion.ts
DELETED
|
@@ -1,43 +0,0 @@
|
|
|
1
|
-
/** True when the user (or environment) prefers no motion. Missing matchMedia
|
|
2
|
-
* — e.g. jsdom / SSR — is treated as reduced so animations never block. */
|
|
3
|
-
export function prefersReducedMotion(): boolean {
|
|
4
|
-
const mm = (globalThis as { matchMedia?: (q: string) => { matches: boolean } }).matchMedia;
|
|
5
|
-
if (typeof mm !== 'function') return true;
|
|
6
|
-
return mm('(prefers-reduced-motion: reduce)').matches;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
/** Animate el's text from `from` to `to` via `fmt`. Skips to final value when
|
|
10
|
-
* motion is reduced or rAF is unavailable. Returns a canceler that stops the loop —
|
|
11
|
-
* call it before the target node is detached so the rAF chain can't keep writing to
|
|
12
|
-
* (and pinning) a dead node, or stack up overlapping loops on rapid updates. */
|
|
13
|
-
export function countUp(
|
|
14
|
-
el: HTMLElement,
|
|
15
|
-
from: number,
|
|
16
|
-
to: number,
|
|
17
|
-
fmt: (n: number) => string,
|
|
18
|
-
durationMs = 450,
|
|
19
|
-
): () => void {
|
|
20
|
-
const raf = (globalThis as { requestAnimationFrame?: typeof requestAnimationFrame }).requestAnimationFrame;
|
|
21
|
-
const caf = (globalThis as { cancelAnimationFrame?: typeof cancelAnimationFrame }).cancelAnimationFrame;
|
|
22
|
-
if (prefersReducedMotion() || typeof raf !== 'function') {
|
|
23
|
-
el.textContent = fmt(to);
|
|
24
|
-
return () => {};
|
|
25
|
-
}
|
|
26
|
-
let handle = 0;
|
|
27
|
-
let cancelled = false;
|
|
28
|
-
const origin = (globalThis as { performance?: { now(): number } }).performance?.now() ?? 0;
|
|
29
|
-
const tick = (t: number) => {
|
|
30
|
-
if (cancelled) return;
|
|
31
|
-
const start = origin;
|
|
32
|
-
const p = Math.min(1, (t - start) / durationMs);
|
|
33
|
-
const eased = 1 - Math.pow(1 - p, 3); // easeOutCubic
|
|
34
|
-
el.textContent = fmt(from + (to - from) * eased);
|
|
35
|
-
if (p < 1) handle = raf(tick);
|
|
36
|
-
else el.textContent = fmt(to);
|
|
37
|
-
};
|
|
38
|
-
handle = raf(tick);
|
|
39
|
-
return () => {
|
|
40
|
-
cancelled = true;
|
|
41
|
-
if (typeof caf === 'function') caf(handle);
|
|
42
|
-
};
|
|
43
|
-
}
|
package/src/shell/shell.css.ts
DELETED
|
@@ -1,449 +0,0 @@
|
|
|
1
|
-
import { SHELL_FONT_CSS } from './fonts';
|
|
2
|
-
|
|
3
|
-
export const SHELL_ROOT_ID = '__ge-game-shell__';
|
|
4
|
-
|
|
5
|
-
// Inter (bundled, base64) leads the stack so the shell renders identically on every
|
|
6
|
-
// platform; the system fonts stay as graceful fallback if the webfont ever fails to load.
|
|
7
|
-
export const SHELL_CSS = SHELL_FONT_CSS + `
|
|
8
|
-
#${SHELL_ROOT_ID} {
|
|
9
|
-
position: absolute; inset: 0;
|
|
10
|
-
container-type: size; /* query container → centred modals size in cq units (responsive on every screen) */
|
|
11
|
-
pointer-events: none; z-index: 9000;
|
|
12
|
-
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
13
|
-
color: var(--shell-fg);
|
|
14
|
-
}
|
|
15
|
-
#${SHELL_ROOT_ID} svg { display:block; }
|
|
16
|
-
|
|
17
|
-
/* floating readouts (no panel) */
|
|
18
|
-
#${SHELL_ROOT_ID} .ge-rd { font-weight:700; font-size:13px; line-height:1; white-space:nowrap;
|
|
19
|
-
text-shadow:0 1px 3px rgba(0,0,0,.65); font-variant-numeric:tabular-nums; }
|
|
20
|
-
#${SHELL_ROOT_ID} .ge-rd .ge-lbl { display:block; color:var(--shell-muted); font-weight:600;
|
|
21
|
-
font-size:9px; letter-spacing:.1em; text-transform:uppercase; margin-bottom:4px; text-shadow:none; }
|
|
22
|
-
|
|
23
|
-
/* icon buttons (borderless) */
|
|
24
|
-
#${SHELL_ROOT_ID} .ge-iconbtn { pointer-events:auto; cursor:pointer; border:none; background:none;
|
|
25
|
-
padding:0; color:var(--shell-icon); width:40px; height:40px; display:flex; align-items:center;
|
|
26
|
-
justify-content:center; font-size:24px; position:relative; transition:transform .08s ease; }
|
|
27
|
-
#${SHELL_ROOT_ID} .ge-iconbtn:active { transform:scale(.92); }
|
|
28
|
-
#${SHELL_ROOT_ID} .ge-iconbtn[disabled] { opacity:.35; cursor:default; }
|
|
29
|
-
#${SHELL_ROOT_ID} .ge-iconbtn.ge-active { color:var(--shell-icon-active); }
|
|
30
|
-
|
|
31
|
-
/* SPIN — white disc by default, big glyph reaching for the rim; lights up accent on hover */
|
|
32
|
-
#${SHELL_ROOT_ID} .ge-shell-spin { pointer-events:auto; cursor:pointer; border:3px solid #000; border-radius:50%;
|
|
33
|
-
width:86px; height:86px; background:var(--shell-spin); color:var(--shell-spin-fg); box-sizing:border-box; font-size:68px;
|
|
34
|
-
display:flex; align-items:center; justify-content:center;
|
|
35
|
-
transition:transform .08s ease, box-shadow .12s ease, background .12s ease, color .12s ease; }
|
|
36
|
-
#${SHELL_ROOT_ID} .ge-shell-spin:hover { background:var(--shell-accent); color:#fff;
|
|
37
|
-
box-shadow:0 0 0 3px var(--shell-accent), 0 0 18px 2px var(--shell-accent); }
|
|
38
|
-
#${SHELL_ROOT_ID} .ge-shell-spin:active { transform:scale(.94); }
|
|
39
|
-
/* disabled: dim via filter (stays opaque) so the plaque seam doesn't show through the disc */
|
|
40
|
-
#${SHELL_ROOT_ID} .ge-shell-spin[disabled] { filter:grayscale(.4) brightness(.62); box-shadow:none; cursor:default; }
|
|
41
|
-
/* spinning while a spin is in progress */
|
|
42
|
-
#${SHELL_ROOT_ID} .ge-shell-spin.ge-spinning svg { transform-origin:50% 50%; animation:ge-spin-rot .8s linear infinite; }
|
|
43
|
-
@keyframes ge-spin-rot { to { transform:rotate(360deg); } }
|
|
44
|
-
/* autoplay running: STOP glyph + countdown stacked in the disc */
|
|
45
|
-
#${SHELL_ROOT_ID} .ge-shell-spin.ge-stop { position:relative; }
|
|
46
|
-
#${SHELL_ROOT_ID} .ge-spin-stop { display:flex; } /* STOP glyph at the disc's full size, like SPIN */
|
|
47
|
-
/* no entrance animation: the bar re-renders many times per spin, so any animation here
|
|
48
|
-
would replay each time and make the counter visibly jump. tabular-nums keeps it steady. */
|
|
49
|
-
#${SHELL_ROOT_ID} .ge-spin-count { position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
|
|
50
|
-
font-size:22px; font-weight:800; line-height:1; font-variant-numeric:tabular-nums; }
|
|
51
|
-
|
|
52
|
-
/* BUY BONUS — round accent badge, 2-line label, text pulses + accent glow on hover */
|
|
53
|
-
#${SHELL_ROOT_ID} .ge-shell-buybonus { pointer-events:auto; cursor:pointer; box-sizing:border-box;
|
|
54
|
-
width:80px; height:80px; border-radius:50%; border:3px solid #000; background:var(--shell-accent);
|
|
55
|
-
color:#fff; font-weight:800; letter-spacing:.02em; font-size:13px; line-height:1.08; text-align:center;
|
|
56
|
-
display:flex; align-items:center; justify-content:center; transition:transform .08s ease, box-shadow .12s ease; }
|
|
57
|
-
#${SHELL_ROOT_ID} .ge-shell-buybonus span { display:inline-block; will-change:transform; }
|
|
58
|
-
#${SHELL_ROOT_ID} .ge-shell-buybonus:hover { box-shadow:0 0 0 3px var(--shell-accent), 0 0 16px 1px var(--shell-accent); }
|
|
59
|
-
#${SHELL_ROOT_ID} .ge-shell-buybonus:hover span { animation:ge-bb-pulse .7s ease-in-out infinite; }
|
|
60
|
-
#${SHELL_ROOT_ID} .ge-shell-buybonus:active { transform:scale(.96); }
|
|
61
|
-
#${SHELL_ROOT_ID} .ge-shell-buybonus[disabled] { filter:grayscale(.5) brightness(.72); box-shadow:none; cursor:default; }
|
|
62
|
-
#${SHELL_ROOT_ID} .ge-shell-buybonus[disabled] span { animation:none; }
|
|
63
|
-
@keyframes ge-bb-pulse { 0%,100%{transform:scale(1)} 50%{transform:scale(1.16)} }
|
|
64
|
-
|
|
65
|
-
/* host = bottom-anchored flex column: [win pill (on overflow)] above [the bar] */
|
|
66
|
-
#${SHELL_ROOT_ID} .ge-shell-barhost { position:absolute; left:0; right:0; bottom:0; pointer-events:none;
|
|
67
|
-
display:flex; flex-direction:column; align-items:center; justify-content:flex-end; gap:4px;
|
|
68
|
-
transform-origin:bottom center; }
|
|
69
|
-
/* bottom bar: transparent, two zones (wide default) */
|
|
70
|
-
#${SHELL_ROOT_ID} .ge-shell-bottom { width:100%; box-sizing:border-box; pointer-events:none;
|
|
71
|
-
display:flex; align-items:center; justify-content:space-between; padding:0 18px 6px; gap:14px; }
|
|
72
|
-
#${SHELL_ROOT_ID} .ge-zone { display:flex; align-items:center; gap:14px; pointer-events:none; }
|
|
73
|
-
#${SHELL_ROOT_ID} .ge-zone > * { pointer-events:auto; }
|
|
74
|
-
#${SHELL_ROOT_ID} .ge-betstep { display:flex; flex-direction:column; gap:2px; }
|
|
75
|
-
/* auto stacked over turbo (far-right column, base/desktop) */
|
|
76
|
-
#${SHELL_ROOT_ID} .ge-autoturbo { display:flex; flex-direction:column; gap:2px; }
|
|
77
|
-
#${SHELL_ROOT_ID} .ge-autoturbo .ge-iconbtn { width:40px; height:30px; }
|
|
78
|
-
|
|
79
|
-
/* mobile (portrait) — full-width stacked plaques: [balance · win] · [controls] · [− bet +] */
|
|
80
|
-
#${SHELL_ROOT_ID}.ge-mobile .ge-shell-bottom { flex-direction:column; align-items:center; gap:14px; padding:8px 12px 8px; }
|
|
81
|
-
#${SHELL_ROOT_ID}.ge-mobile .ge-m-top { width:100%; height:46px; justify-content:space-between; }
|
|
82
|
-
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls { display:flex; align-items:center; justify-content:space-between;
|
|
83
|
-
width:100%; box-sizing:border-box; height:62px; border-radius:18px; padding:0 18px; }
|
|
84
|
-
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-iconbtn,
|
|
85
|
-
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-rd,
|
|
86
|
-
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-rd .ge-lbl { color:#fff; }
|
|
87
|
-
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-rd { text-shadow:none; text-align:center; }
|
|
88
|
-
/* mobile: restore accent hover + autoplay glow (the white rule above out-specifies the base ones) */
|
|
89
|
-
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-iconbtn:hover,
|
|
90
|
-
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-iconbtn.ge-glow { color:var(--shell-accent); }
|
|
91
|
-
#${SHELL_ROOT_ID}.ge-mobile .ge-m-bet { width:100%; height:46px; padding:0 18px; gap:8px; justify-content:space-between; }
|
|
92
|
-
#${SHELL_ROOT_ID}.ge-mobile .ge-shell-spin { width:84px; height:84px; font-size:66px; }
|
|
93
|
-
#${SHELL_ROOT_ID}.ge-mobile .ge-shell-buybonus { width:50px; height:50px; font-size:9px; border-width:2px; }
|
|
94
|
-
/* landscape overflow — size the column to content, centre it; JS scales the whole stack to fit */
|
|
95
|
-
#${SHELL_ROOT_ID} .ge-shell-barhost.ge-fit { left:50%; right:auto; width:max-content; max-width:none; }
|
|
96
|
-
#${SHELL_ROOT_ID} .ge-shell-barhost.ge-fit .ge-shell-bottom { width:max-content; }
|
|
97
|
-
|
|
98
|
-
/* full-screen overlays — header (title left, prominent X/back), scrolling body, vh-clamped for popout/mobile.
|
|
99
|
-
Frosted backdrop: the game shows through, blurred, behind a light dark tint (white-on-blur).
|
|
100
|
-
Same glass "plaque" language as the control bar; scheme-independent. */
|
|
101
|
-
#${SHELL_ROOT_ID} .ge-shell-overlay { position:absolute; inset:0; z-index:50; pointer-events:auto;
|
|
102
|
-
background:rgba(12,17,28,.5); backdrop-filter:blur(20px) saturate(120%);
|
|
103
|
-
-webkit-backdrop-filter:blur(20px) saturate(120%);
|
|
104
|
-
display:flex; flex-direction:column; animation:ge-ov-in .16s ease-out; }
|
|
105
|
-
/* SPIN/BUY BONUS use z-index:3 to overlap their plaques; lift the overlay clear above them */
|
|
106
|
-
@keyframes ge-ov-in { from { opacity:0; } to { opacity:1; } }
|
|
107
|
-
#${SHELL_ROOT_ID} .ge-ov-head { flex:0 0 auto; display:flex; align-items:center; gap:8px; padding:6px 10px; }
|
|
108
|
-
#${SHELL_ROOT_ID} .ge-ov-title { flex:1; margin:0; text-align:center; color:#fff; font-weight:800; letter-spacing:.04em;
|
|
109
|
-
text-transform:uppercase; font-size:clamp(13px,2.6vh,16px); }
|
|
110
|
-
#${SHELL_ROOT_ID} .ge-ov-spacer { flex:0 0 auto; width:32px; }
|
|
111
|
-
#${SHELL_ROOT_ID} .ge-ov-nav { flex:0 0 auto; display:flex; align-items:center; justify-content:center;
|
|
112
|
-
width:32px; height:32px; border-radius:9px; cursor:pointer; color:#fff;
|
|
113
|
-
background:var(--shell-plaque-dark); border:none; font-size:18px;
|
|
114
|
-
transition:background .12s ease, color .12s ease; }
|
|
115
|
-
#${SHELL_ROOT_ID} .ge-ov-nav:hover { background:var(--shell-plaque-glass); color:var(--shell-accent); }
|
|
116
|
-
#${SHELL_ROOT_ID} .ge-ov-scroll { flex:1 1 auto; min-height:0; overflow-y:auto; overflow-x:hidden; }
|
|
117
|
-
#${SHELL_ROOT_ID} .ge-ov-body { max-width:800px; margin:0 auto; box-sizing:border-box;
|
|
118
|
-
padding:clamp(6px,2vh,16px) clamp(16px,4vw,24px) clamp(16px,4vh,28px); }
|
|
119
|
-
|
|
120
|
-
/* full-width overlay rows — glass plaque, white-on-dark */
|
|
121
|
-
#${SHELL_ROOT_ID} .ge-ov-row { display:flex; align-items:center; gap:12px; width:100%; box-sizing:border-box;
|
|
122
|
-
padding:clamp(11px,2.2vh,15px) 16px; margin-bottom:10px; border:none;
|
|
123
|
-
border-radius:16px; background:var(--shell-plaque-glass); color:#fff; font-size:14px; font-weight:600; }
|
|
124
|
-
#${SHELL_ROOT_ID} .ge-ov-row .ge-grow { flex:1; text-align:left; }
|
|
125
|
-
#${SHELL_ROOT_ID} button.ge-ov-row { cursor:pointer; font-family:inherit; transition:background .12s ease, color .12s ease; }
|
|
126
|
-
#${SHELL_ROOT_ID} button.ge-ov-row:hover { background:var(--shell-plaque-glass-hover); color:var(--shell-accent); }
|
|
127
|
-
#${SHELL_ROOT_ID} .ge-ov-row.ge-col { flex-direction:column; align-items:stretch; gap:10px; }
|
|
128
|
-
#${SHELL_ROOT_ID} .ge-ov-row .ge-row-head { display:flex; justify-content:space-between; align-items:center; }
|
|
129
|
-
#${SHELL_ROOT_ID} .ge-ov-row .ge-row-head .ge-val { color:var(--shell-plaque-label); font-variant-numeric:tabular-nums; font-weight:700; }
|
|
130
|
-
#${SHELL_ROOT_ID} .ge-slider { width:100%; accent-color:var(--shell-accent); }
|
|
131
|
-
#${SHELL_ROOT_ID} .ge-toggle { flex:0 0 auto; width:42px; height:24px; border-radius:999px; background:var(--shell-plaque-line);
|
|
132
|
-
border:none; cursor:pointer; position:relative; }
|
|
133
|
-
#${SHELL_ROOT_ID} .ge-toggle.ge-on { background:var(--shell-accent); }
|
|
134
|
-
#${SHELL_ROOT_ID} .ge-toggle i { position:absolute; top:2px; left:2px; width:20px; height:20px; border-radius:50%;
|
|
135
|
-
background:#fff; transition:left .12s ease; }
|
|
136
|
-
#${SHELL_ROOT_ID} .ge-toggle.ge-on i { left:20px; }
|
|
137
|
-
/* sound on/off — speaker icon button (replaces the toggle) */
|
|
138
|
-
#${SHELL_ROOT_ID} .ge-snd { pointer-events:auto; cursor:pointer; border:none; background:none; padding:0;
|
|
139
|
-
width:36px; height:36px; display:flex; align-items:center; justify-content:center; font-size:24px;
|
|
140
|
-
color:#fff; transition:color .12s ease, transform .08s ease; }
|
|
141
|
-
#${SHELL_ROOT_ID} .ge-snd:not(.ge-active) { color:var(--shell-plaque-label); }
|
|
142
|
-
#${SHELL_ROOT_ID} .ge-snd:hover { color:var(--shell-accent); }
|
|
143
|
-
#${SHELL_ROOT_ID} .ge-snd:active { transform:scale(.92); }
|
|
144
|
-
|
|
145
|
-
/* game info — each section is its own glass plaque; body text sized for comfortable reading */
|
|
146
|
-
#${SHELL_ROOT_ID} .ge-gi-sec { margin-bottom:12px; background:var(--shell-plaque-glass);
|
|
147
|
-
border-radius:16px; padding:16px 18px; }
|
|
148
|
-
#${SHELL_ROOT_ID} .ge-gi-sec h3 { color:var(--shell-plaque-label); font-size:11px; letter-spacing:.14em;
|
|
149
|
-
text-transform:uppercase; margin:0 0 12px; }
|
|
150
|
-
#${SHELL_ROOT_ID} .ge-gi-sec p { color:rgba(255,255,255,.88); font-size:15px; line-height:1.6; margin:0; }
|
|
151
|
-
#${SHELL_ROOT_ID} .ge-gi-version { text-align:center; color:var(--shell-muted); font-size:11px;
|
|
152
|
-
letter-spacing:.08em; opacity:.7; margin:4px 0 2px; }
|
|
153
|
-
|
|
154
|
-
/* hotkeys — keycap chips → localized action label, mirrors controls row layout */
|
|
155
|
-
#${SHELL_ROOT_ID} .ge-gi-hk-block { display:flex; flex-direction:column; }
|
|
156
|
-
#${SHELL_ROOT_ID} .ge-gi-hk { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:9px 0; }
|
|
157
|
-
#${SHELL_ROOT_ID} .ge-gi-hk + .ge-gi-hk { border-top:1px solid var(--shell-plaque-line); }
|
|
158
|
-
#${SHELL_ROOT_ID} .ge-gi-hk-chips { display:flex; align-items:center; flex-wrap:wrap; gap:4px; flex:0 0 auto; }
|
|
159
|
-
#${SHELL_ROOT_ID} .ge-gi-hk-combo { display:inline-flex; align-items:center; gap:4px; }
|
|
160
|
-
#${SHELL_ROOT_ID} .ge-gi-hk-chip { display:inline-flex; align-items:center; justify-content:center;
|
|
161
|
-
padding:2px 7px; border-radius:6px; border:1px solid var(--shell-plaque-line);
|
|
162
|
-
background:var(--shell-plaque-dark); color:#fff;
|
|
163
|
-
font-family:'SFMono-Regular',Consolas,'Liberation Mono',Menlo,monospace; font-size:12px;
|
|
164
|
-
font-weight:600; line-height:1.5; white-space:nowrap; min-width:1.6em; text-align:center; }
|
|
165
|
-
#${SHELL_ROOT_ID} .ge-gi-hk-sep { color:var(--shell-plaque-label); font-size:11px; padding:0 1px; }
|
|
166
|
-
#${SHELL_ROOT_ID} .ge-gi-hk-sep2 { color:var(--shell-plaque-label); font-size:11px; padding:0 4px; }
|
|
167
|
-
#${SHELL_ROOT_ID} .ge-gi-hk-tx { color:rgba(255,255,255,.88); font-size:14px; font-weight:600; text-align:right; flex:1; }
|
|
168
|
-
|
|
169
|
-
/* controls — two blocks (gameplay / menu & info), icon/name/description per control */
|
|
170
|
-
#${SHELL_ROOT_ID} .ge-gi-ctl-block + .ge-gi-ctl-block { margin-top:16px; padding-top:4px; border-top:1px solid var(--shell-plaque-line); }
|
|
171
|
-
#${SHELL_ROOT_ID} .ge-gi-ctl-block-h { color:var(--shell-plaque-label); font-size:11px; letter-spacing:.12em;
|
|
172
|
-
text-transform:uppercase; margin:8px 0 2px; font-weight:700; }
|
|
173
|
-
#${SHELL_ROOT_ID} .ge-gi-ctl { display:flex; align-items:center; gap:14px; padding:9px 0; }
|
|
174
|
-
#${SHELL_ROOT_ID} .ge-gi-ctl + .ge-gi-ctl { border-top:1px solid var(--shell-plaque-line); }
|
|
175
|
-
#${SHELL_ROOT_ID} .ge-gi-ctl-ic { flex:0 0 auto; width:48px; height:48px; display:flex; align-items:center;
|
|
176
|
-
justify-content:center; font-size:26px; color:#fff; }
|
|
177
|
-
/* bet shows both chevrons, stacked & smaller */
|
|
178
|
-
#${SHELL_ROOT_ID} .ge-gi-ctl-ic--bet { flex-direction:column; font-size:18px; gap:0; }
|
|
179
|
-
/* buy bonus draws the real control-bar badge, scaled down & non-interactive */
|
|
180
|
-
#${SHELL_ROOT_ID} .ge-gi-ctl-ic .ge-shell-buybonus { width:46px; height:46px; font-size:8px; border-width:2px; pointer-events:none; }
|
|
181
|
-
#${SHELL_ROOT_ID} .ge-gi-ctl-tx { display:flex; flex-direction:column; gap:2px; }
|
|
182
|
-
#${SHELL_ROOT_ID} .ge-gi-ctl-tx b { color:#fff; font-size:15px; font-weight:700; }
|
|
183
|
-
#${SHELL_ROOT_ID} .ge-gi-ctl-tx span { color:rgba(255,255,255,.7); font-size:13px; line-height:1.4; }
|
|
184
|
-
|
|
185
|
-
/* paytable — cards: symbol image on top, name, then win tiers "<count> x<mult>" */
|
|
186
|
-
#${SHELL_ROOT_ID} .ge-gi-pt-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(120px,1fr)); gap:10px; }
|
|
187
|
-
#${SHELL_ROOT_ID} .ge-gi-pt-card { display:flex; flex-direction:column; align-items:center; gap:8px;
|
|
188
|
-
background:var(--shell-plaque-dark); border-radius:14px; padding:14px 12px; }
|
|
189
|
-
#${SHELL_ROOT_ID} .ge-gi-pt-sym { display:flex; flex-direction:column; align-items:center; gap:5px; min-width:0; }
|
|
190
|
-
#${SHELL_ROOT_ID} .ge-gi-pt-sym img { width:56px; height:56px; object-fit:contain; }
|
|
191
|
-
#${SHELL_ROOT_ID} .ge-gi-pt-sym span { color:#fff; font-size:13px; font-weight:700; text-transform:uppercase; letter-spacing:.05em; }
|
|
192
|
-
#${SHELL_ROOT_ID} .ge-gi-pt-wins { display:flex; flex-direction:column; align-items:stretch; gap:2px; width:100%; }
|
|
193
|
-
#${SHELL_ROOT_ID} .ge-gi-pt-win { display:flex; justify-content:space-between; align-items:baseline; gap:14px;
|
|
194
|
-
font-size:13.5px; font-variant-numeric:tabular-nums; white-space:nowrap; }
|
|
195
|
-
#${SHELL_ROOT_ID} .ge-gi-pt-win i { color:var(--shell-plaque-label); font-style:normal; }
|
|
196
|
-
#${SHELL_ROOT_ID} .ge-gi-pt-win b { color:var(--shell-accent); font-weight:700; }
|
|
197
|
-
|
|
198
|
-
/* wins — accent-filled mini-grid(s); classic = one per line, cluster/anywhere = one, ways = two */
|
|
199
|
-
#${SHELL_ROOT_ID} .ge-gi-pl-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(72px,1fr)); gap:12px; }
|
|
200
|
-
#${SHELL_ROOT_ID} .ge-gi-pl-item { display:flex; flex-direction:column; align-items:center; gap:6px; }
|
|
201
|
-
#${SHELL_ROOT_ID} .ge-gi-pl-svg { width:100%; height:auto; max-width:140px; }
|
|
202
|
-
#${SHELL_ROOT_ID} .ge-gi-pl-cell { fill:var(--shell-plaque-line); }
|
|
203
|
-
#${SHELL_ROOT_ID} .ge-gi-pl-on { fill:var(--shell-accent); }
|
|
204
|
-
#${SHELL_ROOT_ID} .ge-gi-pl-cap { color:#fff; font-size:13px; font-weight:700; }
|
|
205
|
-
#${SHELL_ROOT_ID} .ge-gi-win-row { display:flex; align-items:flex-start; gap:16px; flex-wrap:wrap; }
|
|
206
|
-
#${SHELL_ROOT_ID} .ge-gi-win-row .ge-gi-pl-svg { flex:0 0 auto; width:140px; }
|
|
207
|
-
#${SHELL_ROOT_ID} .ge-gi-win-desc { flex:1; min-width:160px; color:rgba(255,255,255,.78); font-size:14px; line-height:1.5; margin:0; }
|
|
208
|
-
#${SHELL_ROOT_ID} .ge-gi-win-two { display:flex; gap:22px; flex-wrap:wrap; }
|
|
209
|
-
#${SHELL_ROOT_ID} .ge-gi-win-col { display:flex; flex-direction:column; align-items:center; gap:6px; }
|
|
210
|
-
#${SHELL_ROOT_ID} .ge-gi-win-col .ge-gi-pl-svg { width:140px; }
|
|
211
|
-
#${SHELL_ROOT_ID} .ge-gi-win-tag { font-size:12px; font-weight:700; }
|
|
212
|
-
#${SHELL_ROOT_ID} .ge-gi-win-ok { color:#4ade80; }
|
|
213
|
-
#${SHELL_ROOT_ID} .ge-gi-win-no { color:#f87171; }
|
|
214
|
-
#${SHELL_ROOT_ID} .ge-gi-win-badge { display:inline-block; margin-left:10px; padding:2px 8px; border-radius:999px;
|
|
215
|
-
font-size:11px; font-weight:700; letter-spacing:.02em; color:var(--shell-accent);
|
|
216
|
-
background:var(--shell-plaque-dark); vertical-align:middle; }
|
|
217
|
-
|
|
218
|
-
/* modes — comparison cards */
|
|
219
|
-
#${SHELL_ROOT_ID} .ge-gi-modes { display:flex; flex-direction:column; }
|
|
220
|
-
#${SHELL_ROOT_ID} .ge-gi-mode { padding:12px 0; }
|
|
221
|
-
#${SHELL_ROOT_ID} .ge-gi-mode + .ge-gi-mode { border-top:1px solid var(--shell-plaque-line); }
|
|
222
|
-
#${SHELL_ROOT_ID} .ge-gi-mode-top { display:flex; flex-wrap:wrap; align-items:baseline; justify-content:space-between; gap:6px 16px; margin-bottom:6px; }
|
|
223
|
-
#${SHELL_ROOT_ID} .ge-gi-mode-h { color:#fff; font-size:16px; font-weight:800; }
|
|
224
|
-
#${SHELL_ROOT_ID} .ge-gi-mode-stats { display:flex; flex-wrap:wrap; gap:14px; }
|
|
225
|
-
#${SHELL_ROOT_ID} .ge-gi-mode-st { display:flex; align-items:baseline; gap:5px; }
|
|
226
|
-
#${SHELL_ROOT_ID} .ge-gi-mode-st span { color:var(--shell-plaque-label); font-size:10px; letter-spacing:.1em; text-transform:uppercase; }
|
|
227
|
-
#${SHELL_ROOT_ID} .ge-gi-mode-st b { color:#fff; font-size:14px; font-weight:800; font-variant-numeric:tabular-nums; }
|
|
228
|
-
#${SHELL_ROOT_ID} .ge-gi-mode-desc { color:rgba(255,255,255,.78); font-size:14px; line-height:1.5; margin:0; }
|
|
229
|
-
/* shapes — row list (grid illustration left, name + description right), modes-style text */
|
|
230
|
-
#${SHELL_ROOT_ID} .ge-gi-shapes { display:flex; flex-direction:column; }
|
|
231
|
-
#${SHELL_ROOT_ID} .ge-gi-shape { display:flex; align-items:center; gap:16px; padding:12px 0; }
|
|
232
|
-
#${SHELL_ROOT_ID} .ge-gi-shape + .ge-gi-shape { border-top:1px solid var(--shell-plaque-line); }
|
|
233
|
-
#${SHELL_ROOT_ID} .ge-gi-shape .ge-gi-pl-svg { flex:0 0 auto; width:96px; }
|
|
234
|
-
#${SHELL_ROOT_ID} .ge-gi-shape-tx { flex:1; min-width:0; display:flex; flex-direction:column; gap:4px; }
|
|
235
|
-
#${SHELL_ROOT_ID} .ge-gi-custom { color:rgba(255,255,255,.88); font-size:15px; line-height:1.6; }
|
|
236
|
-
|
|
237
|
-
/* buy bonus cards — art-forward, centred, flat (no gradients); --card-acc/--card-ink per card.
|
|
238
|
-
order: title → thumb → description → (spacer) → volatility → price → full-bleed button. */
|
|
239
|
-
/* wide: horizontal scrolling strip. Cards scale PROPORTIONALLY with the viewport — every
|
|
240
|
-
dimension is in em-units, so the single font-size knob shrinks the whole card uniformly
|
|
241
|
-
(like the control bar's fit-scale), keeping small popouts readable. mobile: vertical stack. */
|
|
242
|
-
/* the buy-bonus scroll area is a SIZE CONTAINER, so the cards' cqh units measure the overlay
|
|
243
|
-
(the popout frame) and not the browser window — cards fit without any vertical scroll. */
|
|
244
|
-
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-ov-scroll { container-type:size; }
|
|
245
|
-
/* Popout / landscape: the horizontal card strip must be the ONLY scroll axis. The cqh-sized cards
|
|
246
|
-
are meant to fit the overlay height (no vertical scroll), but the old 7px font floor stopped them
|
|
247
|
-
shrinking on the tiniest popouts — so they spilled past the frame and the overlay grew a SECOND,
|
|
248
|
-
vertical scrollbar (scrollable in both directions). Two changes keep it single-axis:
|
|
249
|
-
1. the card font floor drops (below) so cards actually fit the frame height; and
|
|
250
|
-
2. vertical scrolling is locked off as a belt-and-braces guard, with the strip centred (the body
|
|
251
|
-
fills the frame and centres the grid) so any sub-pixel slack splits evenly instead of clipping
|
|
252
|
-
the price/CTA off the bottom.
|
|
253
|
-
This mirrors the pixi shell, which masks the cards and drag-scrolls on the X axis alone. (Centring
|
|
254
|
-
lives on the body, not the scroll box, so the grid keeps its width and its own X-scroll.) */
|
|
255
|
-
#${SHELL_ROOT_ID}:not(.ge-mobile) [data-ge="buybonus-overlay"] .ge-ov-scroll { overflow-y:hidden; }
|
|
256
|
-
#${SHELL_ROOT_ID}:not(.ge-mobile) [data-ge="buybonus-overlay"] .ge-ov-body {
|
|
257
|
-
min-height:100%; box-sizing:border-box; display:flex; flex-direction:column; justify-content:center; }
|
|
258
|
-
/* buy-bonus uses the FULL overlay width (no 800px centre cap) so the card row isn't cropped at
|
|
259
|
-
the sides; small horizontal padding keeps the cards off the screen edges. */
|
|
260
|
-
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-ov-body { max-width:none; padding:clamp(6px,2.5cqh,16px) clamp(12px,3vw,28px); }
|
|
261
|
-
#${SHELL_ROOT_ID} .ge-bb-grid { display:flex; gap:14px; justify-content:safe center; overflow-x:auto; overflow-y:hidden; padding-bottom:6px;
|
|
262
|
-
scroll-snap-type:x proximity; -webkit-overflow-scrolling:touch; }
|
|
263
|
-
/* the one knob that scales the whole card (its whole layout is em-relative). It is the SMALLER of two
|
|
264
|
-
fits, so the card is always fully visible:
|
|
265
|
-
• 3.4cqh — height: the card stays inside the band between the header and the bet footer (cqh
|
|
266
|
-
measures the overlay popout frame, not the browser window);
|
|
267
|
-
• 84cqw / (N*18) — width: the N cards (each 18em) fit the frame width side-by-side instead of
|
|
268
|
-
overflowing into an X-scroll. --ge-bb-n is the live card count, set in BuyBonus.ts.
|
|
269
|
-
Floor is deliberately tiny: on a 400×225 popout a fully visible card beats a bigger clipped one. */
|
|
270
|
-
#${SHELL_ROOT_ID} .ge-bb-grid .ge-bonus-card { flex:0 0 18em; scroll-snap-align:start;
|
|
271
|
-
font-size:clamp(4px, min(3.4cqh, calc(84cqw / (var(--ge-bb-n,3) * 18))), 12px); }
|
|
272
|
-
/* mobile: vertical stack at a fixed, readable size — scroll the list; only shrink if the card would
|
|
273
|
-
be wider than the frame (very narrow viewport), so it never overflows horizontally. */
|
|
274
|
-
#${SHELL_ROOT_ID}.ge-mobile .ge-bb-grid { display:flex; flex-direction:column; gap:14px; overflow:visible; }
|
|
275
|
-
#${SHELL_ROOT_ID}.ge-mobile .ge-bb-grid .ge-bonus-card { flex:0 0 auto; font-size:min(12px, calc(92cqw / 18)); }
|
|
276
|
-
#${SHELL_ROOT_ID} .ge-bonus-card { display:flex; flex-direction:column; border-radius:1.4em; overflow:hidden;
|
|
277
|
-
background:var(--shell-plaque-glass); border:1px solid var(--shell-plaque-line); color:#fff; text-align:center;
|
|
278
|
-
pointer-events:auto; cursor:pointer; transition:box-shadow .12s ease, background .12s ease; }
|
|
279
|
-
#${SHELL_ROOT_ID} .ge-bonus-card:hover:not(.ge-bonus-off) {
|
|
280
|
-
box-shadow:0 0 0 1px var(--card-acc), 0 12px 34px -12px var(--card-acc); }
|
|
281
|
-
#${SHELL_ROOT_ID} .ge-bonus-card--kbd-focus:not(.ge-bonus-off) {
|
|
282
|
-
box-shadow:0 0 0 1px var(--card-acc), 0 12px 34px -12px var(--card-acc); }
|
|
283
|
-
/* custom card (BonusOption.custom): keep grid sizing + accent vars, drop the default chrome so the game owns the UI */
|
|
284
|
-
#${SHELL_ROOT_ID} .ge-bonus-card--custom { background:none; border:none; cursor:default; }
|
|
285
|
-
#${SHELL_ROOT_ID} .ge-bonus-body { display:flex; flex-direction:column; align-items:center; flex:1; padding:1.25em 1.1em .9em; }
|
|
286
|
-
#${SHELL_ROOT_ID} .ge-bonus-title { font-size:1.3em; font-weight:800; letter-spacing:.04em; text-transform:uppercase;
|
|
287
|
-
color:var(--card-acc); margin-bottom:.75em; }
|
|
288
|
-
#${SHELL_ROOT_ID} .ge-bonus-thumb { height:6.2em; display:flex; align-items:center; justify-content:center; margin-bottom:.7em; }
|
|
289
|
-
#${SHELL_ROOT_ID} .ge-bonus-thumb img { max-height:100%; max-width:100%; object-fit:contain; filter:drop-shadow(0 6px 14px rgba(0,0,0,.45)); }
|
|
290
|
-
#${SHELL_ROOT_ID} .ge-bonus-thumb-ph { font-size:3.5em; color:var(--card-acc); opacity:.85; display:flex; }
|
|
291
|
-
#${SHELL_ROOT_ID} .ge-bonus-desc { font-size:.96em; line-height:1.45; color:rgba(255,255,255,.82); }
|
|
292
|
-
#${SHELL_ROOT_ID} .ge-bonus-spacer { flex:1; min-height:.7em; }
|
|
293
|
-
#${SHELL_ROOT_ID} .ge-bonus-vol { display:flex; justify-content:center; align-items:center; gap:0;
|
|
294
|
-
font-size:2.1em; line-height:1; margin-bottom:.55em; }
|
|
295
|
-
#${SHELL_ROOT_ID} .ge-bonus-vol-on, #${SHELL_ROOT_ID} .ge-bonus-vol-off { display:inline-flex; gap:0; }
|
|
296
|
-
#${SHELL_ROOT_ID} .ge-bonus-vol-on { color:var(--card-acc); }
|
|
297
|
-
#${SHELL_ROOT_ID} .ge-bonus-vol-off { color:rgba(255,255,255,.18); }
|
|
298
|
-
#${SHELL_ROOT_ID} .ge-bonus-price { font-size:1.6em; font-weight:800; color:#fff; font-variant-numeric:tabular-nums; white-space:nowrap; }
|
|
299
|
-
#${SHELL_ROOT_ID} .ge-bonus-cta { width:100%; border:none; padding:1.15em; font-weight:800; font-size:1.05em;
|
|
300
|
-
letter-spacing:.05em; text-transform:uppercase; cursor:pointer; background:var(--card-acc); color:var(--card-ink);
|
|
301
|
-
transition:filter .12s ease; }
|
|
302
|
-
#${SHELL_ROOT_ID} .ge-bonus-cta:hover:not([disabled]) { filter:brightness(1.06); }
|
|
303
|
-
/* unaffordable / disabled */
|
|
304
|
-
#${SHELL_ROOT_ID} .ge-bonus-card.ge-bonus-off { opacity:.62; cursor:default; }
|
|
305
|
-
#${SHELL_ROOT_ID} .ge-bonus-off .ge-bonus-title { color:rgba(255,255,255,.6); }
|
|
306
|
-
#${SHELL_ROOT_ID} .ge-bonus-off .ge-bonus-vol-on { color:rgba(255,255,255,.4); }
|
|
307
|
-
#${SHELL_ROOT_ID} .ge-bonus-off .ge-bonus-cta { background:#8d939e; color:#3a3f47; cursor:default; }
|
|
308
|
-
/* buy-bonus confirm — bonus preview inside the shared sheet card (see .ge-sheet-actions below) */
|
|
309
|
-
#${SHELL_ROOT_ID} .ge-confirm-preview { display:flex; flex-direction:column; align-items:center; gap:10px; text-align:center; }
|
|
310
|
-
#${SHELL_ROOT_ID} .ge-confirm-preview .ge-bonus-thumb,
|
|
311
|
-
#${SHELL_ROOT_ID} .ge-confirm-preview .ge-bonus-vol { margin:0; }
|
|
312
|
-
/* effective-bet readout while a feature is active (value colour set inline to the feature accent) */
|
|
313
|
-
#${SHELL_ROOT_ID} .ge-rd.ge-bet-feature { font-weight:800; }
|
|
314
|
-
/* bet control — compact pill in a thin footer at the screen bottom (footer only as tall as the pill) */
|
|
315
|
-
#${SHELL_ROOT_ID} .ge-bb-betbar { flex:0 0 auto; display:flex; justify-content:center; padding:4px; }
|
|
316
|
-
#${SHELL_ROOT_ID} .ge-bb-betpill { display:inline-flex; align-items:center; gap:4px;
|
|
317
|
-
background:var(--shell-plaque-dark); border-radius:999px; padding:3px 5px; }
|
|
318
|
-
#${SHELL_ROOT_ID} .ge-bb-betstep { pointer-events:auto; cursor:pointer; width:32px; height:32px; border:none; border-radius:50%;
|
|
319
|
-
background:none; color:#fff; font-size:20px; display:flex; align-items:center; justify-content:center;
|
|
320
|
-
transition:color .12s ease, transform .08s ease; }
|
|
321
|
-
#${SHELL_ROOT_ID} .ge-bb-betstep:hover { color:var(--shell-accent); }
|
|
322
|
-
#${SHELL_ROOT_ID} .ge-bb-betstep:active { transform:scale(.9); }
|
|
323
|
-
#${SHELL_ROOT_ID} .ge-bb-betval { min-width:80px; text-align:center; padding:0 2px; }
|
|
324
|
-
#${SHELL_ROOT_ID} .ge-bb-betval span { display:block; font-size:7px; font-weight:600; letter-spacing:.14em; text-transform:uppercase;
|
|
325
|
-
color:var(--shell-plaque-label); }
|
|
326
|
-
#${SHELL_ROOT_ID} .ge-bb-betval b { font-size:14px; font-weight:800; font-variant-numeric:tabular-nums; color:#fff; }
|
|
327
|
-
/* Popout S (short landscape): the header (title + ✕) and the bet footer are fixed-px and dwarf the
|
|
328
|
-
shrunk cards. Scale the buy-bonus chrome down with the FRAME height. Units are cqh (the overlay is a
|
|
329
|
-
size container below), NOT vh — vh tracks the browser window, which only equals the frame inside the
|
|
330
|
-
Stake iframe, so it wouldn't shrink in the demo's device-frame view. Coefficients hit the normal cap
|
|
331
|
-
by ~450px tall (Popout L) and shrink below that, to a readable floor at Popout S (225px). */
|
|
332
|
-
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] { container:ge-bb-frame / size; }
|
|
333
|
-
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-ov-head { padding:clamp(3px,1.33cqh,6px) 10px; }
|
|
334
|
-
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-ov-title { font-size:clamp(11px,3.5cqh,16px); }
|
|
335
|
-
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-ov-spacer { width:clamp(24px,7cqh,32px); }
|
|
336
|
-
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-ov-nav { width:clamp(24px,7cqh,32px); height:clamp(24px,7cqh,32px);
|
|
337
|
-
font-size:clamp(14px,4cqh,18px); border-radius:clamp(7px,2cqh,9px); }
|
|
338
|
-
/* the bet pill read too large next to the shrunk cards — size it ~0.8× across the board (the floors
|
|
339
|
-
stay readable; the maxima are what dominate on Popout L / wide frames). */
|
|
340
|
-
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-bb-betbar { padding:clamp(2px,.75cqh,3px); }
|
|
341
|
-
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-bb-betpill { padding:clamp(2px,.55cqh,3px) clamp(3px,.9cqh,4px); }
|
|
342
|
-
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-bb-betstep { width:clamp(20px,5.7cqh,26px); height:clamp(20px,5.7cqh,26px);
|
|
343
|
-
font-size:clamp(12px,3.5cqh,16px); }
|
|
344
|
-
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-bb-betval { min-width:clamp(50px,14cqh,66px); }
|
|
345
|
-
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-bb-betval b { font-size:clamp(9px,2.5cqh,11px); }
|
|
346
|
-
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-bb-betval span { font-size:clamp(5px,1.25cqh,6px); }
|
|
347
|
-
|
|
348
|
-
/* ═══ base/wide plaque bar — grouped dark + glass panels (reference-style) ═══ */
|
|
349
|
-
#${SHELL_ROOT_ID} .ge-zone-plaques { gap:0; } /* panels connect; buttons overlap */
|
|
350
|
-
#${SHELL_ROOT_ID} .ge-pl { display:flex; align-items:center; height:56px; box-sizing:border-box;
|
|
351
|
-
border-radius:16px; padding:0 20px; gap:18px; }
|
|
352
|
-
#${SHELL_ROOT_ID} .ge-pl-dark { background:var(--shell-plaque-dark); }
|
|
353
|
-
#${SHELL_ROOT_ID} .ge-pl-glass { background:var(--shell-plaque-glass); }
|
|
354
|
-
/* FS/replay left blocks — Free Spins counter (compact) + Total Win, standalone glass plaques
|
|
355
|
-
sitting just right of the balance pill */
|
|
356
|
-
#${SHELL_ROOT_ID} .ge-pl-fs, #${SHELL_ROOT_ID} .ge-pl-totalwin { margin-left:8px; }
|
|
357
|
-
#${SHELL_ROOT_ID} .ge-pl-fs { padding:0 16px; }
|
|
358
|
-
#${SHELL_ROOT_ID} .ge-pl .ge-rd { color:#fff; text-shadow:none; }
|
|
359
|
-
#${SHELL_ROOT_ID} .ge-pl .ge-rd .ge-lbl { color:var(--shell-plaque-label); }
|
|
360
|
-
#${SHELL_ROOT_ID} .ge-pl .ge-iconbtn { color:#fff; }
|
|
361
|
-
/* LEFT: [menu] ⊐ coin ⊏ [balance] — coin overlaps both; balance fixed-wide so it doesn't jiggle */
|
|
362
|
-
#${SHELL_ROOT_ID} .ge-pl-menu { border-radius:16px 0 0 16px; padding-right:20px; }
|
|
363
|
-
#${SHELL_ROOT_ID} .ge-pl-bal { border-radius:0 16px 16px 0; padding-left:24px; min-width:200px; }
|
|
364
|
-
#${SHELL_ROOT_ID} .ge-zone-plaques .ge-shell-buybonus { margin:0 -16px; position:relative; z-index:3; }
|
|
365
|
-
/* RIGHT: [bet] · |divider| · [auto · SPIN · turbo] */
|
|
366
|
-
#${SHELL_ROOT_ID} .ge-pl-bet { border-radius:16px 0 0 16px; justify-content:space-between;
|
|
367
|
-
width:210px; padding-right:8px; } /* fixed: bet value never reflows the panel */
|
|
368
|
-
#${SHELL_ROOT_ID} .ge-pl-divider { align-self:center; flex:0 0 auto; width:1px; height:30px;
|
|
369
|
-
background:var(--shell-plaque-line); }
|
|
370
|
-
#${SHELL_ROOT_ID} .ge-spinwrap { display:flex; align-items:center; gap:10px; height:56px;
|
|
371
|
-
border-radius:0 16px 16px 0; padding:0 8px 0 14px; }
|
|
372
|
-
#${SHELL_ROOT_ID} .ge-spinwrap .ge-iconbtn { color:#fff; }
|
|
373
|
-
#${SHELL_ROOT_ID} .ge-spinwrap .ge-shell-spin { margin:0 -2px; position:relative; z-index:3; }
|
|
374
|
-
/* tighter bet chevrons, parked next to autoplay */
|
|
375
|
-
#${SHELL_ROOT_ID} .ge-pl-bet .ge-betstep { gap:0; }
|
|
376
|
-
#${SHELL_ROOT_ID} .ge-pl-bet .ge-betstep .ge-iconbtn { height:24px; }
|
|
377
|
-
/* accent hover highlight on every control */
|
|
378
|
-
#${SHELL_ROOT_ID} .ge-pl .ge-iconbtn:hover, #${SHELL_ROOT_ID} .ge-spinwrap .ge-iconbtn:hover,
|
|
379
|
-
#${SHELL_ROOT_ID} .ge-iconbtn:hover { color:var(--shell-accent); }
|
|
380
|
-
/* turbo at rest (level 0) reads as dimmed — clearly off, but lighter than a true [disabled] control */
|
|
381
|
-
#${SHELL_ROOT_ID} [data-ge="turbo"]:not(.ge-active) { opacity:.5; }
|
|
382
|
-
#${SHELL_ROOT_ID} [data-ge="turbo"]:not(.ge-active):hover { opacity:1; }
|
|
383
|
-
/* autoplay glow while running (beats the white .ge-spinwrap/.ge-pl colour rules) */
|
|
384
|
-
#${SHELL_ROOT_ID} .ge-iconbtn.ge-glow { color:var(--shell-accent); filter:drop-shadow(0 0 6px var(--shell-accent)); }
|
|
385
|
-
/* tappable stake readout opens the bet picker */
|
|
386
|
-
#${SHELL_ROOT_ID} .ge-betbtn { pointer-events:auto; cursor:pointer; border-radius:8px; transition:color .12s ease; }
|
|
387
|
-
#${SHELL_ROOT_ID} .ge-betbtn:hover { color:var(--shell-accent); }
|
|
388
|
-
#${SHELL_ROOT_ID} .ge-betbtn.ge-disabled { pointer-events:none; }
|
|
389
|
-
/* WIN — inline between the groups, same block height/shape as the other plaques (glass tone) */
|
|
390
|
-
#${SHELL_ROOT_ID} .ge-winpill { flex:0 0 auto; align-self:center; display:flex; align-items:center;
|
|
391
|
-
justify-content:center; gap:8px; height:56px; box-sizing:border-box; padding:0 24px; border-radius:16px;
|
|
392
|
-
white-space:nowrap; pointer-events:none; background:var(--shell-plaque-glass); color:#fff;
|
|
393
|
-
font-size:16px; font-weight:800; font-variant-numeric:tabular-nums; text-shadow:none; }
|
|
394
|
-
#${SHELL_ROOT_ID} .ge-winpill .ge-lbl { display:inline; margin:0; color:rgba(255,255,255,.7);
|
|
395
|
-
font-size:10px; letter-spacing:.12em; }
|
|
396
|
-
/* lifted above the bar on overflow — compact pill (flex child of the host, scaled with it) */
|
|
397
|
-
#${SHELL_ROOT_ID} .ge-winpill.ge-up { height:auto; padding:6px 16px; border-radius:999px; }
|
|
398
|
-
|
|
399
|
-
/* centred CARD modal — opaque card on a frosted backdrop, accent title heading at the top
|
|
400
|
-
(no ✕), content, full-bleed footer button(s). Shared by the buy-bonus confirm AND the
|
|
401
|
-
bet/autoplay pickers so every centred modal is the same type. Close via backdrop click. */
|
|
402
|
-
#${SHELL_ROOT_ID} .ge-sheet { position:absolute; inset:0; z-index:60; pointer-events:auto; display:flex;
|
|
403
|
-
align-items:center; justify-content:center; padding:clamp(10px,4vh,24px); box-sizing:border-box;
|
|
404
|
-
background:rgba(12,17,28,.5); backdrop-filter:blur(var(--ge-sheet-blur,20px)) saturate(120%);
|
|
405
|
-
-webkit-backdrop-filter:blur(var(--ge-sheet-blur,20px)) saturate(120%); animation:ge-ov-in .16s ease-out; }
|
|
406
|
-
/* Card sizes in cq units of the shell root → responsive on EVERY screen, not just popouts. The
|
|
407
|
-
card's font-size is the one knob (clamped for readability); everything inside is em-relative so
|
|
408
|
-
the whole card scales as a unit. GameShell.fitModal() still transform-scales it down as a
|
|
409
|
-
backstop for very short popouts. */
|
|
410
|
-
#${SHELL_ROOT_ID} .ge-modal-card { font-size:clamp(11px, 2cqmin, 15px); width:100%; max-width:28em; box-sizing:border-box;
|
|
411
|
-
overflow:hidden; transform-origin:center center; background:var(--shell-plaque-solid); border-radius:1.3em;
|
|
412
|
-
display:flex; flex-direction:column; }
|
|
413
|
-
/* ✕ pinned to the overlay corner (the screen), not the card */
|
|
414
|
-
#${SHELL_ROOT_ID} .ge-modal-close { position:absolute; top:12px; right:12px; z-index:2; width:36px; height:36px;
|
|
415
|
-
border:none; border-radius:50%; cursor:pointer; pointer-events:auto; background:var(--shell-plaque-dark); color:#fff;
|
|
416
|
-
display:flex; align-items:center; justify-content:center; font-size:20px; transition:background .12s ease, color .12s ease; }
|
|
417
|
-
#${SHELL_ROOT_ID} .ge-modal-close:hover { background:var(--shell-plaque-glass); color:var(--shell-accent); }
|
|
418
|
-
#${SHELL_ROOT_ID} .ge-modal-body { padding:1.2em; display:flex; flex-direction:column; gap:1.05em; }
|
|
419
|
-
#${SHELL_ROOT_ID} .ge-modal-title { margin:0; text-align:center; color:var(--card-acc, var(--shell-accent));
|
|
420
|
-
font-weight:800; letter-spacing:.04em; text-transform:uppercase; font-size:1.2em; }
|
|
421
|
-
#${SHELL_ROOT_ID} .ge-modal-text { margin:0; text-align:center; color:rgba(255,255,255,.85); font-size:.93em; line-height:1.5; }
|
|
422
|
-
#${SHELL_ROOT_ID} .ge-sheet-grid { display:grid; gap:.65em; grid-template-columns:repeat(var(--cols,3),1fr); }
|
|
423
|
-
#${SHELL_ROOT_ID}.ge-mobile .ge-sheet-grid { grid-template-columns:repeat(var(--cols-m,var(--cols,3)),1fr); }
|
|
424
|
-
/* the bet picker packs 6 chips/row → give its card room (autoplay & co. stay at the 28em default) */
|
|
425
|
-
#${SHELL_ROOT_ID} .ge-sheet[data-ge="bet-modal"] .ge-modal-card { max-width:44em; }
|
|
426
|
-
#${SHELL_ROOT_ID} .ge-chip { pointer-events:auto; cursor:pointer; border:1px solid var(--shell-plaque-line);
|
|
427
|
-
border-radius:.8em; background:rgba(255,255,255,.04); color:#fff; font-size:1em; font-weight:700;
|
|
428
|
-
font-variant-numeric:tabular-nums; padding:.8em .55em; transition:background .12s ease, border-color .12s ease; }
|
|
429
|
-
#${SHELL_ROOT_ID} .ge-chip:hover { background:var(--shell-plaque-glass-hover); }
|
|
430
|
-
#${SHELL_ROOT_ID} .ge-chip.ge-on { border-color:var(--shell-accent); background:var(--shell-accent); color:#fff; }
|
|
431
|
-
/* full-bleed footer button(s), flush to the card's bottom edge (card clips the corners) */
|
|
432
|
-
#${SHELL_ROOT_ID} .ge-modal-actions { display:flex; }
|
|
433
|
-
#${SHELL_ROOT_ID} .ge-modal-actions > * { flex:1; }
|
|
434
|
-
#${SHELL_ROOT_ID} .ge-modal-btn { width:100%; border:none; padding:1.05em; font-size:1em; font-weight:800;
|
|
435
|
-
letter-spacing:.04em; text-transform:uppercase; cursor:pointer; pointer-events:auto; transition:filter .12s ease; }
|
|
436
|
-
#${SHELL_ROOT_ID} .ge-modal-btn:hover:not([disabled]) { filter:brightness(1.08); }
|
|
437
|
-
#${SHELL_ROOT_ID} .ge-modal-btn--accent { background:var(--card-acc, var(--shell-accent)); color:#fff; }
|
|
438
|
-
#${SHELL_ROOT_ID} .ge-modal-btn--ghost { background:var(--shell-plaque-glass-hover); color:#fff; }
|
|
439
|
-
/* replay summary — label/value rows, accented total-win row */
|
|
440
|
-
#${SHELL_ROOT_ID} .ge-replay-rows { display:flex; flex-direction:column; }
|
|
441
|
-
#${SHELL_ROOT_ID} .ge-replay-row { display:flex; justify-content:space-between; align-items:baseline; gap:1.05em; padding:.73em .13em; }
|
|
442
|
-
#${SHELL_ROOT_ID} .ge-replay-row + .ge-replay-row { border-top:1px solid var(--shell-plaque-line); }
|
|
443
|
-
#${SHELL_ROOT_ID} .ge-replay-row span { color:var(--shell-plaque-label); text-transform:uppercase; letter-spacing:.07em; font-size:.73em; font-weight:700; }
|
|
444
|
-
#${SHELL_ROOT_ID} .ge-replay-row b { color:#fff; font-weight:800; font-size:1em; font-variant-numeric:tabular-nums; }
|
|
445
|
-
#${SHELL_ROOT_ID} .ge-replay-total span { color:#fff; font-size:.8em; }
|
|
446
|
-
#${SHELL_ROOT_ID} .ge-replay-total b { color:var(--shell-accent); font-size:1.27em; }
|
|
447
|
-
|
|
448
|
-
#${SHELL_ROOT_ID}.ge-shell-hidden { opacity:0; pointer-events:none; transition:opacity .25s ease; }
|
|
449
|
-
`;
|
package/src/shell/state.ts
DELETED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import type { ShellConfig, ShellState } from './types';
|
|
2
|
-
|
|
3
|
-
export function createInitialState(config: ShellConfig): ShellState {
|
|
4
|
-
return {
|
|
5
|
-
mode: config.mode,
|
|
6
|
-
replay: config.replay ?? config.mode === 'replay',
|
|
7
|
-
balance: config.balance,
|
|
8
|
-
win: config.win,
|
|
9
|
-
bet: config.currentBet ?? config.defaultBet,
|
|
10
|
-
availableBets: [...config.availableBets],
|
|
11
|
-
busy: false,
|
|
12
|
-
autoplay: { active: false, remaining: 0 },
|
|
13
|
-
turbo: 0,
|
|
14
|
-
buyBonusEnabled: true,
|
|
15
|
-
freeSpins: { current: 0, total: 0, totalWin: 0 },
|
|
16
|
-
activeFeature: null,
|
|
17
|
-
};
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
/** Step bet up/down within availableBets, clamped at the ends. */
|
|
21
|
-
export function stepBet(state: ShellState, direction: 1 | -1): number {
|
|
22
|
-
const idx = state.availableBets.indexOf(state.bet);
|
|
23
|
-
const next = Math.max(0, Math.min(state.availableBets.length - 1, idx + direction));
|
|
24
|
-
return state.availableBets[next];
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/** Cycle turbo level 0..maxLevels (wraps back to 0). */
|
|
28
|
-
export function nextTurbo(current: number, maxLevels: number): number {
|
|
29
|
-
if (maxLevels <= 0) return 0;
|
|
30
|
-
return current >= maxLevels ? 0 : current + 1;
|
|
31
|
-
}
|
package/src/shell/theme.ts
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
import type { ThemeConfig } from './types';
|
|
2
|
-
import { BRAND_ACCENT } from './colors';
|
|
3
|
-
|
|
4
|
-
const DEFAULT_ACCENT = BRAND_ACCENT; // brand purple — BUY BONUS + active states (single source: colors.ts)
|
|
5
|
-
|
|
6
|
-
// Neutral palettes for each scheme. Everything the shell paints reads from these
|
|
7
|
-
// tokens, so a single `scheme` flip recolours bar, icons, overlays and toggles.
|
|
8
|
-
const PALETTE = {
|
|
9
|
-
dark: {
|
|
10
|
-
fg: '#f3f5fa', muted: '#9aa3b6', icon: '#c7cedb', iconActive: '#ffffff',
|
|
11
|
-
surface: '#0c111c', hairline: 'rgba(255,255,255,.07)',
|
|
12
|
-
veil: 'rgba(255,255,255,.05)', veilStrong: 'rgba(255,255,255,.1)', track: 'rgba(255,255,255,.16)',
|
|
13
|
-
soft: '#dfe4ee', spin: '#f4f6fb', spinFg: '#141a28',
|
|
14
|
-
},
|
|
15
|
-
light: {
|
|
16
|
-
fg: '#15202e', muted: '#5a6678', icon: '#3c4658', iconActive: '#0b1220',
|
|
17
|
-
surface: '#eef1f7', hairline: 'rgba(15,23,42,.12)',
|
|
18
|
-
veil: 'rgba(15,23,42,.05)', veilStrong: 'rgba(15,23,42,.09)', track: 'rgba(15,23,42,.22)',
|
|
19
|
-
soft: '#3a4453', spin: '#1c2434', spinFg: '#f3f6fb',
|
|
20
|
-
},
|
|
21
|
-
} as const;
|
|
22
|
-
|
|
23
|
-
/** CSS custom-property block for the shell root. `scheme` picks the palette;
|
|
24
|
-
* only the accent is additionally game-overridable. */
|
|
25
|
-
export function buildThemeVars(theme: ThemeConfig = {}): string {
|
|
26
|
-
const p = PALETTE[theme.scheme === 'light' ? 'light' : 'dark'];
|
|
27
|
-
return [
|
|
28
|
-
`--shell-fg: ${p.fg}`,
|
|
29
|
-
`--shell-muted: ${p.muted}`,
|
|
30
|
-
`--shell-icon: ${p.icon}`,
|
|
31
|
-
`--shell-icon-active: ${p.iconActive}`,
|
|
32
|
-
`--shell-surface: ${p.surface}`,
|
|
33
|
-
`--shell-hairline: ${p.hairline}`,
|
|
34
|
-
`--shell-veil: ${p.veil}`,
|
|
35
|
-
`--shell-veil-strong: ${p.veilStrong}`,
|
|
36
|
-
`--shell-track: ${p.track}`,
|
|
37
|
-
`--shell-soft: ${p.soft}`,
|
|
38
|
-
`--shell-spin: ${p.spin}`,
|
|
39
|
-
`--shell-spin-fg: ${p.spinFg}`,
|
|
40
|
-
`--shell-radius: 12px`,
|
|
41
|
-
// Plaque tokens — the grouped dark/glass panel language shared by the control bar
|
|
42
|
-
// AND the overlays. Scheme-independent (always dark, white-on-dark) so bar + overlays
|
|
43
|
-
// stay visually identical regardless of the dark/light `scheme`.
|
|
44
|
-
`--shell-plaque-dark: rgba(6,9,15,.86)`,
|
|
45
|
-
`--shell-plaque-glass: rgba(30,36,48,.70)`,
|
|
46
|
-
`--shell-plaque-glass-hover: rgba(40,48,64,.86)`,
|
|
47
|
-
// Opaque surface for centred modals (confirm, bet/autoplay pickers) so they read solid,
|
|
48
|
-
// not see-through, over the frosted backdrop.
|
|
49
|
-
`--shell-plaque-solid: #1a2030`,
|
|
50
|
-
`--shell-plaque-line: rgba(255,255,255,.22)`,
|
|
51
|
-
`--shell-plaque-label: rgba(255,255,255,.6)`,
|
|
52
|
-
`--shell-accent: ${theme.accent ?? DEFAULT_ACCENT}`,
|
|
53
|
-
].join('; ') + ';';
|
|
54
|
-
}
|