@energy8platform/shell 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/html.cjs.js +3894 -0
- package/dist/html.cjs.js.map +1 -0
- package/dist/html.d.ts +616 -0
- package/dist/html.esm.js +3879 -0
- package/dist/html.esm.js.map +1 -0
- package/dist/index.cjs.js +1593 -0
- package/dist/index.cjs.js.map +1 -0
- package/dist/index.d.ts +554 -0
- package/dist/index.esm.js +1582 -0
- package/dist/index.esm.js.map +1 -0
- package/dist/pixi.cjs.js +5740 -0
- package/dist/pixi.cjs.js.map +1 -0
- package/dist/pixi.d.ts +682 -0
- package/dist/pixi.esm.js +5726 -0
- package/dist/pixi.esm.js.map +1 -0
- package/package.json +79 -0
- package/src/core/EventEmitter.ts +55 -0
- package/src/core/ShellController.ts +234 -0
- package/src/core/colors.ts +32 -0
- package/src/core/fonts-digits.ts +12 -0
- package/src/core/fonts.ts +13 -0
- package/src/core/format.ts +39 -0
- package/src/core/i18n.ts +96 -0
- package/src/core/index.ts +35 -0
- package/src/core/keyboard.ts +229 -0
- package/src/core/locales.ts +864 -0
- package/src/core/motion.ts +10 -0
- package/src/core/renderer.ts +121 -0
- package/src/core/state.ts +31 -0
- package/src/core/theme.ts +81 -0
- package/src/core/types.ts +269 -0
- package/src/core/version.ts +3 -0
- package/src/ui/html/HtmlRenderer.ts +292 -0
- package/src/ui/html/components/BottomBar.ts +240 -0
- package/src/ui/html/components/BuyBonus.ts +326 -0
- package/src/ui/html/components/GameInfo.ts +384 -0
- package/src/ui/html/components/Modal.ts +36 -0
- package/src/ui/html/components/ReplayModal.ts +58 -0
- package/src/ui/html/components/Settings.ts +59 -0
- package/src/ui/html/components/pickers.ts +146 -0
- package/src/ui/html/icons-preview.svg +224 -0
- package/src/ui/html/icons.ts +31 -0
- package/src/ui/html/index.ts +36 -0
- package/src/ui/html/motion-dom.ts +29 -0
- package/src/ui/html/primitives.ts +85 -0
- package/src/ui/html/shell.css.ts +528 -0
- package/src/ui/html/theme-css.ts +21 -0
- package/src/ui/pixi/PixiRenderer.ts +350 -0
- package/src/ui/pixi/components/BottomBar.ts +477 -0
- package/src/ui/pixi/components/BuyBonus.ts +763 -0
- package/src/ui/pixi/components/GameInfo.ts +559 -0
- package/src/ui/pixi/components/Modal.ts +40 -0
- package/src/ui/pixi/components/ReplayModal.ts +80 -0
- package/src/ui/pixi/components/Settings.ts +117 -0
- package/src/ui/pixi/components/pickers.ts +170 -0
- package/src/ui/pixi/context.ts +52 -0
- package/src/ui/pixi/icons.ts +45 -0
- package/src/ui/pixi/index.ts +56 -0
- package/src/ui/pixi/motion-pixi.ts +58 -0
- package/src/ui/pixi/pixi-icon.ts +119 -0
- package/src/ui/pixi/primitives/card.ts +227 -0
- package/src/ui/pixi/primitives/controls.ts +243 -0
- package/src/ui/pixi/primitives/flex.ts +335 -0
- package/src/ui/pixi/primitives/overlay.ts +181 -0
- package/src/ui/pixi/primitives/scroll.ts +117 -0
- package/src/ui/pixi/primitives/widgets.ts +680 -0
- package/src/ui/pixi/text.ts +131 -0
|
@@ -0,0 +1,528 @@
|
|
|
1
|
+
import { SHELL_FONT_CSS } from '@/core/fonts';
|
|
2
|
+
import { SHELL_DIGIT_FONT_CSS } from '@/core/fonts-digits';
|
|
3
|
+
|
|
4
|
+
export const SHELL_ROOT_ID = '__ge-game-shell__';
|
|
5
|
+
|
|
6
|
+
// Inter (bundled, base64) leads the stack so the shell renders identically on every
|
|
7
|
+
// platform; the system fonts stay as graceful fallback if the webfont ever fails to load.
|
|
8
|
+
export const SHELL_CSS = SHELL_FONT_CSS + SHELL_DIGIT_FONT_CSS + `
|
|
9
|
+
#${SHELL_ROOT_ID} {
|
|
10
|
+
position: absolute; inset: 0;
|
|
11
|
+
container-type: size; /* query container → centred modals size in cq units (responsive on every screen) */
|
|
12
|
+
pointer-events: none; z-index: 9000;
|
|
13
|
+
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
14
|
+
color: var(--shell-fg);
|
|
15
|
+
}
|
|
16
|
+
#${SHELL_ROOT_ID} svg { display:block; }
|
|
17
|
+
|
|
18
|
+
/* floating readouts (no panel) */
|
|
19
|
+
#${SHELL_ROOT_ID} .ge-rd { font-weight:700; font-size:13px; line-height:1; white-space:nowrap;
|
|
20
|
+
text-shadow:0 1px 3px rgba(0,0,0,.65); font-variant-numeric:tabular-nums; }
|
|
21
|
+
#${SHELL_ROOT_ID} .ge-rd .ge-lbl { display:block; color:var(--shell-muted); font-weight:600;
|
|
22
|
+
font-size:9px; letter-spacing:.1em; text-transform:uppercase; margin-bottom:4px; text-shadow:none; }
|
|
23
|
+
|
|
24
|
+
/* icon buttons (borderless) */
|
|
25
|
+
#${SHELL_ROOT_ID} .ge-iconbtn { pointer-events:auto; cursor:pointer; border:none; background:none;
|
|
26
|
+
padding:0; color:var(--shell-icon); width:40px; height:40px; display:flex; align-items:center;
|
|
27
|
+
justify-content:center; font-size:24px; position:relative; transition:transform .08s ease; }
|
|
28
|
+
#${SHELL_ROOT_ID} .ge-iconbtn:active { transform:scale(.92); }
|
|
29
|
+
#${SHELL_ROOT_ID} .ge-iconbtn[disabled] { opacity:.35; cursor:default; }
|
|
30
|
+
#${SHELL_ROOT_ID} .ge-iconbtn.ge-active { color:var(--shell-icon-active); }
|
|
31
|
+
/* Turbo — ONE bolt glyph (turbo1); the level is shown by colour, not by swapping glyphs:
|
|
32
|
+
off (ge-turbo-0) = muted grey · L1 (ge-turbo-1) = white bolt + 2px accent OUTER outline ·
|
|
33
|
+
L2 (ge-turbo-2) = accent-filled bolt + 2px accent outer outline.
|
|
34
|
+
Each level is a full-size bolt with a 2px OUTER ring; only the fill + ring colour change:
|
|
35
|
+
off = white fill + BLACK ring · L1 = white fill + accent ring · L2 = accent fill + accent ring.
|
|
36
|
+
The outer ring uses the paint-order:stroke + double-width trick (the stroke straddles the path, so
|
|
37
|
+
painting it BEHIND the fill hides the inner half, leaving only the outer ~2px). Hover follows the
|
|
38
|
+
standard bar-button behaviour (line ~400: white disc + accent) — the bolt + ring tint accent. */
|
|
39
|
+
#${SHELL_ROOT_ID} .ge-iconbtn[data-ge="turbo"] svg { overflow:visible; }
|
|
40
|
+
#${SHELL_ROOT_ID} .ge-iconbtn[data-ge="turbo"] svg path { paint-order:stroke; stroke:var(--shell-accent); stroke-width:4; stroke-linejoin:round; }
|
|
41
|
+
#${SHELL_ROOT_ID} .ge-iconbtn.ge-turbo-0 svg path { fill:#fff; stroke:#000; }
|
|
42
|
+
#${SHELL_ROOT_ID} .ge-iconbtn.ge-turbo-1 svg path { fill:#fff; }
|
|
43
|
+
#${SHELL_ROOT_ID} .ge-iconbtn.ge-turbo-2 svg path { fill:var(--shell-accent); }
|
|
44
|
+
#${SHELL_ROOT_ID} .ge-bar-panel .ge-iconbtn[data-ge="turbo"]:hover svg path { fill:var(--shell-accent); stroke:var(--shell-accent); }
|
|
45
|
+
|
|
46
|
+
/* SPIN — white disc by default, big glyph reaching for the rim; lights up accent on hover */
|
|
47
|
+
#${SHELL_ROOT_ID} .ge-shell-spin { pointer-events:auto; cursor:pointer; border:3px solid #000; border-radius:50%;
|
|
48
|
+
width:86px; height:86px; background:var(--shell-spin); color:var(--shell-spin-fg); box-sizing:border-box; font-size:68px;
|
|
49
|
+
display:flex; align-items:center; justify-content:center;
|
|
50
|
+
transition:transform .08s ease, box-shadow .12s ease, background .12s ease, color .12s ease; }
|
|
51
|
+
#${SHELL_ROOT_ID} .ge-shell-spin:hover { background:var(--shell-accent); color:#fff;
|
|
52
|
+
box-shadow:0 0 0 3px var(--shell-accent), 0 0 18px 2px var(--shell-accent); }
|
|
53
|
+
#${SHELL_ROOT_ID} .ge-shell-spin:active { transform:scale(.94); }
|
|
54
|
+
/* disabled: dim via filter (stays opaque) so the plaque seam doesn't show through the disc */
|
|
55
|
+
#${SHELL_ROOT_ID} .ge-shell-spin[disabled] { filter:grayscale(.4) brightness(.62); box-shadow:none; cursor:default; }
|
|
56
|
+
/* spinning while a spin is in progress */
|
|
57
|
+
#${SHELL_ROOT_ID} .ge-shell-spin.ge-spinning svg { transform-origin:50% 50%; animation:ge-spin-rot .8s linear infinite; }
|
|
58
|
+
@keyframes ge-spin-rot { to { transform:rotate(360deg); } }
|
|
59
|
+
/* autoplay running: STOP glyph + countdown stacked in the disc */
|
|
60
|
+
#${SHELL_ROOT_ID} .ge-shell-spin.ge-stop { position:relative; }
|
|
61
|
+
#${SHELL_ROOT_ID} .ge-spin-stop { display:flex; } /* STOP glyph at the disc's full size, like SPIN */
|
|
62
|
+
/* no entrance animation: the bar re-renders many times per spin, so any animation here
|
|
63
|
+
would replay each time and make the counter visibly jump. tabular-nums keeps it steady. */
|
|
64
|
+
#${SHELL_ROOT_ID} .ge-spin-count { position:absolute; inset:0; display:flex; align-items:center; justify-content:center;
|
|
65
|
+
font-size:22px; font-weight:800; line-height:1; font-variant-numeric:tabular-nums; }
|
|
66
|
+
|
|
67
|
+
/* BUY BONUS — round accent badge, 2-line label, text pulses + accent glow on hover */
|
|
68
|
+
#${SHELL_ROOT_ID} .ge-shell-buybonus { pointer-events:auto; cursor:pointer; box-sizing:border-box;
|
|
69
|
+
width:80px; height:80px; border-radius:50%; border:3px solid #000; background:var(--shell-accent);
|
|
70
|
+
color:#fff; font-weight:800; letter-spacing:.02em; font-size:13px; line-height:1.08; text-align:center;
|
|
71
|
+
display:flex; align-items:center; justify-content:center; transition:transform .08s ease, box-shadow .12s ease; }
|
|
72
|
+
#${SHELL_ROOT_ID} .ge-shell-buybonus span { display:inline-block; will-change:transform; }
|
|
73
|
+
/* the ticket glyph fills the badge (em-sized off the button's font-size, so it scales per context) */
|
|
74
|
+
#${SHELL_ROOT_ID} .ge-shell-buybonus .ge-bb-tk { display:flex; font-size:3.5em; color:#0b0e16; }
|
|
75
|
+
#${SHELL_ROOT_ID} .ge-shell-buybonus:hover { box-shadow:0 0 11px 1px var(--shell-accent); }
|
|
76
|
+
#${SHELL_ROOT_ID} .ge-shell-buybonus:hover span { animation:ge-bb-pulse .7s ease-in-out infinite; }
|
|
77
|
+
#${SHELL_ROOT_ID} .ge-shell-buybonus:active { transform:scale(.96); }
|
|
78
|
+
#${SHELL_ROOT_ID} .ge-shell-buybonus[disabled] { filter:grayscale(.5) brightness(.72); box-shadow:none; cursor:default; }
|
|
79
|
+
#${SHELL_ROOT_ID} .ge-shell-buybonus[disabled] span { animation:none; }
|
|
80
|
+
@keyframes ge-bb-pulse { 0%,100%{transform:scale(1)} 50%{transform:scale(1.16)} }
|
|
81
|
+
|
|
82
|
+
/* host = bottom-anchored flex column: [win pill (on overflow)] above [the bar] */
|
|
83
|
+
#${SHELL_ROOT_ID} .ge-shell-barhost { position:absolute; left:0; right:0; bottom:0; pointer-events:none;
|
|
84
|
+
display:flex; flex-direction:column; align-items:center; justify-content:flex-end; gap:4px;
|
|
85
|
+
transform-origin:bottom center; }
|
|
86
|
+
/* bottom bar: a row of [BUY BONUS (outside-left)] + [one continuous dark panel] (wide default).
|
|
87
|
+
Capped at 750px and centred (the barhost centres it) so it doesn't stretch edge-to-edge on wide screens. */
|
|
88
|
+
#${SHELL_ROOT_ID} .ge-shell-bottom { width:100%; max-width:850px; box-sizing:border-box; pointer-events:none;
|
|
89
|
+
display:flex; align-items:center; justify-content:flex-start; padding:0 14px 8px; gap:10px; }
|
|
90
|
+
#${SHELL_ROOT_ID} .ge-zone { display:flex; align-items:center; gap:14px; pointer-events:none; }
|
|
91
|
+
#${SHELL_ROOT_ID} .ge-zone > * { pointer-events:auto; }
|
|
92
|
+
#${SHELL_ROOT_ID} .ge-betstep { display:flex; flex-direction:column; gap:2px; }
|
|
93
|
+
/* auto stacked over turbo (far-right column, base/desktop) */
|
|
94
|
+
#${SHELL_ROOT_ID} .ge-autoturbo { display:flex; flex-direction:column; gap:2px; }
|
|
95
|
+
#${SHELL_ROOT_ID} .ge-autoturbo .ge-iconbtn { width:40px; height:30px; }
|
|
96
|
+
|
|
97
|
+
/* mobile (portrait) — two levels: [controls bar] over a small [balance · − bet + · win] info pill */
|
|
98
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-shell-bottom { flex-direction:column; align-items:center; gap:10px; padding:8px 10px 8px; }
|
|
99
|
+
/* level 1 — the dark controls bar (white icons, spin disc, buy badge) */
|
|
100
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls { display:flex; align-items:center; justify-content:space-between;
|
|
101
|
+
width:100%; box-sizing:border-box; height:62px; border-radius:16px; padding:0 18px; background:var(--shell-bar); }
|
|
102
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-iconbtn,
|
|
103
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-rd,
|
|
104
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-rd .ge-lbl { color:#fff; }
|
|
105
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-rd { text-shadow:none; text-align:center; }
|
|
106
|
+
/* Total Win is the one variable-width readout in the controls row — make it the shrinkable slot
|
|
107
|
+
(min-width:0 lets flex shrink it below content; overflow:hidden clips the pre-scale value) so a
|
|
108
|
+
large amount SHRINKS its number (fitReadouts) instead of pushing the buttons / scaling the bar.
|
|
109
|
+
The other children (menu/hero/turbo) keep their intrinsic min-width and stay put. */
|
|
110
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-fs-totalwin { min-width:0; overflow:hidden; }
|
|
111
|
+
/* mobile: restore accent hover + autoplay glow (the white rule above out-specifies the base ones) */
|
|
112
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-iconbtn:hover,
|
|
113
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-controls .ge-iconbtn.ge-glow { color:var(--shell-accent); }
|
|
114
|
+
/* level 2 — the small info pill. Readouts are smaller and each can shrink long numbers (see fitReadouts). */
|
|
115
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-info { display:flex; align-items:center; justify-content:space-between;
|
|
116
|
+
width:100%; box-sizing:border-box; height:40px; border-radius:12px; padding:0 14px; gap:10px;
|
|
117
|
+
background:var(--shell-plaque-glass); }
|
|
118
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-info > .ge-rd { flex:1 1 0; min-width:0; overflow:hidden; color:#fff;
|
|
119
|
+
text-shadow:none; font-size:11px; transform-origin:left center; }
|
|
120
|
+
/* Right-align via flex, not text-align: the WIN value can be WIDER than its flex slot, and
|
|
121
|
+
text-align:right only right-aligns content NARROWER than its box — so a wide value stayed
|
|
122
|
+
left-aligned and overflowed the slot's right edge (clipped mid-number). align-items:flex-end
|
|
123
|
+
anchors the value's right edge to the slot right regardless of width, so fitReadouts's
|
|
124
|
+
transform-origin:right then scales it cleanly into view. */
|
|
125
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-info > .ge-win { display:flex; flex-direction:column; align-items:flex-end; text-align:right; }
|
|
126
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-info > .ge-win .ge-rd-val { transform-origin:right center; }
|
|
127
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-info .ge-rd .ge-lbl { color:var(--shell-plaque-label); font-size:8px; margin-bottom:2px; }
|
|
128
|
+
/* bet sits in the middle: − value + (compact), fixed so the numbers don't shove balance/win */
|
|
129
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-betgroup { flex:0 0 auto; display:flex; align-items:center; gap:6px; }
|
|
130
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-betgroup .ge-iconbtn { width:26px; height:26px; color:#fff; font-size:18px; }
|
|
131
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-betgroup .ge-iconbtn:hover { color:var(--shell-accent); }
|
|
132
|
+
/* fixed-width box (sized for up to ~€100,000) so +/- never resizes the bet or shifts balance/win;
|
|
133
|
+
bigger amounts shrink the number instead (fitReadouts) */
|
|
134
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-betgroup .ge-bet-value { flex:0 0 auto; width:76px; font-size:11px;
|
|
135
|
+
text-align:center; color:#fff; text-shadow:none; }
|
|
136
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-m-betgroup .ge-bet-value .ge-rd-val { transform-origin:center; }
|
|
137
|
+
/* SPIN — same as desktop: white disc, black icon + ring; hover tints the icon accent (no fill/ring) */
|
|
138
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-shell-spin { width:84px; height:84px; font-size:66px; border-width:4px;
|
|
139
|
+
background:var(--shell-btn); color:var(--shell-btn-ink); }
|
|
140
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-shell-spin:hover { background:var(--shell-btn); color:var(--shell-accent); box-shadow:none; }
|
|
141
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-shell-buybonus { width:50px; height:50px; font-size:9px; border-width:2px; }
|
|
142
|
+
/* landscape overflow — size the column to content, centre it; JS scales the whole stack to fit */
|
|
143
|
+
#${SHELL_ROOT_ID} .ge-shell-barhost.ge-fit { left:50%; right:auto; width:max-content; max-width:none; }
|
|
144
|
+
#${SHELL_ROOT_ID} .ge-shell-barhost.ge-fit .ge-shell-bottom { width:max-content; }
|
|
145
|
+
|
|
146
|
+
/* full-screen overlays — header (title left, prominent X/back), scrolling body, vh-clamped for popout/mobile.
|
|
147
|
+
Frosted backdrop: the game shows through, blurred, behind a light dark tint (white-on-blur).
|
|
148
|
+
Same glass "plaque" language as the control bar; scheme-independent. */
|
|
149
|
+
#${SHELL_ROOT_ID} .ge-shell-overlay { position:absolute; inset:0; z-index:50; pointer-events:auto;
|
|
150
|
+
background:rgba(12,17,28,.5); backdrop-filter:blur(20px) saturate(120%);
|
|
151
|
+
-webkit-backdrop-filter:blur(20px) saturate(120%);
|
|
152
|
+
display:flex; flex-direction:column; animation:ge-ov-in .16s ease-out; }
|
|
153
|
+
/* SPIN/BUY BONUS use z-index:3 to overlap their plaques; lift the overlay clear above them */
|
|
154
|
+
@keyframes ge-ov-in { from { opacity:0; } to { opacity:1; } }
|
|
155
|
+
#${SHELL_ROOT_ID} .ge-ov-head { flex:0 0 auto; display:flex; align-items:center; gap:8px; padding:6px 10px; }
|
|
156
|
+
#${SHELL_ROOT_ID} .ge-ov-title { flex:1; margin:0; text-align:center; color:#fff; font-weight:800; letter-spacing:.04em;
|
|
157
|
+
text-transform:uppercase; font-size:clamp(13px,2.6vh,16px); }
|
|
158
|
+
#${SHELL_ROOT_ID} .ge-ov-spacer { flex:0 0 auto; width:32px; }
|
|
159
|
+
#${SHELL_ROOT_ID} .ge-ov-nav { flex:0 0 auto; display:flex; align-items:center; justify-content:center;
|
|
160
|
+
width:32px; height:32px; border-radius:9px; cursor:pointer; color:#fff;
|
|
161
|
+
background:var(--shell-plaque-dark); border:none; font-size:18px;
|
|
162
|
+
transition:background .12s ease, color .12s ease; }
|
|
163
|
+
#${SHELL_ROOT_ID} .ge-ov-nav:hover { background:var(--shell-plaque-glass); color:var(--shell-accent); }
|
|
164
|
+
#${SHELL_ROOT_ID} .ge-ov-scroll { flex:1 1 auto; min-height:0; overflow-y:auto; overflow-x:hidden; }
|
|
165
|
+
#${SHELL_ROOT_ID} .ge-ov-body { max-width:800px; margin:0 auto; box-sizing:border-box;
|
|
166
|
+
padding:clamp(6px,2vh,16px) clamp(16px,4vw,24px) clamp(16px,4vh,28px); }
|
|
167
|
+
|
|
168
|
+
/* full-width overlay rows — glass plaque, white-on-dark */
|
|
169
|
+
#${SHELL_ROOT_ID} .ge-ov-row { display:flex; align-items:center; gap:12px; width:100%; box-sizing:border-box;
|
|
170
|
+
padding:clamp(11px,2.2vh,15px) 16px; margin-bottom:10px; border:none;
|
|
171
|
+
border-radius:16px; background:var(--shell-plaque-glass); color:#fff; font-size:14px; font-weight:600; }
|
|
172
|
+
#${SHELL_ROOT_ID} .ge-ov-row .ge-grow { flex:1; text-align:left; }
|
|
173
|
+
#${SHELL_ROOT_ID} button.ge-ov-row { cursor:pointer; font-family:inherit; transition:background .12s ease, color .12s ease; }
|
|
174
|
+
#${SHELL_ROOT_ID} button.ge-ov-row:hover { background:var(--shell-plaque-glass-hover); color:var(--shell-accent); }
|
|
175
|
+
#${SHELL_ROOT_ID} .ge-ov-row.ge-col { flex-direction:column; align-items:stretch; gap:10px; }
|
|
176
|
+
#${SHELL_ROOT_ID} .ge-ov-row .ge-row-head { display:flex; justify-content:space-between; align-items:center; }
|
|
177
|
+
#${SHELL_ROOT_ID} .ge-ov-row .ge-row-head .ge-val { color:var(--shell-plaque-label); font-variant-numeric:tabular-nums; font-weight:700; }
|
|
178
|
+
#${SHELL_ROOT_ID} .ge-slider { width:100%; accent-color:var(--shell-accent); }
|
|
179
|
+
#${SHELL_ROOT_ID} .ge-toggle { flex:0 0 auto; width:42px; height:24px; border-radius:999px; background:var(--shell-plaque-line);
|
|
180
|
+
border:none; cursor:pointer; position:relative; }
|
|
181
|
+
#${SHELL_ROOT_ID} .ge-toggle.ge-on { background:var(--shell-accent); }
|
|
182
|
+
#${SHELL_ROOT_ID} .ge-toggle i { position:absolute; top:2px; left:2px; width:20px; height:20px; border-radius:50%;
|
|
183
|
+
background:#fff; transition:left .12s ease; }
|
|
184
|
+
#${SHELL_ROOT_ID} .ge-toggle.ge-on i { left:20px; }
|
|
185
|
+
/* sound on/off — speaker icon button (replaces the toggle) */
|
|
186
|
+
#${SHELL_ROOT_ID} .ge-snd { pointer-events:auto; cursor:pointer; border:none; background:none; padding:0;
|
|
187
|
+
width:36px; height:36px; display:flex; align-items:center; justify-content:center; font-size:24px;
|
|
188
|
+
color:#fff; transition:color .12s ease, transform .08s ease; }
|
|
189
|
+
#${SHELL_ROOT_ID} .ge-snd:not(.ge-active) { color:var(--shell-plaque-label); }
|
|
190
|
+
#${SHELL_ROOT_ID} .ge-snd:hover { color:var(--shell-accent); }
|
|
191
|
+
#${SHELL_ROOT_ID} .ge-snd:active { transform:scale(.92); }
|
|
192
|
+
|
|
193
|
+
/* game info — each section is its own glass plaque; body text sized for comfortable reading */
|
|
194
|
+
#${SHELL_ROOT_ID} .ge-gi-sec { margin-bottom:12px; background:var(--shell-plaque-glass);
|
|
195
|
+
border-radius:16px; padding:16px 18px; }
|
|
196
|
+
#${SHELL_ROOT_ID} .ge-gi-sec h3 { color:var(--shell-plaque-label); font-size:11px; letter-spacing:.14em;
|
|
197
|
+
text-transform:uppercase; margin:0 0 12px; }
|
|
198
|
+
#${SHELL_ROOT_ID} .ge-gi-sec p { color:rgba(255,255,255,.88); font-size:15px; line-height:1.6; margin:0; }
|
|
199
|
+
#${SHELL_ROOT_ID} .ge-gi-version { text-align:center; color:var(--shell-muted); font-size:11px;
|
|
200
|
+
letter-spacing:.08em; opacity:.7; margin:4px 0 2px; }
|
|
201
|
+
|
|
202
|
+
/* hotkeys — keycap chips → localized action label, mirrors controls row layout */
|
|
203
|
+
#${SHELL_ROOT_ID} .ge-gi-hk-block { display:flex; flex-direction:column; }
|
|
204
|
+
#${SHELL_ROOT_ID} .ge-gi-hk { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:9px 0; }
|
|
205
|
+
#${SHELL_ROOT_ID} .ge-gi-hk + .ge-gi-hk { border-top:1px solid var(--shell-plaque-line); }
|
|
206
|
+
#${SHELL_ROOT_ID} .ge-gi-hk-chips { display:flex; align-items:center; flex-wrap:wrap; gap:4px; flex:0 0 auto; }
|
|
207
|
+
#${SHELL_ROOT_ID} .ge-gi-hk-combo { display:inline-flex; align-items:center; gap:4px; }
|
|
208
|
+
#${SHELL_ROOT_ID} .ge-gi-hk-chip { display:inline-flex; align-items:center; justify-content:center;
|
|
209
|
+
padding:2px 7px; border-radius:6px; border:1px solid var(--shell-plaque-line);
|
|
210
|
+
background:var(--shell-plaque-dark); color:#fff;
|
|
211
|
+
font-family:'SFMono-Regular',Consolas,'Liberation Mono',Menlo,monospace; font-size:12px;
|
|
212
|
+
font-weight:600; line-height:1.5; white-space:nowrap; min-width:1.6em; text-align:center; }
|
|
213
|
+
#${SHELL_ROOT_ID} .ge-gi-hk-sep { color:var(--shell-plaque-label); font-size:11px; padding:0 1px; }
|
|
214
|
+
#${SHELL_ROOT_ID} .ge-gi-hk-sep2 { color:var(--shell-plaque-label); font-size:11px; padding:0 4px; }
|
|
215
|
+
#${SHELL_ROOT_ID} .ge-gi-hk-tx { color:rgba(255,255,255,.88); font-size:14px; font-weight:600; text-align:right; flex:1; }
|
|
216
|
+
|
|
217
|
+
/* controls — two blocks (gameplay / menu & info), icon/name/description per control */
|
|
218
|
+
#${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); }
|
|
219
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl-block-h { color:var(--shell-plaque-label); font-size:11px; letter-spacing:.12em;
|
|
220
|
+
text-transform:uppercase; margin:8px 0 2px; font-weight:700; }
|
|
221
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl { display:flex; align-items:center; gap:14px; padding:9px 0; }
|
|
222
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl + .ge-gi-ctl { border-top:1px solid var(--shell-plaque-line); }
|
|
223
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl-ic { flex:0 0 auto; width:48px; height:48px; display:flex; align-items:center;
|
|
224
|
+
justify-content:center; font-size:26px; color:#fff; }
|
|
225
|
+
/* bet shows both chevrons, stacked & smaller */
|
|
226
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl-ic--bet { flex-direction:column; font-size:18px; gap:0; }
|
|
227
|
+
/* buy bonus draws the real control-bar badge, scaled down & non-interactive */
|
|
228
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl-ic .ge-shell-buybonus { width:46px; height:46px; font-size:8px; border-width:2px; pointer-events:none; }
|
|
229
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl-tx { display:flex; flex-direction:column; gap:2px; }
|
|
230
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl-tx b { color:#fff; font-size:15px; font-weight:700; }
|
|
231
|
+
#${SHELL_ROOT_ID} .ge-gi-ctl-tx span { color:rgba(255,255,255,.7); font-size:13px; line-height:1.4; }
|
|
232
|
+
|
|
233
|
+
/* paytable — cards: symbol image on top, name, then win tiers "<count> x<mult>" */
|
|
234
|
+
#${SHELL_ROOT_ID} .ge-gi-pt-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(120px,1fr)); gap:10px; }
|
|
235
|
+
#${SHELL_ROOT_ID} .ge-gi-pt-card { display:flex; flex-direction:column; align-items:center; gap:8px;
|
|
236
|
+
background:var(--shell-plaque-dark); border-radius:14px; padding:14px 12px; }
|
|
237
|
+
#${SHELL_ROOT_ID} .ge-gi-pt-sym { display:flex; flex-direction:column; align-items:center; gap:5px; min-width:0; }
|
|
238
|
+
#${SHELL_ROOT_ID} .ge-gi-pt-sym img { width:56px; height:56px; object-fit:contain; }
|
|
239
|
+
#${SHELL_ROOT_ID} .ge-gi-pt-sym span { color:#fff; font-size:13px; font-weight:700; text-transform:uppercase; letter-spacing:.05em; }
|
|
240
|
+
#${SHELL_ROOT_ID} .ge-gi-pt-wins { display:flex; flex-direction:column; align-items:stretch; gap:2px; width:100%; }
|
|
241
|
+
#${SHELL_ROOT_ID} .ge-gi-pt-win { display:flex; justify-content:space-between; align-items:baseline; gap:14px;
|
|
242
|
+
font-size:13.5px; font-variant-numeric:tabular-nums; white-space:nowrap; }
|
|
243
|
+
#${SHELL_ROOT_ID} .ge-gi-pt-win i { color:var(--shell-plaque-label); font-style:normal; }
|
|
244
|
+
#${SHELL_ROOT_ID} .ge-gi-pt-win b { color:var(--shell-accent); font-weight:700; }
|
|
245
|
+
|
|
246
|
+
/* wins — accent-filled mini-grid(s); classic = one per line, cluster/anywhere = one, ways = two */
|
|
247
|
+
#${SHELL_ROOT_ID} .ge-gi-pl-grid { display:grid; grid-template-columns:repeat(auto-fill, minmax(72px,1fr)); gap:12px; }
|
|
248
|
+
#${SHELL_ROOT_ID} .ge-gi-pl-item { display:flex; flex-direction:column; align-items:center; gap:6px; }
|
|
249
|
+
#${SHELL_ROOT_ID} .ge-gi-pl-svg { width:100%; height:auto; max-width:140px; }
|
|
250
|
+
#${SHELL_ROOT_ID} .ge-gi-pl-cell { fill:var(--shell-plaque-line); }
|
|
251
|
+
#${SHELL_ROOT_ID} .ge-gi-pl-on { fill:var(--shell-accent); }
|
|
252
|
+
#${SHELL_ROOT_ID} .ge-gi-pl-cap { color:#fff; font-size:13px; font-weight:700; }
|
|
253
|
+
#${SHELL_ROOT_ID} .ge-gi-win-row { display:flex; align-items:flex-start; gap:16px; flex-wrap:wrap; }
|
|
254
|
+
#${SHELL_ROOT_ID} .ge-gi-win-row .ge-gi-pl-svg { flex:0 0 auto; width:140px; }
|
|
255
|
+
#${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; }
|
|
256
|
+
#${SHELL_ROOT_ID} .ge-gi-win-two { display:flex; gap:22px; flex-wrap:wrap; }
|
|
257
|
+
#${SHELL_ROOT_ID} .ge-gi-win-col { display:flex; flex-direction:column; align-items:center; gap:6px; }
|
|
258
|
+
#${SHELL_ROOT_ID} .ge-gi-win-col .ge-gi-pl-svg { width:140px; }
|
|
259
|
+
#${SHELL_ROOT_ID} .ge-gi-win-tag { font-size:12px; font-weight:700; }
|
|
260
|
+
#${SHELL_ROOT_ID} .ge-gi-win-ok { color:#4ade80; }
|
|
261
|
+
#${SHELL_ROOT_ID} .ge-gi-win-no { color:#f87171; }
|
|
262
|
+
#${SHELL_ROOT_ID} .ge-gi-win-badge { display:inline-block; margin-left:10px; padding:2px 8px; border-radius:999px;
|
|
263
|
+
font-size:11px; font-weight:700; letter-spacing:.02em; color:var(--shell-accent);
|
|
264
|
+
background:var(--shell-plaque-dark); vertical-align:middle; }
|
|
265
|
+
|
|
266
|
+
/* modes — comparison cards */
|
|
267
|
+
#${SHELL_ROOT_ID} .ge-gi-modes { display:flex; flex-direction:column; }
|
|
268
|
+
#${SHELL_ROOT_ID} .ge-gi-mode { padding:12px 0; }
|
|
269
|
+
#${SHELL_ROOT_ID} .ge-gi-mode + .ge-gi-mode { border-top:1px solid var(--shell-plaque-line); }
|
|
270
|
+
#${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; }
|
|
271
|
+
#${SHELL_ROOT_ID} .ge-gi-mode-h { color:#fff; font-size:16px; font-weight:800; }
|
|
272
|
+
#${SHELL_ROOT_ID} .ge-gi-mode-stats { display:flex; flex-wrap:wrap; gap:14px; }
|
|
273
|
+
#${SHELL_ROOT_ID} .ge-gi-mode-st { display:flex; align-items:baseline; gap:5px; }
|
|
274
|
+
#${SHELL_ROOT_ID} .ge-gi-mode-st span { color:var(--shell-plaque-label); font-size:10px; letter-spacing:.1em; text-transform:uppercase; }
|
|
275
|
+
#${SHELL_ROOT_ID} .ge-gi-mode-st b { color:#fff; font-size:14px; font-weight:800; font-variant-numeric:tabular-nums; }
|
|
276
|
+
#${SHELL_ROOT_ID} .ge-gi-mode-desc { color:rgba(255,255,255,.78); font-size:14px; line-height:1.5; margin:0; }
|
|
277
|
+
/* shapes — row list (grid illustration left, name + description right), modes-style text */
|
|
278
|
+
#${SHELL_ROOT_ID} .ge-gi-shapes { display:flex; flex-direction:column; }
|
|
279
|
+
#${SHELL_ROOT_ID} .ge-gi-shape { display:flex; align-items:center; gap:16px; padding:12px 0; }
|
|
280
|
+
#${SHELL_ROOT_ID} .ge-gi-shape + .ge-gi-shape { border-top:1px solid var(--shell-plaque-line); }
|
|
281
|
+
#${SHELL_ROOT_ID} .ge-gi-shape .ge-gi-pl-svg { flex:0 0 auto; width:96px; }
|
|
282
|
+
#${SHELL_ROOT_ID} .ge-gi-shape-tx { flex:1; min-width:0; display:flex; flex-direction:column; gap:4px; }
|
|
283
|
+
#${SHELL_ROOT_ID} .ge-gi-custom { color:rgba(255,255,255,.88); font-size:15px; line-height:1.6; }
|
|
284
|
+
|
|
285
|
+
/* buy bonus cards — art-forward, centred, flat (no gradients); --card-acc/--card-ink per card.
|
|
286
|
+
order: title → thumb → description → (spacer) → volatility → price → full-bleed button. */
|
|
287
|
+
/* wide: horizontal scrolling strip. Cards scale PROPORTIONALLY with the viewport — every
|
|
288
|
+
dimension is in em-units, so the single font-size knob shrinks the whole card uniformly
|
|
289
|
+
(like the control bar's fit-scale), keeping small popouts readable. mobile: vertical stack. */
|
|
290
|
+
/* the buy-bonus scroll area is a SIZE CONTAINER, so the cards' cqh units measure the overlay
|
|
291
|
+
(the popout frame) and not the browser window — cards fit without any vertical scroll. */
|
|
292
|
+
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-ov-scroll { container-type:size; }
|
|
293
|
+
/* Popout / landscape: the horizontal card strip must be the ONLY scroll axis. The cqh-sized cards
|
|
294
|
+
are meant to fit the overlay height (no vertical scroll), but the old 7px font floor stopped them
|
|
295
|
+
shrinking on the tiniest popouts — so they spilled past the frame and the overlay grew a SECOND,
|
|
296
|
+
vertical scrollbar (scrollable in both directions). Two changes keep it single-axis:
|
|
297
|
+
1. the card font floor drops (below) so cards actually fit the frame height; and
|
|
298
|
+
2. vertical scrolling is locked off as a belt-and-braces guard, with the strip centred (the body
|
|
299
|
+
fills the frame and centres the grid) so any sub-pixel slack splits evenly instead of clipping
|
|
300
|
+
the price/CTA off the bottom.
|
|
301
|
+
This mirrors the pixi shell, which masks the cards and drag-scrolls on the X axis alone. (Centring
|
|
302
|
+
lives on the body, not the scroll box, so the grid keeps its width and its own X-scroll.) */
|
|
303
|
+
#${SHELL_ROOT_ID}:not(.ge-mobile) [data-ge="buybonus-overlay"] .ge-ov-scroll { overflow-y:hidden; }
|
|
304
|
+
#${SHELL_ROOT_ID}:not(.ge-mobile) [data-ge="buybonus-overlay"] .ge-ov-body {
|
|
305
|
+
min-height:100%; box-sizing:border-box; display:flex; flex-direction:column; justify-content:center; }
|
|
306
|
+
/* buy-bonus uses the FULL overlay width (no 800px centre cap) so the card row isn't cropped at
|
|
307
|
+
the sides; small horizontal padding keeps the cards off the screen edges. */
|
|
308
|
+
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-ov-body { max-width:none; padding:clamp(6px,2.5cqh,16px) clamp(12px,3vw,28px); }
|
|
309
|
+
#${SHELL_ROOT_ID} .ge-bb-grid { display:flex; gap:14px; justify-content:safe center; overflow-x:auto; overflow-y:hidden; padding-bottom:6px;
|
|
310
|
+
scroll-snap-type:x proximity; -webkit-overflow-scrolling:touch; }
|
|
311
|
+
/* the one knob that scales the whole card (its whole layout is em-relative). It is the SMALLER of two
|
|
312
|
+
fits, so the card is always fully visible:
|
|
313
|
+
• 3.4cqh — height: the card stays inside the band between the header and the bet footer (cqh
|
|
314
|
+
measures the overlay popout frame, not the browser window);
|
|
315
|
+
• 84cqw / (N*18) — width: the N cards (each 18em) fit the frame width side-by-side instead of
|
|
316
|
+
overflowing into an X-scroll. --ge-bb-n is the live card count, set in BuyBonus.ts.
|
|
317
|
+
Floor is deliberately tiny: on a 400×225 popout a fully visible card beats a bigger clipped one. */
|
|
318
|
+
#${SHELL_ROOT_ID} .ge-bb-grid .ge-bonus-card { flex:0 0 18em; scroll-snap-align:start;
|
|
319
|
+
font-size:clamp(4px, min(3.4cqh, calc(84cqw / (var(--ge-bb-n,3) * 18))), 12px); }
|
|
320
|
+
/* mobile: vertical stack at a fixed, readable size — scroll the list; only shrink if the card would
|
|
321
|
+
be wider than the frame (very narrow viewport), so it never overflows horizontally. */
|
|
322
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-bb-grid { display:flex; flex-direction:column; gap:14px; overflow:visible; }
|
|
323
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-bb-grid .ge-bonus-card { flex:0 0 auto; font-size:min(12px, calc(92cqw / 18)); }
|
|
324
|
+
#${SHELL_ROOT_ID} .ge-bonus-card { display:flex; flex-direction:column; border-radius:1.4em; overflow:hidden;
|
|
325
|
+
background:var(--shell-plaque-glass); border:1px solid var(--shell-plaque-line); color:#fff; text-align:center;
|
|
326
|
+
pointer-events:auto; cursor:pointer; transition:box-shadow .12s ease, background .12s ease; }
|
|
327
|
+
#${SHELL_ROOT_ID} .ge-bonus-card:hover:not(.ge-bonus-off) {
|
|
328
|
+
box-shadow:0 0 0 1px var(--card-acc), 0 12px 34px -12px var(--card-acc); }
|
|
329
|
+
#${SHELL_ROOT_ID} .ge-bonus-card--kbd-focus:not(.ge-bonus-off) {
|
|
330
|
+
box-shadow:0 0 0 1px var(--card-acc), 0 12px 34px -12px var(--card-acc); }
|
|
331
|
+
/* custom card (BonusOption.custom): keep grid sizing + accent vars, drop the default chrome so the game owns the UI */
|
|
332
|
+
#${SHELL_ROOT_ID} .ge-bonus-card--custom { background:none; border:none; cursor:default; }
|
|
333
|
+
#${SHELL_ROOT_ID} .ge-bonus-body { display:flex; flex-direction:column; align-items:center; flex:1; padding:1.25em 1.1em .9em; }
|
|
334
|
+
#${SHELL_ROOT_ID} .ge-bonus-title { font-size:1.3em; font-weight:800; letter-spacing:.04em; text-transform:uppercase;
|
|
335
|
+
color:var(--card-acc); margin-bottom:.75em; }
|
|
336
|
+
#${SHELL_ROOT_ID} .ge-bonus-thumb { height:6.2em; display:flex; align-items:center; justify-content:center; margin-bottom:.7em; }
|
|
337
|
+
#${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)); }
|
|
338
|
+
#${SHELL_ROOT_ID} .ge-bonus-thumb-ph { font-size:3.5em; color:var(--card-acc); opacity:.85; display:flex; }
|
|
339
|
+
#${SHELL_ROOT_ID} .ge-bonus-desc { font-size:.96em; line-height:1.45; color:rgba(255,255,255,.82); }
|
|
340
|
+
#${SHELL_ROOT_ID} .ge-bonus-spacer { flex:1; min-height:.7em; }
|
|
341
|
+
#${SHELL_ROOT_ID} .ge-bonus-vol { display:flex; justify-content:center; align-items:center; gap:0;
|
|
342
|
+
font-size:2.2em; line-height:1; margin-bottom:.55em; }
|
|
343
|
+
#${SHELL_ROOT_ID} .ge-bonus-vol-on, #${SHELL_ROOT_ID} .ge-bonus-vol-off { display:inline-flex; gap:0; }
|
|
344
|
+
/* Volatility bolts get the same outline+fill treatment as turbo: white fill + a 2px OUTER ring
|
|
345
|
+
(paint-order:stroke + double width). Active = accent ring; inactive = black ring, dimmed. */
|
|
346
|
+
#${SHELL_ROOT_ID} .ge-bonus-vol svg { overflow:visible; }
|
|
347
|
+
#${SHELL_ROOT_ID} .ge-bonus-vol svg path { paint-order:stroke; fill:#fff; stroke-width:1; stroke-linejoin:round; }
|
|
348
|
+
#${SHELL_ROOT_ID} .ge-bonus-vol-on svg path { stroke:var(--card-acc); fill:var(--card-acc); }
|
|
349
|
+
#${SHELL_ROOT_ID} .ge-bonus-vol-off svg path { stroke:#000; fill:#000; }
|
|
350
|
+
#${SHELL_ROOT_ID} .ge-bonus-vol-off { opacity:.5; }
|
|
351
|
+
#${SHELL_ROOT_ID} .ge-bonus-price { font-size:1.6em; font-weight:800; color:#fff; font-variant-numeric:tabular-nums; white-space:nowrap; }
|
|
352
|
+
#${SHELL_ROOT_ID} .ge-bonus-cta { width:100%; border:none; padding:1.15em; font-weight:800; font-size:1.05em;
|
|
353
|
+
letter-spacing:.05em; text-transform:uppercase; cursor:pointer; background:var(--card-acc); color:var(--card-ink);
|
|
354
|
+
transition:filter .12s ease; }
|
|
355
|
+
#${SHELL_ROOT_ID} .ge-bonus-cta:hover:not([disabled]) { filter:brightness(1.06); }
|
|
356
|
+
/* unaffordable / disabled */
|
|
357
|
+
#${SHELL_ROOT_ID} .ge-bonus-card.ge-bonus-off { opacity:.62; cursor:default; }
|
|
358
|
+
#${SHELL_ROOT_ID} .ge-bonus-off .ge-bonus-title { color:rgba(255,255,255,.6); }
|
|
359
|
+
#${SHELL_ROOT_ID} .ge-bonus-off .ge-bonus-vol-on { color:rgba(255,255,255,.4); }
|
|
360
|
+
#${SHELL_ROOT_ID} .ge-bonus-off .ge-bonus-cta { background:#8d939e; color:#3a3f47; cursor:default; }
|
|
361
|
+
/* buy-bonus confirm — bonus preview inside the shared sheet card (see .ge-sheet-actions below) */
|
|
362
|
+
#${SHELL_ROOT_ID} .ge-confirm-preview { display:flex; flex-direction:column; align-items:center; gap:10px; text-align:center; }
|
|
363
|
+
#${SHELL_ROOT_ID} .ge-confirm-preview .ge-bonus-thumb,
|
|
364
|
+
#${SHELL_ROOT_ID} .ge-confirm-preview .ge-bonus-vol { margin:0; }
|
|
365
|
+
/* effective-bet readout while a feature is active (value colour set inline to the feature accent) */
|
|
366
|
+
#${SHELL_ROOT_ID} .ge-rd.ge-bet-feature { font-weight:800; }
|
|
367
|
+
/* bet control — compact pill in a thin footer at the screen bottom (footer only as tall as the pill) */
|
|
368
|
+
#${SHELL_ROOT_ID} .ge-bb-betbar { flex:0 0 auto; display:flex; justify-content:center; padding:4px; }
|
|
369
|
+
#${SHELL_ROOT_ID} .ge-bb-betpill { display:inline-flex; align-items:center; gap:4px;
|
|
370
|
+
background:var(--shell-plaque-dark); border-radius:999px; padding:3px 5px; }
|
|
371
|
+
#${SHELL_ROOT_ID} .ge-bb-betstep { pointer-events:auto; cursor:pointer; width:32px; height:32px; border:none; border-radius:50%;
|
|
372
|
+
background:none; color:#fff; font-size:20px; display:flex; align-items:center; justify-content:center;
|
|
373
|
+
transition:color .12s ease, transform .08s ease; }
|
|
374
|
+
#${SHELL_ROOT_ID} .ge-bb-betstep:hover { color:var(--shell-accent); }
|
|
375
|
+
#${SHELL_ROOT_ID} .ge-bb-betstep:active { transform:scale(.9); }
|
|
376
|
+
#${SHELL_ROOT_ID} .ge-bb-betval { min-width:80px; text-align:center; padding:0 2px; }
|
|
377
|
+
#${SHELL_ROOT_ID} .ge-bb-betval span { display:block; font-size:7px; font-weight:600; letter-spacing:.14em; text-transform:uppercase;
|
|
378
|
+
color:var(--shell-plaque-label); }
|
|
379
|
+
#${SHELL_ROOT_ID} .ge-bb-betval b { font-size:14px; font-weight:800; font-variant-numeric:tabular-nums; color:#fff; }
|
|
380
|
+
/* Popout S (short landscape): the header (title + ✕) and the bet footer are fixed-px and dwarf the
|
|
381
|
+
shrunk cards. Scale the buy-bonus chrome down with the FRAME height. Units are cqh (the overlay is a
|
|
382
|
+
size container below), NOT vh — vh tracks the browser window, which only equals the frame inside the
|
|
383
|
+
Stake iframe, so it wouldn't shrink in the demo's device-frame view. Coefficients hit the normal cap
|
|
384
|
+
by ~450px tall (Popout L) and shrink below that, to a readable floor at Popout S (225px). */
|
|
385
|
+
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] { container:ge-bb-frame / size; }
|
|
386
|
+
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-ov-head { padding:clamp(3px,1.33cqh,6px) 10px; }
|
|
387
|
+
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-ov-title { font-size:clamp(11px,3.5cqh,16px); }
|
|
388
|
+
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-ov-spacer { width:clamp(24px,7cqh,32px); }
|
|
389
|
+
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-ov-nav { width:clamp(24px,7cqh,32px); height:clamp(24px,7cqh,32px);
|
|
390
|
+
font-size:clamp(14px,4cqh,18px); border-radius:clamp(7px,2cqh,9px); }
|
|
391
|
+
/* the bet pill read too large next to the shrunk cards — size it ~0.8× across the board (the floors
|
|
392
|
+
stay readable; the maxima are what dominate on Popout L / wide frames). */
|
|
393
|
+
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-bb-betbar { padding:clamp(2px,.75cqh,3px); }
|
|
394
|
+
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-bb-betpill { padding:clamp(2px,.55cqh,3px) clamp(3px,.9cqh,4px); }
|
|
395
|
+
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-bb-betstep { width:clamp(20px,5.7cqh,26px); height:clamp(20px,5.7cqh,26px);
|
|
396
|
+
font-size:clamp(12px,3.5cqh,16px); }
|
|
397
|
+
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-bb-betval { min-width:clamp(50px,14cqh,66px); }
|
|
398
|
+
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-bb-betval b { font-size:clamp(9px,2.5cqh,11px); }
|
|
399
|
+
#${SHELL_ROOT_ID} [data-ge="buybonus-overlay"] .ge-bb-betval span { font-size:clamp(5px,1.25cqh,6px); }
|
|
400
|
+
|
|
401
|
+
/* ═══ desktop control bar — one continuous dark panel; white-disc buttons; BUY BONUS outside-left ═══ */
|
|
402
|
+
/* the dark surface fills the row width (BUY BONUS sits outside, to its left) */
|
|
403
|
+
#${SHELL_ROOT_ID} .ge-bar-panel { flex:1 1 auto; min-width:0; box-sizing:border-box; display:flex;
|
|
404
|
+
align-items:center; justify-content:space-between; gap:10px; height:68px; padding:0 14px;
|
|
405
|
+
border-radius:12px; background:var(--shell-bar); }
|
|
406
|
+
#${SHELL_ROOT_ID} .ge-bar-panel .ge-zone-left, #${SHELL_ROOT_ID} .ge-bar-panel .ge-zone-right { gap:12px; min-width:0; }
|
|
407
|
+
#${SHELL_ROOT_ID} .ge-bar-panel .ge-rd { color:#fff; text-shadow:none; }
|
|
408
|
+
#${SHELL_ROOT_ID} .ge-bar-panel .ge-rd .ge-lbl { color:var(--shell-plaque-label); }
|
|
409
|
+
#${SHELL_ROOT_ID} .ge-bar-panel .ge-balance { min-width:142px; } /* fixed-wide so the digits don't reflow the row */
|
|
410
|
+
|
|
411
|
+
/* white-disc buttons: black icon + black ring; hover tints just the ICON accent (no outer ring) */
|
|
412
|
+
#${SHELL_ROOT_ID} .ge-bar-panel .ge-iconbtn { width:38px; height:38px; border-radius:50%; border:2px solid #000;
|
|
413
|
+
background:var(--shell-btn); color:var(--shell-btn-ink); font-size:19px; }
|
|
414
|
+
/* hover + active/engaged tint the icon AND the ring accent (menu/steppers have no border, so unaffected) */
|
|
415
|
+
#${SHELL_ROOT_ID} .ge-bar-panel .ge-iconbtn:hover { background:var(--shell-btn); color:var(--shell-accent); border-color:var(--shell-accent); }
|
|
416
|
+
#${SHELL_ROOT_ID} .ge-bar-panel .ge-iconbtn.ge-active,
|
|
417
|
+
#${SHELL_ROOT_ID} .ge-bar-panel .ge-iconbtn.ge-glow { color:var(--shell-accent); border-color:var(--shell-accent); }
|
|
418
|
+
/* autoplay glyph reads small in its disc — enlarge it */
|
|
419
|
+
#${SHELL_ROOT_ID} .ge-bar-panel .ge-iconbtn[data-ge="autoplay"] { font-size:25px; }
|
|
420
|
+
/* MENU stays a plain (borderless) icon — just larger; no white disc */
|
|
421
|
+
#${SHELL_ROOT_ID} .ge-bar-panel .ge-iconbtn[data-ge="menu"] { background:none; border:none;
|
|
422
|
+
width:auto; height:auto; color:#fff; font-size:30px; }
|
|
423
|
+
#${SHELL_ROOT_ID} .ge-bar-panel .ge-iconbtn[data-ge="menu"]:hover { background:none; color:var(--shell-accent); }
|
|
424
|
+
/* turbo at rest reads dimmed — clearly off, but lighter than a true [disabled] control */
|
|
425
|
+
#${SHELL_ROOT_ID} [data-ge="turbo"]:not(.ge-active) { }
|
|
426
|
+
#${SHELL_ROOT_ID} [data-ge="turbo"]:not(.ge-active):hover { opacity:1; }
|
|
427
|
+
|
|
428
|
+
/* BET group — value + tight stacked chevrons, parked close to the divider (no extra air) */
|
|
429
|
+
#${SHELL_ROOT_ID} .ge-betgroup { display:flex; align-items:center; gap:8px; }
|
|
430
|
+
/* the value box is a FIXED width (sized to hold up to ~€100,000) so changing the stake never shifts
|
|
431
|
+
the steppers/divider/SPIN; bigger amounts shrink the value span instead — see fitReadouts(). */
|
|
432
|
+
#${SHELL_ROOT_ID} .ge-betgroup .ge-bet-value { flex:0 0 auto; width:90px; border-radius:0; }
|
|
433
|
+
/* every readout's value is an inline-block span so it can be measured & transform-scaled to fit;
|
|
434
|
+
numerals render in OswaldNum (the digit-only subset), text glyphs fall back to Inter */
|
|
435
|
+
#${SHELL_ROOT_ID} .ge-rd-val { display:inline-block; white-space:nowrap; transform-origin:left center;
|
|
436
|
+
font-family:'OswaldNum','Inter',sans-serif; font-size:1.15em; }
|
|
437
|
+
#${SHELL_ROOT_ID} .ge-spin-count { font-family:'OswaldNum','Inter',sans-serif; }
|
|
438
|
+
#${SHELL_ROOT_ID} .ge-betgroup .ge-betstep { gap:2px; }
|
|
439
|
+
/* the +/- stay plain icons (no white disc), just bolder/bigger than the default */
|
|
440
|
+
#${SHELL_ROOT_ID} .ge-betgroup .ge-betstep .ge-iconbtn { background:none; border:none; width:30px; height:22px;
|
|
441
|
+
color:#fff; font-size:22px; }
|
|
442
|
+
#${SHELL_ROOT_ID} .ge-betgroup .ge-betstep .ge-iconbtn:hover { background:none; color:var(--shell-accent); }
|
|
443
|
+
/* tappable stake readout opens the bet picker */
|
|
444
|
+
#${SHELL_ROOT_ID} .ge-betbtn { pointer-events:auto; cursor:pointer; border-radius:8px; transition:color .12s ease; }
|
|
445
|
+
#${SHELL_ROOT_ID} .ge-betbtn:hover { color:var(--shell-accent); }
|
|
446
|
+
#${SHELL_ROOT_ID} .ge-betbtn.ge-disabled { pointer-events:none; }
|
|
447
|
+
|
|
448
|
+
/* divider — short, with air above & below (not full bar height) */
|
|
449
|
+
#${SHELL_ROOT_ID} .ge-pl-divider { align-self:center; flex:0 0 auto; width:1px; height:22px;
|
|
450
|
+
background:var(--shell-plaque-line); }
|
|
451
|
+
|
|
452
|
+
/* SPIN + auto/turbo cluster — SPIN is the hero white disc: larger than the bar so it pops above/below */
|
|
453
|
+
#${SHELL_ROOT_ID} .ge-spinwrap { display:flex; align-items:center; gap:8px; }
|
|
454
|
+
#${SHELL_ROOT_ID} .ge-bar-panel .ge-shell-spin { width:84px; height:84px; font-size:65px; border-width:4px;
|
|
455
|
+
background:var(--shell-btn); color:var(--shell-btn-ink); position:relative; z-index:3; }
|
|
456
|
+
#${SHELL_ROOT_ID} .ge-bar-panel .ge-shell-spin:hover { background:var(--shell-btn); color:var(--shell-accent); box-shadow:none; }
|
|
457
|
+
|
|
458
|
+
/* FS hero — replaces SPIN in free spins: same white/black-ring hero, but a rounded rectangle showing
|
|
459
|
+
the spins counter. Pops above/below the bar like SPIN (height matches the disc). */
|
|
460
|
+
#${SHELL_ROOT_ID} .ge-fs-hero { pointer-events:auto; box-sizing:border-box; height:84px; min-width:96px;
|
|
461
|
+
padding:0 18px; border:4px solid #000; border-radius:20px; background:var(--shell-btn); color:var(--shell-btn-ink);
|
|
462
|
+
display:flex; flex-direction:column; align-items:center; justify-content:center; gap:3px;
|
|
463
|
+
position:relative; z-index:3; }
|
|
464
|
+
/* the label wraps to ≤2 lines so long localizations ("Бесплатные вращения") fit the narrow hero */
|
|
465
|
+
#${SHELL_ROOT_ID} .ge-fs-hero .ge-fs-lbl { font-weight:700; font-size:9px; line-height:1.1; letter-spacing:.08em;
|
|
466
|
+
text-transform:uppercase; color:#454c5a; text-align:center; max-width:11ch; }
|
|
467
|
+
#${SHELL_ROOT_ID} .ge-fs-hero .ge-fs-num { font-family:'OswaldNum','Inter',sans-serif; font-weight:700;
|
|
468
|
+
font-size:30px; line-height:1; font-variant-numeric:tabular-nums; white-space:nowrap; }
|
|
469
|
+
|
|
470
|
+
/* BUY BONUS — floats outside-left of the bar; keeps its accent disc + hover ring (the one exception).
|
|
471
|
+
relative/z-index so it stacks predictably under the full-screen overlays (see overlay-stacking test). */
|
|
472
|
+
#${SHELL_ROOT_ID} .ge-shell-bottom > .ge-shell-buybonus { flex:0 0 auto; width:62px; height:62px;
|
|
473
|
+
font-size:11px; border-width:3px; position:relative; z-index:3; }
|
|
474
|
+
|
|
475
|
+
/* WIN — a plain readout (same as balance/bet), grouped on the left with the other info */
|
|
476
|
+
#${SHELL_ROOT_ID} .ge-bar-panel .ge-win { pointer-events:none; }
|
|
477
|
+
|
|
478
|
+
/* centred CARD modal — opaque card on a frosted backdrop, accent title heading at the top
|
|
479
|
+
(no ✕), content, full-bleed footer button(s). Shared by the buy-bonus confirm AND the
|
|
480
|
+
bet/autoplay pickers so every centred modal is the same type. Close via backdrop click. */
|
|
481
|
+
#${SHELL_ROOT_ID} .ge-sheet { position:absolute; inset:0; z-index:60; pointer-events:auto; display:flex;
|
|
482
|
+
align-items:center; justify-content:center; padding:clamp(10px,4vh,24px); box-sizing:border-box;
|
|
483
|
+
background:rgba(12,17,28,.5); backdrop-filter:blur(var(--ge-sheet-blur,20px)) saturate(120%);
|
|
484
|
+
-webkit-backdrop-filter:blur(var(--ge-sheet-blur,20px)) saturate(120%); animation:ge-ov-in .16s ease-out; }
|
|
485
|
+
/* Card sizes in cq units of the shell root → responsive on EVERY screen, not just popouts. The
|
|
486
|
+
card's font-size is the one knob (clamped for readability); everything inside is em-relative so
|
|
487
|
+
the whole card scales as a unit. GameShell.fitModal() still transform-scales it down as a
|
|
488
|
+
backstop for very short popouts. */
|
|
489
|
+
#${SHELL_ROOT_ID} .ge-modal-card { font-size:clamp(11px, 2cqmin, 15px); width:100%; max-width:28em; box-sizing:border-box;
|
|
490
|
+
overflow:hidden; transform-origin:center center; background:var(--shell-plaque-solid); border-radius:1.3em;
|
|
491
|
+
display:flex; flex-direction:column; }
|
|
492
|
+
/* ✕ pinned to the overlay corner (the screen), not the card */
|
|
493
|
+
#${SHELL_ROOT_ID} .ge-modal-close { position:absolute; top:12px; right:12px; z-index:2; width:36px; height:36px;
|
|
494
|
+
border:none; border-radius:50%; cursor:pointer; pointer-events:auto; background:var(--shell-plaque-dark); color:#fff;
|
|
495
|
+
display:flex; align-items:center; justify-content:center; font-size:20px; transition:background .12s ease, color .12s ease; }
|
|
496
|
+
#${SHELL_ROOT_ID} .ge-modal-close:hover { background:var(--shell-plaque-glass); color:var(--shell-accent); }
|
|
497
|
+
#${SHELL_ROOT_ID} .ge-modal-body { padding:1.2em; display:flex; flex-direction:column; gap:1.05em; }
|
|
498
|
+
#${SHELL_ROOT_ID} .ge-modal-title { margin:0; text-align:center; color:var(--card-acc, var(--shell-accent));
|
|
499
|
+
font-weight:800; letter-spacing:.04em; text-transform:uppercase; font-size:1.2em; }
|
|
500
|
+
#${SHELL_ROOT_ID} .ge-modal-text { margin:0; text-align:center; color:rgba(255,255,255,.85); font-size:.93em; line-height:1.5; }
|
|
501
|
+
#${SHELL_ROOT_ID} .ge-sheet-grid { display:grid; gap:.65em; grid-template-columns:repeat(var(--cols,3),1fr); }
|
|
502
|
+
#${SHELL_ROOT_ID}.ge-mobile .ge-sheet-grid { grid-template-columns:repeat(var(--cols-m,var(--cols,3)),1fr); }
|
|
503
|
+
/* the bet picker packs 6 chips/row → give its card room (autoplay & co. stay at the 28em default) */
|
|
504
|
+
#${SHELL_ROOT_ID} .ge-sheet[data-ge="bet-modal"] .ge-modal-card { max-width:44em; }
|
|
505
|
+
#${SHELL_ROOT_ID} .ge-chip { pointer-events:auto; cursor:pointer; border:1px solid var(--shell-plaque-line);
|
|
506
|
+
border-radius:.8em; background:rgba(255,255,255,.04); color:#fff; font-size:1em; font-weight:700;
|
|
507
|
+
font-variant-numeric:tabular-nums; padding:.8em .55em; transition:background .12s ease, border-color .12s ease; }
|
|
508
|
+
#${SHELL_ROOT_ID} .ge-chip:hover { background:var(--shell-plaque-glass-hover); }
|
|
509
|
+
#${SHELL_ROOT_ID} .ge-chip.ge-on { border-color:var(--shell-accent); background:var(--shell-accent); color:#fff; }
|
|
510
|
+
/* full-bleed footer button(s), flush to the card's bottom edge (card clips the corners) */
|
|
511
|
+
#${SHELL_ROOT_ID} .ge-modal-actions { display:flex; }
|
|
512
|
+
#${SHELL_ROOT_ID} .ge-modal-actions > * { flex:1; }
|
|
513
|
+
#${SHELL_ROOT_ID} .ge-modal-btn { width:100%; border:none; padding:1.05em; font-size:1em; font-weight:800;
|
|
514
|
+
letter-spacing:.04em; text-transform:uppercase; cursor:pointer; pointer-events:auto; transition:filter .12s ease; }
|
|
515
|
+
#${SHELL_ROOT_ID} .ge-modal-btn:hover:not([disabled]) { filter:brightness(1.08); }
|
|
516
|
+
#${SHELL_ROOT_ID} .ge-modal-btn--accent { background:var(--card-acc, var(--shell-accent)); color:#fff; }
|
|
517
|
+
#${SHELL_ROOT_ID} .ge-modal-btn--ghost { background:var(--shell-plaque-glass-hover); color:#fff; }
|
|
518
|
+
/* replay summary — label/value rows, accented total-win row */
|
|
519
|
+
#${SHELL_ROOT_ID} .ge-replay-rows { display:flex; flex-direction:column; }
|
|
520
|
+
#${SHELL_ROOT_ID} .ge-replay-row { display:flex; justify-content:space-between; align-items:baseline; gap:1.05em; padding:.73em .13em; }
|
|
521
|
+
#${SHELL_ROOT_ID} .ge-replay-row + .ge-replay-row { border-top:1px solid var(--shell-plaque-line); }
|
|
522
|
+
#${SHELL_ROOT_ID} .ge-replay-row span { color:var(--shell-plaque-label); text-transform:uppercase; letter-spacing:.07em; font-size:.73em; font-weight:700; }
|
|
523
|
+
#${SHELL_ROOT_ID} .ge-replay-row b { color:#fff; font-weight:800; font-size:1em; font-variant-numeric:tabular-nums; }
|
|
524
|
+
#${SHELL_ROOT_ID} .ge-replay-total span { color:#fff; font-size:.8em; }
|
|
525
|
+
#${SHELL_ROOT_ID} .ge-replay-total b { color:var(--shell-accent); font-size:1.27em; }
|
|
526
|
+
|
|
527
|
+
#${SHELL_ROOT_ID}.ge-shell-hidden { opacity:0; pointer-events:none; transition:opacity .25s ease; }
|
|
528
|
+
`;
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ShellTokens } from '@/core/theme';
|
|
2
|
+
|
|
3
|
+
/** Emit the shell root's CSS custom-property block from resolved tokens — the DOM renderer's
|
|
4
|
+
* applyTheme. Property names/values must match platform-core's buildThemeVars byte-for-byte. */
|
|
5
|
+
export function buildThemeVars(t: ShellTokens): string {
|
|
6
|
+
return [
|
|
7
|
+
`--shell-fg: ${t.fg}`, `--shell-muted: ${t.muted}`, `--shell-icon: ${t.icon}`,
|
|
8
|
+
`--shell-icon-active: ${t.iconActive}`, `--shell-surface: ${t.surface}`,
|
|
9
|
+
`--shell-hairline: ${t.hairline}`, `--shell-veil: ${t.veil}`, `--shell-veil-strong: ${t.veilStrong}`,
|
|
10
|
+
`--shell-track: ${t.track}`, `--shell-soft: ${t.soft}`, `--shell-spin: ${t.spin}`, `--shell-spin-fg: ${t.spinFg}`,
|
|
11
|
+
`--shell-radius: 12px`,
|
|
12
|
+
`--shell-plaque-dark: ${t.plaqueDark}`, `--shell-plaque-glass: ${t.plaqueGlass}`,
|
|
13
|
+
`--shell-plaque-glass-hover: ${t.plaqueGlassHover}`,
|
|
14
|
+
// The desktop control bar is one continuous, slightly-darker surface (vs the lighter plaque
|
|
15
|
+
// panels). Buttons sitting on it are white discs with black icons (see .ge-bar-panel CSS).
|
|
16
|
+
`--shell-bar: ${t.bar}`, `--shell-btn: ${t.btn}`, `--shell-btn-ink: ${t.btnInk}`,
|
|
17
|
+
`--shell-plaque-solid: ${t.plaqueSolid}`,
|
|
18
|
+
`--shell-plaque-line: ${t.plaqueLine}`, `--shell-plaque-label: ${t.plaqueLabel}`,
|
|
19
|
+
`--shell-accent: ${t.accent}`,
|
|
20
|
+
].join('; ') + ';';
|
|
21
|
+
}
|