@energy8platform/shell 0.6.2 → 0.6.4

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/pixi.esm.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { applySocialReplacements } from '@energy8platform/game-sdk/social';
2
- import { TextStyle, Text, CanvasTextMetrics, Container, Graphics, Rectangle, GraphicsContext, BlurFilter, Color, Assets, Sprite, RenderTexture, ColorMatrixFilter } from 'pixi.js';
2
+ import { TextStyle, Text, CanvasTextMetrics, Container, Graphics, Rectangle, GraphicsContextSystem, GraphicsContext, BlurFilter, Color, Assets, Sprite, RenderTexture, ColorMatrixFilter } from 'pixi.js';
3
3
 
4
4
  /**
5
5
  * Minimal typed event emitter — internal utility for platform-core.
@@ -1219,12 +1219,19 @@ function normalizeLang(code) {
1219
1219
  return (LANG_SET.has(base) ? base : 'en');
1220
1220
  }
1221
1221
  function createI18n(opts) {
1222
- // Social is the `en-social` pseudo-locale: it rewrites the English source. Non-English locales are
1223
- // authored social-safe already, so `socialize` only runs on the English source string.
1222
+ // Social is the `en-social` pseudo-locale: it rewrites the English source into social-safe
1223
+ // vocabulary. The replacement dictionary only exists for English, so social mode FORCES English
1224
+ // the requested `language` is ignored, matching the `isSocial` contract ("…derived from English…
1225
+ // regardless of `language`"). Rendering a non-English locale in social mode would leak untranslated
1226
+ // restricted terms, so we never do it.
1227
+ if (opts.isSocial) {
1228
+ const t = (src) => socialize(src);
1229
+ return { lang: 'en', t };
1230
+ }
1224
1231
  const lang = normalizeLang(opts.language);
1225
1232
  const t = (src) => {
1226
1233
  if (lang === 'en')
1227
- return opts.isSocial ? socialize(src) : src;
1234
+ return src;
1228
1235
  return opts.messages?.[lang]?.[src] ?? LOCALES[lang]?.[src] ?? src;
1229
1236
  };
1230
1237
  return { lang, t };
@@ -1447,7 +1454,7 @@ class KeyboardController {
1447
1454
 
1448
1455
  // AUTO-GENERATED by scripts/gen-version.mjs — do not edit. Mirrors package.json "version".
1449
1456
  /** The @energy8platform/shell package version, stamped into the game-info footer. */
1450
- const PACKAGE_VERSION = '0.6.2';
1457
+ const PACKAGE_VERSION = '0.6.4';
1451
1458
 
1452
1459
  /** Apply defaults to the raw config (the mount target lives on the renderer, not here). */
1453
1460
  function resolveConfig(config) {
@@ -2004,6 +2011,14 @@ function countUpText(ticker, text, from, to, fmt, durationMs = 450) {
2004
2011
  });
2005
2012
  }
2006
2013
 
2014
+ // AUTO-GENERATED from ../../icons.svg by scripts/gen-buy-bonus.mjs — do not edit by hand.
2015
+ // The full-colour buy-bonus coins (default / social / deactivate), shared by the DOM shell
2016
+ // (inline <svg>) and the Pixi shell (GraphicsContext.svg). To refresh: edit icons.svg, then
2017
+ // `node scripts/gen-buy-bonus.mjs`.
2018
+ const BUY_BONUS_ART = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="850.09 834.52 263.91 264.00"><path fill="#ebac28" stroke="#fac64c" stroke-miterlimit="10" d="M1112.44,966.72c-.06,72.01-58.5,130.3-130.51,130.19-72.01-.11-130.34-58.58-130.28-130.59.06-72.01,58.5-130.3,130.51-130.19s130.34,58.58,130.28,130.59Z"/> <path fill="#724d23" stroke="#b05f05" stroke-miterlimit="10" d="M1095.56,966.7c.02,62.62-50.73,113.37-113.35,113.35s-113.4-50.8-113.42-113.42c-.02-62.62,50.73-113.37,113.35-113.35,62.62.02,113.4,50.8,113.42,113.42h0Z"/> <path fill="#e8be56" stroke="#fdf3a8" stroke-miterlimit="10" d="M1087.69,966.08c.02,58.32-47.24,105.58-105.56,105.56s-105.61-47.31-105.63-105.63,47.24-105.58,105.56-105.56c58.32.02,105.61,47.31,105.63,105.63Z"/> <path fill="#c38a32" stroke="#944f09" stroke-width=".5px" stroke-miterlimit="10" d="M981.14,1039.18l2.77,8.52c.05.15.2.26.35.26h8.86c.39,0,.56.5.24.74l-7.17,5.21c-.13.1-.19.26-.14.41l2.77,8.52c.11.35-.28.64-.58.41l-7.25-5.26c-.13-.1-.31-.1-.44,0l-7.25,5.26c-.3.21-.69-.07-.58-.41l2.77-8.52c.05-.15,0-.32-.14-.41l-7.25-5.26c-.3-.21-.14-.68.22-.68h8.96c.16,0,.31-.1.35-.26l2.77-8.52c.11-.35.6-.35.71,0h-.02s.01-.01.01-.01Z"/> <path fill="#c38a32" stroke="#944f09" stroke-width=".5px" stroke-miterlimit="10" d="M1005.2,1043.69l1.31,4.05c.03.07.1.13.17.13h4.25c.18,0,.24.23.11.33l-3.44,2.5c-.07.04-.1.13-.07.2l1.31,4.05c.05.17-.14.31-.28.2l-3.44-2.5c-.07-.04-.15-.04-.21,0l-3.44,2.5c-.14.1-.33-.03-.28-.2l1.31-4.05c.03-.07,0-.16-.07-.2l-3.44-2.5c-.14-.1-.07-.33.11-.33h4.25c.07,0,.15-.05.17-.13l1.31-4.05c.05-.17.29-.17.35,0h-.01Z"/> <path fill="#c38a32" stroke="#944f09" stroke-width=".5px" stroke-miterlimit="10" d="M957.17,1043.81l1.31,4.05c.03.07.1.13.17.13h4.25c.18,0,.24.23.11.33l-3.44,2.5c-.07.04-.1.13-.07.2l1.31,4.05c.05.17-.14.31-.28.2l-3.44-2.5c-.07-.04-.15-.04-.21,0l-3.44,2.5c-.14.1-.33-.03-.28-.2l1.31-4.05c.03-.07,0-.16-.07-.2l-3.44-2.5c-.14-.1-.07-.33.11-.33h4.25c.07,0,.15-.05.17-.13l1.31-4.05c.05-.17.29-.17.35,0h-.01Z"/> <rect fill="#c38a32" stroke="#944f09" stroke-width=".5px" stroke-miterlimit="10" x="970.41" y="888.96" width="21.53" height="3.12" rx="1.44" ry="1.44"/> <path fill="#c38a32" stroke="#944f09" stroke-width=".5px" stroke-miterlimit="10" d="M995.46,872.54c0,.66-.55,1.2-1.21,1.2-.16,0-.32-.04-.46-.1l-2.45,14.68s-.01.03-.03.03l-20.36-.11s-.03-.01-.03-.03l-2.38-14.63c-.16.08-.32.12-.5.12-.67,0-1.2-.55-1.2-1.21s.54-1.2,1.2-1.2,1.21.54,1.21,1.2c0,.42-.22.79-.55,1-.02.01-.02.03,0,.04l7.01,5.83s.04,0,.04-.01l5-10.38s0-.04-.02-.04c-.21-.03-.4-.11-.59-.22-.68-.46-.83-1.48-.31-2.11.47-.55,1.29-.67,1.9-.25.65.45.82,1.34.37,1.98-.21.32-.54.52-.87.58-.02,0-.03.02-.02.04l5.86,9.71s.03.02.04,0l6.38-5.15s.01-.03,0-.04c-.29-.22-.47-.57-.47-.95,0-.67.54-1.21,1.2-1.21s1.21.55,1.21,1.21v.03s0,0,0,0Z"/> <g> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M935.88,905.66c2.96-.3,7.26-.06,10.52-.12,4.7.14,10.12-.21,13.66,3.32,3.36,3.25,3.94,10.96,1.95,15.25-.68,1.37-3.04,2.32-3.17,3.1-.13.7.87,1.21,1.46,1.66,2.23,1.36,3.21,3.44,3.55,6.27.21,4.87,1.3,12.42-2.92,15.88-1.83,1.43-3.82,1.61-6.79,1.8-3.57.19-5.87.14-10.03.15-2.46-.03-5.72.08-7.99-.08-.36-.06-.54-.16-.64-.33-.3-4.82-.04-35.33-.08-46.34.04-.27.13-.43.41-.53l.07-.02ZM948.07,913.71c-.51.13-.33.75-.36,1.83,0,1.21,0,2.45,0,3.82.11,2.16-.25,3.85.2,4.68.72.4,2.27,0,2.89-.41,1.2-.9.85-3.93.95-5.59.08-2.62-.76-4.69-3.56-4.35l-.11.03ZM948.19,931.6c-.69.13-.42.96-.47,2.37,0,2.47,0,5.42,0,8.01,0,.82,0,1.52,0,2.01,0,.49.16.75.56.83,1.05.07,2.45-.2,2.96-1.23.89-2.78.47-6.31.42-9.17-.06-2.29-1.3-2.87-3.35-2.83l-.12.02Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M996.05,905.73c.38.19.45.58.48.97.04,1.17,0,3.62.02,6.68-.13,12,.28,20.25-.22,29.52-.46,7.23-7.19,11.55-14.25,10.98-5.09-.1-10.69-2.42-12.63-7.43-1.35-3.23-.97-7.4-1.1-11.17-.04-6.25,0-12.99-.01-21.52.06-3.51-.13-5.79.11-7.41.45-1.14,1.54-.76,3.86-.88,2.08,0,4.73-.02,6.22.02,1,.06,2.12.01,2.12,1.1.15,5.89-.15,28.48.13,35.86.12,1.43,1.14,3,2.07,2.44,1.2-.83,1.29-2.56,1.35-3.88.07-9.17,0-24.77.03-33.63-.12-1.61.51-1.85,1.98-1.87,2.57.08,7.02-.3,9.78.2l.07.03Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1027.34,905.76c.3.16.3.42.25.74-.36,1.44-1.67,5.54-3.08,10.27-2.15,7.09-4.69,15.42-5.42,17.89-.35,1.09-.35,2.07-.35,3.27-.01,2.86.01,9.73-.01,12.73.06,1.7-.58,2.32-2.39,2.28-1.76.04-4.69.03-6.48.01-2.15.05-2.84-.48-2.77-2.3-.02-2.48,0-7.65-.01-10.96-.05-2.36.16-3.21-.34-5.04-1.19-4.25-6.82-23.02-8.19-28.15-.08-.99.61-.84,1.76-.95,1.45-.02,4.06-.05,6.01.01,4.72.05,4.18,5.07,5.07,8.61.29,1.66.57,3.32.82,4.97.31,1.97.47,3.95.68,3.74.92-3.48,1.17-6.83,2.08-10.81.49-2.09.73-5.34,2.6-6.07,1.45-.62,3.11-.43,4.7-.47,1.51.02,3.99-.11,5.01.2l.07.03Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M901.92,977.66c2.96-.3,7.26-.06,10.52-.12,4.7.14,10.12-.21,13.66,3.32,3.36,3.25,3.94,10.96,1.95,15.25-.68,1.37-3.04,2.32-3.17,3.1-.13.7.87,1.21,1.46,1.66,2.23,1.36,3.21,3.44,3.55,6.27.21,4.87,1.3,12.42-2.92,15.88-1.83,1.43-3.82,1.61-6.79,1.8-3.57.19-5.87.14-10.03.15-2.46-.03-5.72.08-7.99-.08-.36-.06-.54-.16-.64-.33-.3-4.82-.04-35.33-.08-46.34.04-.27.13-.43.41-.53l.07-.02ZM914.12,985.71c-.51.13-.33.75-.36,1.83,0,1.21,0,2.45,0,3.82.11,2.16-.25,3.85.2,4.68.72.4,2.27,0,2.89-.41,1.2-.9.85-3.93.95-5.59.08-2.62-.76-4.69-3.56-4.35l-.11.03ZM914.24,1003.6c-.69.13-.42.96-.47,2.37,0,2.47,0,5.42,0,8.01,0,.82,0,1.52,0,2.01,0,.49.16.75.56.83,1.05.07,2.45-.2,2.96-1.23.89-2.78.47-6.31.42-9.17-.06-2.29-1.3-2.87-3.35-2.83l-.12.02Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M962.74,1005.96c-.18,5.2.5,10.77-2.72,15.03-5.29,6.84-18.04,6.66-23.12-.06-3.89-5.6-2.31-12.71-2.69-19.34.15-10.8-1.95-23.36,12.25-24.95,6.93-.87,14.4,2.56,15.7,9.65.98,6.26.44,13.16.57,19.47v.21ZM950.38,988.4c-.04-.8-.09-1.72-.39-2.53-1.09-2.61-3.17-.91-3.28,1.17-.39,5.72-.05,17.95-.16,23.77.02,1.68-.04,3.37.21,5.04.23,2.39,2.78,2.97,3.37.18.13-.58.18-1.21.21-1.8.16-7.09.07-17.52.04-25.69v-.15Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M994.31,977.71c.56.17.56.58.57,1.08,0,5.94.01,39.72,0,45.1-.07.98-.49.94-1.77,1.05-1.53.05-4.68.04-6.57,0-.87-.05-1.89-.03-2.47-.74-1.54-3.38-2.75-9.04-4.5-14.4-.94-2.89-1.62-6.09-1.86-5.59-.13,3.1,0,14.27-.06,18.61-.03.6-.04,1.21-.42,1.6-1.25.82-2.88.42-4.72.53-1.41,0-2.75,0-3.6-.05-1.27-.08-1.52-.32-1.55-1.03-.01-5.46-.03-40.18,0-45.35.1-.9.56-.85,1.69-.96,1.5-.05,4.63-.06,6.43,0,1.14.01,2.29.3,2.65,1.51.89,2.51,3.19,9.82,4.78,14.65.94,2.85,1.47,4.74,1.59,4.44.23-2.46-.02-15.02.11-18.93.04-1.66,1.31-1.65,2.91-1.7,1.82,0,5.18-.12,6.72.14l.08.02Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1027.37,977.73c.38.19.45.58.48.97.04,1.17,0,3.62.02,6.68-.13,12,.28,20.25-.22,29.52-.46,7.23-7.19,11.55-14.25,10.98-5.09-.1-10.69-2.42-12.63-7.43-1.35-3.23-.97-7.4-1.1-11.17-.04-6.25,0-12.99-.01-21.52.06-3.51-.13-5.79.11-7.41.45-1.14,1.54-.76,3.86-.88,2.08,0,4.73-.02,6.22.02,1,.06,2.12.01,2.12,1.1.15,5.89-.15,28.48.13,35.86.12,1.43,1.14,3,2.07,2.44,1.2-.83,1.29-2.56,1.35-3.88.07-9.17,0-24.77.03-33.63-.12-1.61.51-1.85,1.98-1.87,2.57.08,7.02-.3,9.78.2l.07.03Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1058.28,990.89c-.4.56-1.14.81-2.19.89-1.95.06-4.54.16-6.51-.08-2.37-.28-2.12-2.56-2.53-4.31-.2-1.02-1.05-2.4-2.19-1.86-.95.5-1.35,1.83-1.39,2.88-.24,5.1,5.86,7.07,9.22,9.8,3.83,2.83,5.49,4.39,6.48,8.37.85,5,1.65,11.82-2.22,15.69-5.98,5.26-17.55,5.13-22.7-1.18-2.2-3.02-2.68-8.11-2.02-11.62,1.03-1.98,3.23-1.32,5.92-1.47,7.65-.59,4.08,3.47,5.95,8.01.97,1.86,2.9,1.05,3.41-.76.76-2.5.16-5.88-1.78-7.52-4.15-3.81-10.9-6.14-13.05-11.8-1.67-4.95-1.82-12.38,2.1-16.06,4.54-4.02,11.78-4.04,17.28-2.21,3.75,1.41,6.07,3.96,6.44,7.6.18,1.64.63,4.13-.15,5.51l-.07.11Z"/> </g></svg>`;
2019
+ const BUY_BONUS_SOCIAL_ART = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="1142.49 836.33 263.91 264.00"><path fill="#ebac28" stroke="#fac64c" stroke-miterlimit="10" d="M1404.84,968.53c-.06,72.01-58.5,130.3-130.51,130.19-72.01-.11-130.34-58.58-130.28-130.59.06-72.01,58.5-130.3,130.51-130.19s130.34,58.58,130.28,130.59Z"/> <path fill="#724d23" stroke="#b05f05" stroke-miterlimit="10" d="M1387.96,968.51c.02,62.62-50.73,113.37-113.35,113.35s-113.4-50.8-113.42-113.42c-.02-62.62,50.73-113.37,113.35-113.35,62.62.02,113.4,50.8,113.42,113.42h0Z"/> <path fill="#e8be56" stroke="#fdf3a8" stroke-miterlimit="10" d="M1380.09,967.89c.02,58.32-47.24,105.58-105.56,105.56s-105.61-47.31-105.63-105.63,47.24-105.58,105.56-105.56c58.32.02,105.61,47.31,105.63,105.63Z"/> <path fill="#c38a32" stroke="#944f09" stroke-width=".5px" stroke-miterlimit="10" d="M1273.54,1040.98l2.77,8.52c.05.15.2.26.35.26h8.86c.39,0,.56.5.24.74l-7.17,5.21c-.13.1-.19.26-.14.41l2.77,8.52c.11.35-.28.64-.58.41l-7.25-5.26c-.13-.1-.31-.1-.44,0l-7.25,5.26c-.3.21-.69-.07-.58-.41l2.77-8.52c.05-.15,0-.32-.14-.41l-7.25-5.26c-.3-.21-.14-.68.22-.68h8.96c.16,0,.31-.1.35-.26l2.77-8.52c.11-.35.6-.35.71,0h-.02s.01-.01.01-.01Z"/> <path fill="#c38a32" stroke="#944f09" stroke-width=".5px" stroke-miterlimit="10" d="M1297.6,1045.5l1.31,4.05c.03.07.1.13.17.13h4.25c.18,0,.24.23.11.33l-3.44,2.5c-.07.04-.1.13-.07.2l1.31,4.05c.05.17-.14.31-.28.2l-3.44-2.5c-.07-.04-.15-.04-.21,0l-3.44,2.5c-.14.1-.33-.03-.28-.2l1.31-4.05c.03-.07,0-.16-.07-.2l-3.44-2.5c-.14-.1-.07-.33.11-.33h4.25c.07,0,.15-.05.17-.13l1.31-4.05c.05-.17.29-.17.35,0h-.01Z"/> <path fill="#c38a32" stroke="#944f09" stroke-width=".5px" stroke-miterlimit="10" d="M1249.57,1045.61l1.31,4.05c.03.07.1.13.17.13h4.25c.18,0,.24.23.11.33l-3.44,2.5c-.07.04-.1.13-.07.2l1.31,4.05c.05.17-.14.31-.28.2l-3.44-2.5c-.07-.04-.15-.04-.21,0l-3.44,2.5c-.14.1-.33-.03-.28-.2l1.31-4.05c.03-.07,0-.16-.07-.2l-3.44-2.5c-.14-.1-.07-.33.11-.33h4.25c.07,0,.15-.05.17-.13l1.31-4.05c.05-.17.29-.17.35,0h-.01Z"/> <rect fill="#c38a32" stroke="#944f09" stroke-width=".5px" stroke-miterlimit="10" x="1262.81" y="890.76" width="21.53" height="3.12" rx="1.44" ry="1.44"/> <path fill="#c38a32" stroke="#944f09" stroke-width=".5px" stroke-miterlimit="10" d="M1287.86,874.34c0,.66-.55,1.2-1.21,1.2-.16,0-.32-.04-.46-.1l-2.45,14.68s-.01.03-.03.03l-20.36-.11s-.03-.01-.03-.03l-2.38-14.63c-.16.08-.32.12-.5.12-.67,0-1.2-.55-1.2-1.21s.54-1.2,1.2-1.2,1.21.54,1.21,1.2c0,.42-.22.79-.55,1-.02.01-.02.03,0,.04l7.01,5.83s.04,0,.04-.01l5-10.38s0-.04-.02-.04c-.21-.03-.4-.11-.59-.22-.68-.46-.83-1.48-.31-2.11.47-.55,1.29-.67,1.9-.25.65.45.82,1.34.37,1.98-.21.32-.54.52-.87.58-.02,0-.03.02-.02.04l5.86,9.71s.03.02.04,0l6.38-5.15s.01-.03,0-.04c-.29-.22-.47-.57-.47-.95,0-.67.54-1.21,1.2-1.21s1.21.55,1.21,1.21v.03s0,0,0,0Z"/> <g> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1260.24,923.82c-1.21,1.32-3.18.8-5.84.92-4.55.03-6.12.36-5.96-4.95-.11-1.7.01-3.73-1.57-4.59-1.62-.51-2.36,2.26-2.33,3.77-.11,5.71-.01,15.47-.04,21.13.14,2.2-.46,5.02,1.39,6.62,2.74,1.36,3.06-4.16,2.95-6.1.04-2.52-1.18-3.59-2.01-5.57-.72-2.3-.92-5.88,1.97-5.72,2.33-.07,6.98-.02,9.42-.02.86.08,2.1-.15,2.47.78.35,5.76.06,19.15.12,23.15-.03.52-.11.98-.51,1.28-1.73.77-4.7.59-6.48-.27-.92-.45-1.48-.9-2.08-.99-1.61-.15-2.95,1.56-4.5,1.95-3.95,1.37-8.65-.09-11.49-3.06-4.95-5.67-3.19-12.08-3.58-19.74.04-5.03-.14-9.89.33-14.88.34-8.33,9.22-12.5,16.95-10.97,5.72.74,10.69,5.2,11.02,11.05.14,1.84.66,4.55-.15,6.13l-.07.1Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1265.94,907.48c4.26-.34,13.96-.1,18.76-.09,1.14.03,1.38,1.04,1.41,2-.05,1.89.4,4.69-.41,6.5-1.63,1.91-5.25.08-7.07,1.62-.98,1.03-.66,3.54-.67,5.42.15,6.15,8.37-.74,7.67,6.97,0,1.75.09,4.06-1.06,4.52-1.34.67-3.3.28-4.74.56-1.74.28-1.85,1.42-1.9,3.14.03,1.77-.15,3.97.17,5.68.16.69.61,1.13,1.28,1.28,2.4.74,7.59-1.12,7.54,2.61-.05,2.1.43,4.74-.39,6.59-.52.67-1.93.44-4.22.49-4.59-.13-12.11.26-16.38-.16-.34-.17-.32-.53-.34-.9,0-4.1,0-24.6,0-36.82.01-4.43-.02-7.74.02-8.85.02-.24.07-.42.29-.54l.05-.02Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1314.36,907.94c.31.97.19,2.84.22,4.14-.11,1.48.39,3.48-1.04,4.36-1.57.8-4.56-.06-6,.79-.45.54-.22,1.21-.28,1.96,0,2.08,0,5.62,0,9.67-.08,9.98.16,21.72-.1,25.34-.47.73-1.16.46-2.32.56-2.4-.09-7.2.26-9.48-.21-.45-.22-.41-.68-.43-1.16,0-4.84,0-26.16,0-33.6-.07-1.73.29-2.57-.82-2.81-1.47-.35-4.12.11-5.44-.55-1.05-.58-.99-1.7-1.04-3.44.19-1.78-.65-5.66,1.23-5.64,3.61-.07,19.83-.02,24.13-.02.56.02,1.07.01,1.33.52l.04.08Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1194.32,979.47c2.96-.3,7.26-.06,10.52-.12,4.7.14,10.12-.21,13.66,3.32,3.36,3.25,3.94,10.96,1.95,15.25-.68,1.37-3.04,2.32-3.17,3.1-.13.7.87,1.21,1.46,1.66,2.23,1.36,3.21,3.44,3.55,6.27.21,4.87,1.3,12.42-2.92,15.88-1.83,1.43-3.82,1.61-6.79,1.8-3.57.19-5.87.14-10.03.15-2.46-.03-5.72.08-7.99-.08-.36-.06-.54-.16-.64-.33-.3-4.82-.04-35.33-.08-46.34.04-.27.13-.43.41-.53l.07-.02ZM1206.52,987.51c-.51.13-.33.75-.36,1.83,0,1.21,0,2.45,0,3.82.11,2.16-.25,3.85.2,4.68.72.4,2.27,0,2.89-.41,1.2-.9.85-3.93.95-5.59.08-2.62-.76-4.69-3.56-4.35l-.11.03ZM1206.64,1005.41c-.69.13-.42.96-.47,2.37,0,2.47,0,5.42,0,8.01,0,.82,0,1.52,0,2.01,0,.49.16.75.56.83,1.05.07,2.45-.2,2.96-1.23.89-2.78.47-6.31.42-9.17-.06-2.29-1.3-2.87-3.35-2.83l-.12.02Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1255.14,1007.76c-.18,5.2.5,10.77-2.72,15.03-5.29,6.84-18.04,6.66-23.12-.06-3.89-5.6-2.31-12.71-2.69-19.34.15-10.8-1.95-23.36,12.25-24.95,6.93-.87,14.4,2.56,15.7,9.65.98,6.26.44,13.16.57,19.47v.21ZM1242.78,990.2c-.04-.8-.09-1.72-.39-2.53-1.09-2.61-3.17-.91-3.28,1.17-.39,5.72-.05,17.95-.16,23.77.02,1.68-.04,3.37.21,5.04.23,2.39,2.78,2.97,3.37.18.13-.58.18-1.21.21-1.8.16-7.09.07-17.52.04-25.69v-.15Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1286.71,979.51c.56.17.56.58.57,1.08,0,5.94.01,39.72,0,45.1-.07.98-.49.94-1.77,1.05-1.53.05-4.68.04-6.57,0-.87-.05-1.89-.03-2.47-.74-1.54-3.38-2.75-9.04-4.5-14.4-.94-2.89-1.62-6.09-1.86-5.59-.13,3.1,0,14.27-.06,18.61-.03.6-.04,1.21-.42,1.6-1.25.82-2.88.42-4.72.53-1.41,0-2.75,0-3.6-.05-1.27-.08-1.52-.32-1.55-1.03-.01-5.46-.03-40.18,0-45.35.1-.9.56-.85,1.69-.96,1.5-.05,4.63-.06,6.43,0,1.14.01,2.29.3,2.65,1.51.89,2.51,3.19,9.82,4.78,14.65.94,2.85,1.47,4.74,1.59,4.44.23-2.46-.02-15.02.11-18.93.04-1.66,1.31-1.65,2.91-1.7,1.82,0,5.18-.12,6.72.14l.08.02Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1319.77,979.53c.38.19.45.58.48.97.04,1.17,0,3.62.02,6.68-.13,12,.28,20.25-.22,29.52-.46,7.23-7.19,11.55-14.25,10.98-5.09-.1-10.69-2.42-12.63-7.43-1.35-3.23-.97-7.4-1.1-11.17-.04-6.25,0-12.99-.01-21.52.06-3.51-.13-5.79.11-7.41.45-1.14,1.54-.76,3.86-.88,2.08,0,4.73-.02,6.22.02,1,.06,2.12.01,2.12,1.1.15,5.89-.15,28.48.13,35.86.12,1.43,1.14,3,2.07,2.44,1.2-.83,1.29-2.56,1.35-3.88.07-9.17,0-24.77.03-33.63-.12-1.61.51-1.85,1.98-1.87,2.57.08,7.02-.3,9.78.2l.07.03Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1350.68,992.69c-.4.56-1.14.81-2.19.89-1.95.06-4.54.16-6.51-.08-2.37-.28-2.12-2.56-2.53-4.31-.2-1.02-1.05-2.4-2.19-1.86-.95.5-1.35,1.83-1.39,2.88-.24,5.1,5.86,7.07,9.22,9.8,3.83,2.83,5.49,4.39,6.48,8.37.85,5,1.65,11.82-2.22,15.69-5.98,5.26-17.55,5.13-22.7-1.18-2.2-3.02-2.68-8.11-2.02-11.62,1.03-1.98,3.23-1.32,5.92-1.47,7.65-.59,4.08,3.47,5.95,8.01.97,1.86,2.9,1.05,3.41-.76.76-2.5.16-5.88-1.78-7.52-4.15-3.81-10.9-6.14-13.05-11.8-1.67-4.95-1.82-12.38,2.1-16.06,4.54-4.02,11.78-4.04,17.28-2.21,3.75,1.41,6.07,3.96,6.44,7.6.18,1.64.63,4.13-.15,5.51l-.07.11Z"/> </g></svg>`;
2020
+ const BUY_BONUS_DISABLED_ART = `<svg xmlns="http://www.w3.org/2000/svg" viewBox="1427.21 845.04 263.91 264.00"><path fill="#ebac28" stroke="#fac64c" stroke-miterlimit="10" d="M1689.56,977.24c-.06,72.01-58.5,130.3-130.51,130.19-72.01-.11-130.34-58.58-130.28-130.59.06-72.01,58.5-130.3,130.51-130.19s130.34,58.58,130.28,130.59Z"/> <path fill="#724d23" stroke="#b05f05" stroke-miterlimit="10" d="M1672.68,977.22c.02,62.62-50.73,113.37-113.35,113.35s-113.4-50.8-113.42-113.42c-.02-62.62,50.73-113.37,113.35-113.35,62.62.02,113.4,50.8,113.42,113.42h0Z"/> <path fill="#e8be56" stroke="#fdf3a8" stroke-miterlimit="10" d="M1664.81,976.6c.02,58.32-47.24,105.58-105.56,105.56s-105.61-47.31-105.63-105.63,47.24-105.58,105.56-105.56c58.32.02,105.61,47.31,105.63,105.63Z"/> <g> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1463,957.03c2.56-.29,8.57-.12,11.76-.04,5.58-.09,11.59,1.52,12.44,7.62.66,7.82.32,17.48.24,24.98.14,10.47-9.06,9.13-17.32,9.19-2.17-.03-5.06.09-7.03-.09-.63-.1-.74-.36-.76-.9,0-4.3-.06-30.8.02-40.15.06-.33.21-.49.56-.59l.09-.02ZM1473.96,964.03c-1.03-.07-.69,2.43-.74,4.21,0,6.76-.02,15.33,0,22.09-.03,1.34.99,1.52,1.85.95,2.18-1.68,1.38-5.18,1.59-7.63,0-2.48,0-7.55,0-10.85-.26-3.14,1.01-8.14-2.62-8.78h-.09Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1502.08,957.04c.27.1.37.26.41.52.06,1.07.01,4.28.03,8.51,0,7.65.01,27,0,31.75-.02.94-.66.89-1.64.95-1.21.04-3.49.01-5.4.02-1.35-.14-3.91.46-3.84-.97,0-5.08-.06-34.32.02-40.19.23-.96,1.12-.62,2.51-.74,2.53.07,5.57-.17,7.85.13l.07.02Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1529.42,968.56c-.85,1.03-2.68.92-4.09.95-1.35-.02-2.79.09-4.01-.42-1.48-.59-1.34-2.38-1.93-3.86-.25-.68-.9-1.48-1.68-1.07-.65.35-1.02,1.25-1.13,1.99-.61,4.62,5.09,6.64,8.11,9.08,3.6,2.65,5.08,4.21,5.85,8.48.5,4.24,1.02,9.62-2.34,12.79-5.22,4.53-15.5,4.43-19.94-1.23-1.85-2.65-2.4-6.91-1.69-9.95,1.16-2.1,3.98-1.35,6.42-1.48,4.23-.11,3.01,3.19,3.8,6.28.24.81.87,1.75,1.81,1.56,1.52-.55,1.73-2.71,1.6-4.15-.26-3.34-2.56-4.65-5.14-6.48-2.81-2.04-6.27-3.87-7.79-7.15-1.89-4.39-2.2-11.51,1.64-15.04,5.19-4.47,17.33-4.04,20.28,2.82.6,1.99,1.25,4.91.3,6.76l-.08.11Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1552.87,957.05c.42.18.43.63.52,1.07.82,5.55,5.33,35.33,5.96,39.77,0,.98-.61.77-1.89.88-2.19-.1-5.81.31-8.09-.26-2.04-.92-.88-4.45-2.16-6.1-.6-1-1.97-1.25-2.76-.35-1.61,1.51-.51,4.92-2.14,6.26-1.61.89-7.78.33-9.37.41-.9-.09-1.16-.19-1.11-.92.92-5.43,3.98-32.81,5.65-40.64.51-.33,1.24-.25,1.9-.28,3.58.11,10.36-.22,13.44.14l.06.02ZM1547.05,983.96c.36-.37.29-.99.24-1.5-.48-4.19-.92-9.67-1.45-15.13-.26-1.5-.51.91-.59,1.36-.79,5.88-1.35,10.57-1.66,13.97-.45,2,2.26,2.08,3.36,1.39l.09-.09Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1561.74,957.03c3.14-.32,7.34-.09,10.7-.12,6.59-.11,12.7,1.62,13.11,9.39.18,2.07.16,4.77-.7,6.54-.69,1.55-2.61,2.36-2.72,3.15-.11.93,1.25,1.54,1.87,2.17,1.31,1.07,1.95,2.62,2.25,4.47.34,4.17.97,9.41-1.37,13.16-3.15,4.13-10.24,2.63-14.89,2.98-2.46-.05-5.73.12-8.09-.11-.33-.07-.51-.18-.6-.36-.31-4.81-.07-30.82-.08-40.74.05-.27.16-.43.44-.53l.07-.02ZM1572.4,964.11c-.47.13-.25.83-.3,1.99v2.76c.1,1.97-.22,3.52.17,4.36.63.38,1.99.03,2.54-.33.92-.56.77-2.75.86-4.06.09-2.51-.16-5.13-3.16-4.75l-.11.03ZM1572.52,979.92c-.57.12-.39.77-.42,1.9,0,2.2,0,4.85,0,7.16.06,1.43-.2,2.39.4,2.57.96.16,2.29-.15,2.73-1.11.72-2.21.38-5.09.39-7.46-.08-2.19-.63-3.14-2.99-3.08l-.12.02Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1600.9,957.09c.46.21.45.63.47,1.1.02,4.6,0,25.94.01,30.78.02.59.1,1.02.74,1.21,4.86.63,6.14-.68,5.87,5.67.04,3.11-.8,2.98-4.49,2.92-3.95-.07-9.05.16-12.46-.1-.53-.13-.53-.49-.56-.97,0-5.34-.01-31.54,0-39.66-.13-1.21,1.14-1.11,2.02-1.15,2.39.08,5.98-.23,8.32.17l.07.03Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1611.03,957.04c3.53-.41,11.5-.05,15.56-.14.74.03,1.54.08,1.87.8.26.56.29,1.3.32,1.95-.04,2.41.48,5.14-2.08,5.5-3.78.3-5.42-.13-5.16,4.7-.09,5.4,4.65,1.78,6.41,4.68.58,1.7.6,4.92-.4,5.93-1.48,1.38-4.4,0-5.59,1.79-.73,1.81-.5,4.61-.24,6.58.76,3.03,6.18.22,7.44,2.86.47,1.06.35,3.19.32,4.72-.04.62-.08,1.26-.39,1.76-.49.77-1.77.6-3.87.63-4.03-.13-10.44.28-14.21-.18-.37-.19-.35-.59-.37-1.01,0-3.97,0-21.65,0-32.24.02-3.86-.03-6.74.03-7.75.03-.24.1-.42.31-.53l.05-.02Z"/> <path fill="#412b16" stroke="#331700" stroke-miterlimit="10" d="M1633.34,957.03c2.56-.29,8.57-.12,11.76-.04,5.58-.09,11.59,1.52,12.44,7.62.66,7.82.32,17.48.24,24.98.14,10.47-9.06,9.13-17.32,9.19-2.17-.03-5.06.09-7.03-.09-.63-.1-.74-.36-.76-.9,0-4.3-.06-30.8.02-40.15.06-.33.21-.49.56-.59l.09-.02ZM1644.29,964.03c-1.03-.07-.69,2.43-.74,4.21,0,6.76-.02,15.33,0,22.09-.03,1.34.99,1.52,1.85.95,2.18-1.68,1.38-5.18,1.59-7.63,0-2.48,0-7.55,0-10.85-.26-3.14,1.01-8.14-2.62-8.78h-.09Z"/> </g></svg>`;
2021
+
2007
2022
  function isSizable(n) {
2008
2023
  return !!n && typeof n.measureSize === 'function' && typeof n.setLayoutSize === 'function';
2009
2024
  }
@@ -2263,89 +2278,74 @@ function roundedPath(g, x, y, w, h, [tl, tr, br, bl]) {
2263
2278
  g.closePath();
2264
2279
  }
2265
2280
 
2266
- // AUTO-GENERATED from ./icons-preview.svg by scripts/gen-icons.mjs — do not edit by hand.
2281
+ // AUTO-GENERATED from ../../icons.svg by scripts/gen-icons-from-svg.mjs — do not edit by hand.
2267
2282
  // Sharp monochrome icon set: each glyph is a 24×24 viewBox fragment using currentColor
2268
- // (hollow shapes use fill-rule="evenodd"; the few stroked glyphs use stroke="currentColor").
2269
- // Coordinates are baked into the 24×24 space (no <g transform>), so the same fragment renders
2270
- // identically in the DOM <svg> and in Pixi's GraphicsContext.svg.
2271
- // To change an icon: edit icons-preview.svg, then run `node scripts/gen-icons.mjs`.
2283
+ // (hollow shapes use fill-rule="evenodd"). Coordinates are baked into the 24×24 space (no
2284
+ // <g transform>), so the same fragment renders identically in the DOM <svg> and in Pixi's
2285
+ // GraphicsContext.svg. To change an icon: edit icons.svg, then run
2286
+ // `node scripts/gen-icons-from-svg.mjs`.
2272
2287
  const SVGS = {
2273
- spin: `<g fill="currentColor" fill-rule="evenodd"><path d="M17.72 4.86c-1.77 -0.89 -5.48 -2.51 -7.37 -3.08l1.4 1.96h0c-5.36 0.16 -8.6 5.47 -9.04 10.35l1.58 2.61l0.19 0.1l1.86 -1.68c-0.21 -0.42 -0.56 -1.87 -0.56 -1.87h0c-0.41 -4.55 3.28 -7.88 7.75 -7.37h0l2.33 0.75l1.86 -1.59v-0.18Z"/><path d="M6.29 19.38c1.79 0.86 5.54 2.39 7.43 2.92l-1.44 -1.93h0c5.36 -0.27 8.48 -5.65 8.81 -10.54l-1.64 -2.57l-0.19 -0.1l-1.82 1.72c0.22 0.42 0.6 1.86 0.6 1.86h0c0.51 4.54 -3.12 7.95 -7.59 7.53h0l-2.35 -0.7l-1.83 1.63v0.18Z"/></g>`,
2274
- turbo1: `<g fill="currentColor" fill-rule="evenodd"><path d="M11.77 2.71l-4.62 10.63h4.39c-2.08 3.86 -4.74 8.67 -4.74 8.67h0.23s11.95 -12.16 11.95 -12.16l-5.79 0.03l5.63 -8.67l-7.05 1.5Z"/></g>`,
2275
- autoplay: `<g fill="currentColor" fill-rule="evenodd"><path d="M9.61 8.97v6.44h0.1c0.82 -0.65 4.19 -2.6 5.15 -3.17v-0.1c-0.99 -0.55 -4.32 -2.68 -5.25 -3.17Z"/><path d="M20.32 8.33l-1.5 1.31v7.33l-2.08 1.69s-12.68 0 -12.68 0v0.1l4.36 3.76l-0.59 -2.18h10.1l2.48 -2.57h0l-0.09 -9.44Z"/><path d="M2.88 15.89l1.51 -1.3l0.05 -7.33l2.09 -1.67s12.68 0.08 12.68 0.08v-0.1s-4.34 -3.79 -4.34 -3.79l0.58 2.18h0l-10.1 -0.07l-2.5 2.55h0l0.03 9.44Z"/></g>`,
2276
- stop: `<g fill="currentColor" fill-rule="evenodd"><path d="M20.96 1.6h-17.58l-1.78 1.85v17.51l1.3 -1.84v-14.37l1.64 -1.64h14.58l1.84 -1.51Z"/><path d="M3.09 22.37l17.58 -0.04l1.78 -1.85l-0.04 -17.51l-1.3 1.84l0.03 14.37l-1.64 1.64l-14.58 0.03h0l-1.84 1.52Z"/></g>`,
2277
- menu: `<g fill="currentColor" fill-rule="evenodd"><path d="M1.6 13.52h18.01l2.79 -3.17h-18.01l-2.79 3.17h0Z"/><path d="M1.6 6.04h18.01l2.79 -3.17h-18.01l-2.79 3.17h0Z"/><path d="M1.64 21.02h18.01l2.79 -3.17h-18.01l-2.79 3.17h0Z"/></g>`,
2278
- minus: `<g fill="currentColor" fill-rule="evenodd"><path d="M19.81 13.26l2.59 -2.44l-18.13 -0.08l-2.67 2.52"/></g>`,
2279
- plus: `<g fill="currentColor" fill-rule="evenodd"><path d="M13.16 1.6h0l-2.39 3.82v5.31l-0.08 0.07h-6.36l-2.54 2.4h8.9l0.08 0.07v9.13l2.39 -3.74h0v-5.39l0.07 -0.07h6.36l2.62 -2.4h-8.98l-0.07 -0.07v-9.13Z"/></g>`,
2288
+ spin: `<g fill="currentColor" fill-rule="evenodd"><path d="M13.57 8.41C13.3 8.31 13.51 7.09 13.59 6.65C13.61 6.56 13.55 6.48 13.46 6.48C12.32 6.45 5.74 6.61 4.16 14.85C4.15 14.9 4 15.16 3.8 14.91C3.27 14.23 2.43 12.08 3.46 9.16C6.01 1.92 13 3.07 13.79 3.23C13.83 3.23 13.86 3.2 13.86 3.17C13.85 2.86 13.82 1.68 14.17 1.64C14.51 1.6 19.04 5.61 18.96 5.95C18.86 6.36 13.83 8.51 13.57 8.41Z"/><path d="M10.43 15.59C10.7 15.69 10.49 16.91 10.41 17.35C10.39 17.44 10.45 17.52 10.54 17.52C11.68 17.55 18.26 17.39 19.84 9.15C19.85 9.1 20 8.84 20.2 9.09C20.73 9.77 21.57 11.92 20.54 14.85C17.99 22.08 11 20.93 10.21 20.77C10.17 20.77 10.13 20.8 10.14 20.83C10.15 21.14 10.18 22.32 9.83 22.36C9.49 22.4 4.96 18.39 5.04 18.05C5.14 17.64 10.17 15.49 10.43 15.59Z"/></g>`,
2289
+ turbo1: `<g fill="currentColor" fill-rule="evenodd"><path d="M17.81 2.62L14.41 8.21C14.15 8.64 14.46 9.2 14.97 9.2L18.84 9.21C19.42 9.21 19.72 9.9 19.32 10.31L8.02 22.19C7.89 22.33 7.72 22.4 7.53 22.4L7.53 22.4C7.08 22.39 6.77 21.93 6.93 21.5L9.91 13.91C10.08 13.48 9.76 13.01 9.29 13.01L5.11 13.06C4.61 13.06 4.28 12.53 4.53 12.09L10.07 1.94C10.19 1.73 10.41 1.6 10.65 1.6L17.26 1.62C17.77 1.62 18.08 2.18 17.81 2.62Z"/></g>`,
2290
+ autoplay: `<g fill="currentColor" fill-rule="evenodd"><path d="M19.97 12.41C20.22 11.94 20.95 11.88 20.97 12.47C21.14 16.4 18.71 19.92 14.44 20.97C8.59 22.4 5.29 18.55 5.28 18.52C5.2 18.33 4.41 19.77 4.19 19.59C3.81 19.28 3.78 14.9 3.9 14.78C4.01 14.67 8.39 14.95 8.48 15.31C8.58 15.64 7.2 16.1 7.4 16.38C9.14 18.67 15.82 20.29 19.97 12.41Z"/><path d="M9.3 15.36L9.3 8.67C9.3 8.32 9.97 7.74 10.58 8.14C15.83 11.56 16 11.46 16.01 12.04C16.02 12.61 15.12 12.98 10.63 15.91C10.32 16.11 9.29 15.72 9.29 15.36Z"/><path d="M4.03 11.59C3.78 12.06 3.05 12.12 3.03 11.53C2.86 7.6 5.29 4.08 9.56 3.03C15.41 1.6 18.71 5.45 18.72 5.48C18.8 5.67 19.59 4.23 19.81 4.41C20.19 4.72 20.22 9.1 20.1 9.22C19.99 9.33 15.61 9.05 15.52 8.69C15.42 8.36 16.8 7.9 16.6 7.62C14.86 5.33 8.18 3.71 4.03 11.59Z"/></g>`,
2291
+ stop: `<g fill="currentColor" fill-rule="evenodd"><rect x="1.6" y="1.6" width="20.8" height="20.8" rx="2.9"/></g>`,
2292
+ menu: `<g fill="currentColor" fill-rule="evenodd"><rect x="1.61" y="3.81" width="20.79" height="4.47" rx="2.23"/><rect x="1.6" y="9.77" width="20.79" height="4.46" rx="2.23"/><rect x="1.6" y="15.72" width="20.79" height="4.47" rx="2.23"/></g>`,
2293
+ minus: `<g fill="currentColor" fill-rule="evenodd"><rect x="1.6" y="9.86" width="20.8" height="4.28" rx="2.11"/></g>`,
2294
+ plus: `<g fill="currentColor" fill-rule="evenodd"><path d="M19.72 14.37L13.84 14.37L13.84 20.33C13.84 21.48 12.89 22.4 11.74 22.36C10.61 22.36 9.7 21.45 9.7 20.33L9.7 14.37L3.67 14.37C2.52 14.37 1.6 13.42 1.63 12.27C1.63 11.15 2.55 10.23 3.67 10.23L9.7 10.23L9.7 4.28C9.71 1.61 13.83 1.6 13.84 4.28L13.84 10.23L19.72 10.23C22.39 10.24 22.4 14.36 19.72 14.37Z"/></g>`,
2280
2295
  gift: `<rect x="4" y="9" width="16" height="11" rx="2" fill="currentColor"/><path d="M9 9c-1.35 -0.31 -2.18 -1.65 -1.87 -3s1.65 -2.18 3 -1.87c0.93 0.22 1.65 0.94 1.87 1.87c0.31 -1.35 1.65 -2.18 3 -1.87c1.35 0.31 2.18 1.65 1.87 3c-0.22 0.93 -0.94 1.65 -1.87 1.87h-6Z" fill="currentColor"/><rect x="11" y="9" width="2" height="11" fill="rgba(0,0,0,.35)"/>`,
2281
- info: `<g fill="currentColor" fill-rule="evenodd"><path d="M14.22 7.78l-3.09 0.08s-1.82 1.49 -1.96 1.43h0.98l-2.03 13.03h0l5.5 -4.37h-1.66"/><path d="M15.96 1.6h-3.02c-0.86 1.04 -2.06 2.98 -2.86 4.07l2.71 -0.08c0.84 -0.95 2.43 -2.96 3.17 -3.99Z"/></g>`,
2282
- soundOn: `<g fill="currentColor" fill-rule="evenodd"><path d="M18.88 10.12l-2.27 -3.44l0.55 1.72l0.24 1.57v3.91l-0.24 1.56l-0.55 1.72l2.27 -3.44"/><path d="M20.91 13.33c0.08 1.91 -0.87 4.39 -1.64 6.1c1.12 -1.38 2.35 -3.64 3.13 -5.24c0 0 0 -4.61 0 -4.61c-0.75 -1.65 -2 -3.83 -3.13 -5.24c0.85 1.78 1.69 4.08 1.64 6.1"/><path d="M13.17 1.76c-1.7 1.45 -5.26 4.82 -6.88 6.41l-4.69 0.08v7.35c0.14 0.14 4.37 0.65 4.69 0.7c1.67 1.52 5.13 4.54 6.88 5.94v-20.48Z"/><path d="M11.69 5.59l0.23 0.08v13.37l-0.23 0.08l-4.62 -4.3l-2.5 -0.32l-0.15 -0.15v-3.84l0.15 -0.15l2.5 -0.08l4.62 -4.69Z"/></g>`,
2283
- soundOff: `<g fill="currentColor" fill-rule="evenodd"><path d="M13.68 13.18l-1.15 1.22v4.04l-0.23 0.07l-1.9 -1.75l-1.07 1.07l3.51 3.05l0.91 0.68v-8.38h-0.07Z"/><path d="M17.71 8.69l-0.22 0.22l0.07 0.99s0.22 4.19 -0.53 5.49l1.75 -2.51v-2.59l-1.07 -1.6Z"/><path d="M19.47 6.63l1.06 3.5v2.9l-1.22 3.88l2.21 -3.73v-3.28l-2.05 -3.27Z"/><path d="M20.91 2.21c-1.64 1.63 -5.41 5.42 -6.93 7.09l-0.23 -0.08v-7.62c-1.67 1.43 -5.11 4.69 -6.7 6.25l-4.65 0.07l0.08 0.08v7.16l4.03 0.54l1.22 -1.38l-2.43 -0.3l-0.08 -0.08v-3.81l2.59 -0.23l4.49 -4.57l0.23 0.08v5.49c-3.03 3.13 -7.21 8.09 -9.98 11.5c6.55 -5.77 13.23 -13.13 18.36 -20.19Z"/></g>`,
2284
- close: `<g fill="currentColor" fill-rule="evenodd"><path d="M16.89 5.27l-4.77 5.02l-4.77 -4.9l-5.14 -3.3l3.67 5.14l4.53 4.77l-5.05 5.19l-3.52 5.21l5.39 -3.67l4.77 -5.02h0.12l4.9 5.02l5.14 3.43l-3.5 -4.99l-4.95 -5.05v-0.24l4.82 -5.08l3.63 -5.19"/></g>`,
2285
- back: `<path d="M15 6l-6 6l6 6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>`,
2286
- chevronRight: `<path d="M9 6l6 6l-6 6" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>`,
2296
+ info: `<g fill="currentColor" fill-rule="evenodd"><circle cx="11.98" cy="4.27" r="2.67"/><path d="M16.05 21.09L16.05 21.54C16.05 22.02 15.67 22.4 15.19 22.4L8.89 22.4C8.42 22.4 8.04 22.02 8.04 21.54L8.04 20.98C8.04 20.5 8.42 20.12 8.89 20.12L8.89 20.12C9.13 20.12 9.32 19.93 9.32 19.7L9.32 11.59C9.32 11.29 9.08 11.05 8.8 11.05C8.33 11.05 7.95 10.67 7.95 10.19L7.95 9.18C7.95 8.7 8.33 8.32 8.8 8.32L9.32 8.32C9.32 8.32 13.8 8.32 13.8 8.32C14.27 8.32 14.65 8.71 14.65 9.18L14.65 19.7C14.65 19.93 14.84 20.12 15.07 20.12L15.07 20.12C15.61 20.12 16.05 20.56 16.05 21.09Z"/></g>`,
2297
+ soundOn: `<g fill="currentColor" fill-rule="evenodd"><path d="M1.6 15.04L1.6 9.18C1.6 8.75 1.95 8.4 2.38 8.4L5.03 8.4C5.21 8.4 5.39 8.36 5.56 8.28L11.6 5.26C12.12 5 12.73 5.38 12.73 5.96L12.73 18.27C12.73 18.85 12.12 19.22 11.6 18.96L5.56 15.94C5.39 15.86 5.22 15.82 5.03 15.82L2.38 15.82C1.95 15.82 1.6 15.47 1.6 15.04Z"/><path d="M16.01 12.11C16.04 12.36 15.67 14.7 14.7 15.23C13.92 15.42 13.54 14.77 13.86 14.1C14.89 12.56 14.88 11.66 13.86 10.13C13.54 9.46 13.92 8.8 14.7 8.99C15.67 9.53 16.06 11.81 16.01 12.11Z"/><path d="M19.15 12.12C19.2 12.51 18.6 16.4 17.3 17.05C16.32 17.54 15.99 16.87 16.42 15.81C17.8 13.4 17.59 9.93 16.42 8.41C15.73 7.5 16.34 6.58 17.26 7.18C18.74 8.15 19.21 11.64 19.15 12.12Z"/><path d="M22.4 12.16C22.16 15.18 21 17.95 19.71 18.82C18.73 19.48 18.22 18.42 18.93 17.32C20.96 14.17 21.92 9.95 18.9 6.29C18.13 5.35 19.35 4.52 20.11 5.48C21.43 7.14 22.33 9.14 22.4 12.16Z"/></g>`,
2298
+ soundOff: `<g fill="currentColor" fill-rule="evenodd"><path d="M1.6 14.77L1.6 9.23C1.6 8.82 1.93 8.49 2.34 8.49L4.85 8.49C5.02 8.49 5.19 8.45 5.34 8.38L11.06 5.52C11.55 5.27 12.12 5.63 12.12 6.18L12.12 17.82C12.12 18.37 11.55 18.73 11.06 18.48L5.34 15.62C5.19 15.55 5.02 15.51 4.85 15.51L2.34 15.51C1.93 15.51 1.6 15.18 1.6 14.77Z"/><path d="M21.95 16.53C21.5 16.98 20.78 16.98 20.33 16.53L17.79 13.99L15.25 16.54C14.8 16.99 14.07 16.99 13.62 16.54C13.17 16.09 13.17 15.36 13.62 14.92L16.17 12.37L13.63 9.83C13.18 9.38 13.18 8.65 13.63 8.21C13.85 7.98 14.14 7.87 14.44 7.87C14.73 7.87 15.02 7.98 15.25 8.21L17.79 10.75L20.33 8.21C20.77 7.76 21.5 7.76 21.95 8.21C22.17 8.44 22.28 8.73 22.28 9.02C22.28 9.32 22.17 9.61 21.95 9.83L19.41 12.37L21.95 14.91C22.4 15.36 22.4 16.08 21.95 16.53Z"/></g>`,
2299
+ close: `<g fill="currentColor" fill-rule="evenodd"><path d="M21.39 21.37C20.38 22.38 18.74 22.38 17.73 21.37L12.01 15.64L6.27 21.38C5.26 22.39 3.62 22.39 2.61 21.38C1.6 20.38 1.6 18.74 2.61 17.73L8.35 11.99L2.62 6.25C1.61 5.24 1.61 3.61 2.62 2.6C3.12 2.09 3.79 1.84 4.45 1.84C5.11 1.84 5.77 2.09 6.28 2.6L12.01 8.33L17.72 2.62C18.73 1.61 20.37 1.61 21.38 2.62C21.89 3.12 22.14 3.78 22.14 4.44C22.14 5.11 21.89 5.77 21.38 6.27L15.67 11.99L21.39 17.71C22.4 18.72 22.4 20.36 21.39 21.37Z"/></g>`,
2300
+ back: `<g fill="currentColor" fill-rule="evenodd"><path d="M17.49 2.32L17.49 2.32C18.2 3.03 18.2 4.19 17.49 4.9L11.68 10.71C10.97 11.42 10.97 12.58 11.68 13.29L17.49 19.1C18.21 19.81 18.21 20.97 17.49 21.69L17.49 21.69C16.78 22.4 15.62 22.4 14.91 21.69L7.8 14.58L7.8 14.58C7.8 14.58 6.51 13.29 6.51 13.29C5.79 12.58 5.79 11.42 6.51 10.71L14.9 2.31C15.62 1.6 16.77 1.6 17.49 2.31Z"/></g>`,
2301
+ chevronRight: `<g fill="currentColor" fill-rule="evenodd"><path d="M6.51 21.68L6.51 21.68C5.8 20.97 5.8 19.81 6.51 19.1L12.32 13.29C13.03 12.58 13.03 11.42 12.32 10.71L6.51 4.9C5.79 4.19 5.79 3.03 6.51 2.31L6.51 2.31C7.22 1.6 8.38 1.6 9.09 2.31L16.2 9.42L16.2 9.42C16.2 9.42 17.49 10.71 17.49 10.71C18.21 11.42 18.21 12.58 17.49 13.29L9.1 21.69C8.38 22.4 7.23 22.4 6.51 21.69Z"/></g>`,
2287
2302
  ticket: `<path d="M1.72 9.48L19.64 4.67L20.28 7.07C19.19 7.8 18.82 9 19.11 10.09C19.41 11.19 20.33 12.04 21.64 12.13L22.28 14.52L4.36 19.32L3.72 16.93C4.81 16.2 5.18 15 4.89 13.91C4.59 12.81 3.67 11.96 2.36 11.87L1.72 9.48Z" fill="none" stroke="currentColor" stroke-width="1.84" stroke-linejoin="miter"/><path d="M12.57 7.17L9.75 13.26L11.92 12.4L11.84 16.74L14.85 10.06L12.38 11.1Z" fill="currentColor"/>`,
2303
+ turbo2: `<g fill="currentColor" fill-rule="evenodd"><path d="M8.24 8.81L3.92 7.68C3.88 7.67 3.88 7.61 3.92 7.61L9.66 6.45C9.8 6.43 9.89 6.56 9.83 6.68L8.86 8.35C8.67 8.73 8.33 8.83 8.24 8.81Z"/><path d="M20.58 3.04L17.34 8.38C17.09 8.8 17.39 9.33 17.87 9.33L21.57 9.33C22.12 9.33 22.4 9.99 22.02 10.39L11.24 21.73C11.12 21.86 10.95 21.93 10.77 21.92L10.77 21.92C10.34 21.92 10.04 21.47 10.2 21.07L13.04 13.82C13.21 13.41 12.9 12.96 12.45 12.97L8.46 13.01C7.98 13.01 7.67 12.5 7.9 12.08L13.2 2.4C13.31 2.2 13.52 2.07 13.75 2.07L20.05 2.09C20.54 2.1 20.84 2.63 20.58 3.04Z"/><path d="M5.96 12.68L1.64 11.54C1.6 11.54 1.6 11.47 1.64 11.47L7.38 10.31C7.52 10.3 7.61 10.42 7.55 10.55L6.58 12.22C6.39 12.59 6.05 12.69 5.96 12.68Z"/><path d="M9.67 16.66L5.35 15.53C5.31 15.52 5.31 15.46 5.35 15.46L11.09 14.3C11.22 14.28 11.32 14.41 11.26 14.53L10.29 16.2C10.1 16.58 9.76 16.68 9.67 16.66Z"/></g>`,
2304
+ turboOff: `<g fill="currentColor" fill-rule="evenodd"><path d="M17.81 2.62L14.41 8.21C14.15 8.64 14.46 9.2 14.97 9.2L18.84 9.21C19.42 9.21 19.72 9.9 19.32 10.31L8.02 22.19C7.89 22.33 7.72 22.4 7.53 22.4L7.53 22.4C7.08 22.39 6.77 21.93 6.93 21.5L9.91 13.91C10.08 13.48 9.76 13.01 9.29 13.01L5.11 13.06C4.61 13.06 4.28 12.53 4.53 12.09L10.07 1.94C10.19 1.73 10.41 1.6 10.65 1.6L17.26 1.62C17.77 1.62 18.08 2.18 17.81 2.62Z"/></g>`,
2305
+ chevronUp: `<g fill="currentColor" fill-rule="evenodd"><path d="M21.68 17.49L21.68 17.49C20.97 18.2 19.81 18.2 19.1 17.49L13.29 11.68C12.58 10.97 11.42 10.97 10.71 11.68L4.9 17.49C4.19 18.21 3.03 18.21 2.31 17.49L2.31 17.49C1.6 16.78 1.6 15.62 2.31 14.91L9.42 7.8L9.42 7.8C9.42 7.8 10.71 6.51 10.71 6.51C11.42 5.79 12.58 5.79 13.29 6.51L21.69 14.9C22.4 15.62 22.4 16.77 21.69 17.49Z"/></g>`,
2306
+ chevronDown: `<g fill="currentColor" fill-rule="evenodd"><path d="M2.32 6.51L2.32 6.51C3.03 5.8 4.19 5.8 4.9 6.51L10.71 12.32C11.42 13.03 12.58 13.03 13.29 12.32L19.1 6.51C19.82 5.79 20.97 5.79 21.69 6.51L21.69 6.51C22.4 7.22 22.4 8.38 21.69 9.09L14.58 16.19L14.58 16.2C14.58 16.2 13.29 17.49 13.29 17.49C12.58 18.21 11.42 18.21 10.71 17.49L2.31 9.1C1.6 8.38 1.6 7.23 2.31 6.51Z"/></g>`,
2288
2307
  };
2289
2308
  /** A standalone, parseable SVG document with `currentColor` resolved to `color`.
2290
2309
  * Used by the Pixi renderer to build vector geometry via GraphicsContext.svg(). */
2291
2310
  function iconSVG(name, color = '#ffffff') {
2292
2311
  return `<svg viewBox="0 0 24 24">${SVGS[name].split('currentColor').join(color)}</svg>`;
2293
2312
  }
2294
- /** A standalone SVG with the glyph rendered as a stroke-only outline (fill:none).
2295
- * Used to build the outer-ring effect in Pixi: draw this first (under), then iconSVG on top;
2296
- * only the outer half of the stroke is visible — the paint-order:stroke CSS equivalent. */
2297
- function iconStrokeSVG(name, stroke, width = 4) {
2298
- const frag = SVGS[name].split('fill="currentColor"').join(`fill="none" stroke="${stroke}" stroke-width="${width}" stroke-linejoin="round"`);
2299
- return `<svg viewBox="0 0 24 24">${frag}</svg>`;
2300
- }
2301
2313
 
2314
+ // Icons/coins are parsed from small-viewBox SVGs (24-unit glyphs, ~260-unit coin). Pixi's default
2315
+ // bezierSmoothness (0.5) flattens curves with a ~1-unit path-space tolerance — coarse facets at
2316
+ // that scale that magnify when the glyph is scaled to button size. Max it out (capped at 0.99) so
2317
+ // every SVG-parsed curve stays smooth. Global default; set once, before any GraphicsContext builds.
2318
+ GraphicsContextSystem.defaultOptions.bezierSmoothness = 0.99;
2302
2319
  // The DOM shell renders each icon as an inline <svg> sized to 1em and recoloured via
2303
2320
  // `currentColor`. The Pixi shell rebuilds the same 24×24 vector paths through Pixi's SVG
2304
2321
  // parser (GraphicsContext.svg) and scales them to the requested pixel size — same shapes,
2305
2322
  // crisp at any scale. Recolour rebuilds the geometry with the colour substituted for
2306
2323
  // `currentColor` (recolour happens only on hover/active, so this is cheap).
2307
2324
  const VIEWBOX = 24;
2308
- // Cache parsed contexts by `name|fill|ring|width` — many icons repeat across the bar at the same colour.
2325
+ // Cache parsed contexts by `name|fill` — many icons repeat across the bar at the same colour.
2309
2326
  const cache = new Map();
2310
- function context(name, color, ring, ringWidth = 4) {
2311
- const key = `${name}|${color}|${ring ?? ''}|${ring ? ringWidth : 0}`;
2327
+ function context(name, color) {
2328
+ const key = `${name}|${color}`;
2312
2329
  let ctx = cache.get(key);
2313
2330
  if (!ctx) {
2314
- if (ring) {
2315
- // Two-pass layering: stroke context first (under), then fill on top.
2316
- // Only the outer half of the stroke shows — the paint-order:stroke CSS equivalent.
2317
- ctx = new GraphicsContext()
2318
- .svg(iconStrokeSVG(name, ring, ringWidth))
2319
- .svg(iconSVG(name, color));
2320
- }
2321
- else {
2322
- ctx = new GraphicsContext().svg(iconSVG(name, color));
2323
- }
2331
+ ctx = new GraphicsContext().svg(iconSVG(name, color));
2324
2332
  cache.set(key, ctx);
2325
2333
  }
2326
2334
  return ctx;
2327
2335
  }
2328
2336
  /** A recolourable, scalable icon. Its content is centred on the local origin's box so the view's
2329
- * bounds are `size × size` and `rotation` spins around the glyph centre (like CSS 50% 50%).
2330
- *
2331
- * Optional `ring` and `ringWidth` enable the outer-ring treatment (fill-on-top-of-stroke): the
2332
- * stroke is drawn first (underneath), then the fill path is drawn on top — only the outer half
2333
- * of the stroke is visible, mirroring the DOM `paint-order:stroke` technique. */
2337
+ * bounds are `size × size` and `rotation` spins around the glyph centre (like CSS 50% 50%). */
2334
2338
  class IconView extends Container {
2335
2339
  gfx;
2336
2340
  _size;
2337
2341
  _color;
2338
- _ring;
2339
- _ringWidth;
2340
2342
  iconName;
2341
- constructor(name, size, color = '#ffffff', ring, ringWidth = 4) {
2343
+ constructor(name, size, color = '#ffffff') {
2342
2344
  super();
2343
2345
  this.iconName = name;
2344
2346
  this._size = size;
2345
2347
  this._color = color;
2346
- this._ring = ring;
2347
- this._ringWidth = ringWidth;
2348
- this.gfx = new Graphics(context(name, color, ring, ringWidth));
2348
+ this.gfx = new Graphics(context(name, color));
2349
2349
  this.addChild(this.gfx);
2350
2350
  this.layout();
2351
2351
  }
@@ -2359,20 +2359,11 @@ class IconView extends Container {
2359
2359
  return this._size;
2360
2360
  }
2361
2361
  setColor(color) {
2362
- if (color === this._color && !this._ring)
2362
+ if (color === this._color)
2363
2363
  return;
2364
2364
  this._color = color;
2365
- this._ring = undefined;
2366
2365
  this.gfx.context = context(this.iconName, color);
2367
2366
  }
2368
- /** Update fill colour and ring colour simultaneously (ring is preserved if provided). */
2369
- setColors(fill, ring) {
2370
- if (fill === this._color && ring === this._ring)
2371
- return;
2372
- this._color = fill;
2373
- this._ring = ring;
2374
- this.gfx.context = context(this.iconName, fill, ring, this._ringWidth);
2375
- }
2376
2367
  setSize(size) {
2377
2368
  if (size === this._size)
2378
2369
  return;
@@ -2399,11 +2390,6 @@ class IconView extends Container {
2399
2390
  function makeIcon(name, size, color = '#ffffff') {
2400
2391
  return new IconView(name, size, color);
2401
2392
  }
2402
- /** Create a ringed icon — glyph filled with `fill`, outer ring coloured `ring` (width `ringWidth`).
2403
- * Uses the two-pass stroke-under-fill technique to show only the outer half of the stroke. */
2404
- function makeRingedIcon(name, size, fill, ring, ringWidth = 4) {
2405
- return new IconView(name, size, fill, ring, ringWidth);
2406
- }
2407
2393
 
2408
2394
  // Shared interactive primitives, ported from the DOM shell's CSS rules. Each widget reproduces
2409
2395
  // one CSS class (.ge-iconbtn, .ge-rd, .ge-shell-spin, .ge-shell-buybonus, .ge-chip, .ge-slider …)
@@ -2674,6 +2660,34 @@ function drawGlow(glow, size, accent, on) {
2674
2660
  const r = size / 2;
2675
2661
  glow.circle(r, r, r + 6).fill({ color: accent, alpha: 0.7 }); // blurred by the glow's filter → halo
2676
2662
  }
2663
+ // Each buy-bonus coin variant (default/social/disabled) is parsed once from its shared SVG art
2664
+ // (same vectors as the DOM shell), keyed by the art string.
2665
+ const _coinCache = new Map();
2666
+ function coinData(art) {
2667
+ let d = _coinCache.get(art);
2668
+ if (!d) {
2669
+ const m = art.match(/viewBox="([-\d.]+) ([-\d.]+) ([-\d.]+) ([-\d.]+)"/);
2670
+ d = {
2671
+ ctx: new GraphicsContext().svg(art),
2672
+ vb: m ? { x: +m[1], y: +m[2], w: +m[3], h: +m[4] } : { x: 0, y: 0, w: 24, h: 24 },
2673
+ };
2674
+ _coinCache.set(art, d);
2675
+ }
2676
+ return d;
2677
+ }
2678
+ /** A Container holding the coin `art` scaled to fit `size`, pivoted on its centre so it can pulse. */
2679
+ function buildCoin(size, art) {
2680
+ const { ctx, vb } = coinData(art);
2681
+ const wrap = new Container();
2682
+ const g = new Graphics(ctx);
2683
+ const s = size / Math.max(vb.w, vb.h);
2684
+ g.scale.set(s);
2685
+ g.position.set(-vb.x * s + (size - vb.w * s) / 2, -vb.y * s + (size - vb.h * s) / 2);
2686
+ wrap.addChild(g);
2687
+ wrap.pivot.set(size / 2, size / 2);
2688
+ wrap.position.set(size / 2, size / 2);
2689
+ return wrap;
2690
+ }
2677
2691
  class SpinDisc extends Container {
2678
2692
  size;
2679
2693
  glyphSize;
@@ -2731,8 +2745,9 @@ class SpinDisc extends Container {
2731
2745
  drawDisc(this.disc, this.size, this.tokens.btn, 4);
2732
2746
  const glyphColor = hot ? this.tokens.accent : this.tokens.btnInk;
2733
2747
  this.glyph.setColor(glyphColor);
2748
+ // the count sits ON the solid (btnInk) STOP square, so it must be light to read
2734
2749
  if (this.countText)
2735
- this.countText.style.fill = hot ? this.tokens.accent : this.tokens.btnInk;
2750
+ this.countText.style.fill = '#ffffff';
2736
2751
  // Disabled (mid-spin / can't spin): darken OPAQUELY (≈ filter:grayscale(.4) brightness(.62))
2737
2752
  // with a dark veil over the disc — not alpha, which would let the bright board show through and
2738
2753
  // read as a translucent/missing button (what looked "transparent" while spinning).
@@ -2750,7 +2765,7 @@ class SpinDisc extends Container {
2750
2765
  this.glyph.visible = false;
2751
2766
  this.stopGlyph();
2752
2767
  if (!this.countText) {
2753
- this.countText = makeText('', { size: 22, weight: '800', color: this.tokens.btnInk, align: 'center', family: NUM_FONT_FAMILY });
2768
+ this.countText = makeText('', { size: 22, weight: '800', color: '#ffffff', align: 'center', family: NUM_FONT_FAMILY });
2754
2769
  this.addChild(this.countText); // added after the STOP glyph → renders on top of it
2755
2770
  }
2756
2771
  const label = Number.isFinite(remaining) ? String(remaining) : '∞';
@@ -2831,7 +2846,9 @@ class BuyBonusBadge extends Container {
2831
2846
  disc;
2832
2847
  labelText;
2833
2848
  iconView;
2834
- content; // labelText or iconView — the pulsed node
2849
+ content; // labelText / iconView / coin — the pulsed node
2850
+ isCoin = false;
2851
+ dim; // disabled veil over the coin (no filter → crisp, mirrors the spin disc)
2835
2852
  bg;
2836
2853
  fg;
2837
2854
  border;
@@ -2848,7 +2865,14 @@ class BuyBonusBadge extends Container {
2848
2865
  this.ticker = opts.ticker;
2849
2866
  this.disc = new Graphics();
2850
2867
  this.addChild(this.glow, this.disc);
2851
- if (opts.icon) {
2868
+ if (opts.coinArt) {
2869
+ this.isCoin = true;
2870
+ const coin = buildCoin(this.size, opts.coinArt);
2871
+ this.content = coin;
2872
+ this.dim = new Graphics();
2873
+ this.addChild(coin, this.dim); // veil on top for the disabled state
2874
+ }
2875
+ else if (opts.icon) {
2852
2876
  const gs = opts.iconSize ?? this.size * 0.62;
2853
2877
  this.iconView = makeIcon(opts.icon, gs, opts.iconColor ?? '#0b0e16');
2854
2878
  this.iconView.pivot.set(gs / 2, gs / 2); // pulse/scale around the glyph centre
@@ -2881,12 +2905,23 @@ class BuyBonusBadge extends Container {
2881
2905
  });
2882
2906
  }
2883
2907
  paint(hovering) {
2884
- // disabled → grayscale(.5) brightness(.72) baked into the fill/label (no filter → crisp)
2885
- drawDisc(this.disc, this.size, this._disabled ? dimColor(this.bg) : this.bg, this.border);
2886
- if (this.labelText)
2887
- this.labelText.style.fill = this._disabled ? dimColor(this.fg) : this.fg;
2908
+ if (this.isCoin) {
2909
+ // no accent disc the coin IS the button. disabled → dark veil (crisp, no filter).
2910
+ if (this.dim) {
2911
+ this.dim.clear();
2912
+ if (this._disabled)
2913
+ this.dim.circle(this.size / 2, this.size / 2, this.size / 2).fill({ color: 0x000000, alpha: 0.5 });
2914
+ }
2915
+ }
2916
+ else {
2917
+ // disabled → grayscale(.5) brightness(.72) baked into the fill/label (no filter → crisp)
2918
+ drawDisc(this.disc, this.size, this._disabled ? dimColor(this.bg) : this.bg, this.border);
2919
+ if (this.labelText)
2920
+ this.labelText.style.fill = this._disabled ? dimColor(this.fg) : this.fg;
2921
+ }
2888
2922
  const on = hovering && !this._disabled;
2889
- drawGlow(this.glow, this.size, this.bg, on); // soft accent glow only (no ring) `.ge-shell-buybonus:hover`
2923
+ // the coin carries its own gold look no accent halo (mirrors `.ge-bb-coin:hover`)
2924
+ drawGlow(this.glow, this.size, this.bg, this.isCoin ? false : on);
2890
2925
  if (on)
2891
2926
  this.startPulse();
2892
2927
  else
@@ -2998,77 +3033,33 @@ const BAR_REF_W = 840; // zoom reference width
2998
3033
  const BAR_MIN_SCALE = 0.5;
2999
3034
  const WIDE_PAD_BOTTOM = 8;
3000
3035
  const WIDE_BAR_H = WIDE_PAD_BOTTOM + SPIN; // ≈92
3001
- const BUY_W = 62; // buy-bonus disc on desktop
3036
+ const BUY_W = 71; // buy-bonus coin on desktop (+15% over the old 62 disc)
3002
3037
  const DISC = 38; // white-disc icon button box (auto/turbo)
3003
3038
  const FS_BOX = 90; // fixed bet-value box width
3004
3039
  // mobile
3005
3040
  const M_CTRL_H = 62, M_INFO_H = 40, M_GAP = 10, M_PAD_BOTTOM = 8, M_SIDE = 10;
3006
- const M_BUY = 50;
3041
+ const M_BUY = 58; // mobile buy-bonus coin (+15%)
3007
3042
  const MOBILE_BAR_H = M_PAD_BOTTOM + SPIN + M_GAP + M_INFO_H; // spin pops above the controls
3008
- /** Three-state turbo button: single `turbo1` bolt on a white disc, with fill+ring treatment
3009
- * matching the DOM redesign. Level is conveyed by fill colour + ring colour (not glyph swap):
3010
- * - off (0): bolt fill #fff + BLACK ring, container alpha .5
3011
- * - L1: bolt fill #fff + ACCENT ring, alpha 1
3012
- * - L≥2: bolt fill ACCENT + ACCENT ring, alpha 1
3013
- * Hover at any level: bolt fill+ring → accent, disc border → accent (standard bar-button hover). */
3014
- class TurboButton extends Container {
3015
- box;
3016
- view;
3017
- discG;
3018
- discBorder;
3019
- level;
3020
- accent;
3021
- constructor(opts) {
3022
- super();
3023
- this.box = opts.size;
3024
- this.accent = opts.accent;
3025
- this.level = opts.level;
3026
- this.discBorder = opts.discBorder ?? 2;
3027
- this.discG = new Graphics();
3028
- this.addChild(this.discG);
3029
- const { fill, ring } = this._colors(false);
3030
- this.view = makeRingedIcon('turbo1', opts.glyph, fill, ring);
3031
- this.view.position.set((this.box - opts.glyph) / 2, (this.box - opts.glyph) / 2);
3032
- this.addChild(this.view);
3033
- // off is no longer dimmed — it reads as off via the black ring (DOM dropped the opacity:.5 rule)
3034
- this.eventMode = 'static';
3035
- this.cursor = 'pointer';
3036
- this.hitArea = new Rectangle(0, 0, this.box, this.box);
3037
- this._paint(false);
3038
- attachHover(this, () => this._paint(true), () => this._paint(false));
3039
- attachPress(this, 0.92, () => opts.onTap());
3040
- }
3041
- _colors(hovering) {
3042
- if (hovering)
3043
- return { fill: this.accent, ring: this.accent };
3044
- if (this.level === 0)
3045
- return { fill: '#ffffff', ring: '#000000' };
3046
- if (this.level === 1)
3047
- return { fill: '#ffffff', ring: this.accent };
3048
- return { fill: this.accent, ring: this.accent };
3049
- }
3050
- _paint(hovering) {
3051
- const { fill, ring } = this._colors(hovering);
3052
- this.view.setColors(fill, ring);
3053
- const r = this.box / 2 - this.discBorder / 2;
3054
- this.discG.clear();
3055
- this.discG.circle(this.box / 2, this.box / 2, r).fill('#ffffff');
3056
- // border lights accent on hover OR when engaged (level>0) — mirrors DOM .ge-iconbtn.ge-active
3057
- const ringActive = hovering || this.level > 0;
3058
- this.discG.stroke({ color: ringActive ? this.accent : '#000000', width: this.discBorder });
3059
- }
3060
- /** Update turbo level and repaint (called when the shell re-renders the bar). */
3061
- setLevel(level, accent) {
3062
- this.level = level;
3063
- this.accent = accent;
3064
- this._paint(false);
3065
- }
3066
- measureSize() {
3067
- return { w: this.box, h: this.box };
3068
- }
3069
- setLayoutSize() {
3070
- /* fixed */
3071
- }
3043
+ /** Three-state turbo glyph: the level is shown by SWAPPING the glyph (off → single bolt `turboOff`,
3044
+ * L1 single bolt `turbo1`, L≥2 bolt-with-speed-lines `turbo2`). off = svg turbo-off grey (#ccc),
3045
+ * engaged/hover = accent. On desktop it sits on the white disc (like auto); mobile is disc-less. */
3046
+ function turboGlyph(level) {
3047
+ if (level <= 0)
3048
+ return 'turboOff';
3049
+ return level >= 2 ? 'turbo2' : 'turbo1';
3050
+ }
3051
+ function makeTurboButton(host, level, onTap, size = DISC, glyph = 19, disc = false) {
3052
+ const { tokens } = host;
3053
+ return new IconButton(turboGlyph(level), {
3054
+ size,
3055
+ glyph,
3056
+ ...(disc ? { disc: tokens.btn, discBorder: 2 } : {}),
3057
+ color: '#cccccc', // svg turbo-off grey (off); engaged/hover switch to accent
3058
+ hover: tokens.accent,
3059
+ activeColor: tokens.accent,
3060
+ active: level > 0,
3061
+ onTap,
3062
+ });
3072
3063
  }
3073
3064
  /** A readout in the bar (white Oswald value, plaque-label caption, no shadow). */
3074
3065
  function readout(host, label, value, opts = {}) {
@@ -3237,15 +3228,7 @@ class BottomBar extends Container {
3237
3228
  }));
3238
3229
  }
3239
3230
  if (config.features.turbo > 0) {
3240
- this.turboBtn = new TurboButton({
3241
- size: DISC,
3242
- glyph: 19,
3243
- discFill: tokens.btn,
3244
- discBorder: 2,
3245
- accent: tokens.accent,
3246
- level: state.turbo,
3247
- onTap: () => this.onTurbo(),
3248
- });
3231
+ this.turboBtn = makeTurboButton(this.host, state.turbo, () => this.onTurbo(), DISC, 19, true);
3249
3232
  spinWrap.add(this.turboBtn);
3250
3233
  }
3251
3234
  const right = new FlexBox({ direction: 'row', align: 'center', gap: ZONE_GAP });
@@ -3261,14 +3244,13 @@ class BottomBar extends Container {
3261
3244
  return null;
3262
3245
  const feature = state.activeFeature;
3263
3246
  if (feature) {
3264
- const accent = effectiveAccent(feature);
3247
+ // a feature is active → the coin becomes the "deactivate" variant
3265
3248
  return new BuyBonusBadge({
3266
3249
  size,
3267
- fontSize,
3268
3250
  border,
3269
- bg: accent,
3270
- fg: contrastText(accent),
3271
- label: this.host.t('DISABLE'),
3251
+ bg: effectiveAccent(feature),
3252
+ coinArt: BUY_BONUS_DISABLED_ART,
3253
+ label: '',
3272
3254
  tokens,
3273
3255
  ticker: this.host.ticker,
3274
3256
  onTap: () => this.host.actions.deactivateFeature(),
@@ -3278,9 +3260,7 @@ class BottomBar extends Container {
3278
3260
  size,
3279
3261
  border,
3280
3262
  bg: tokens.accent,
3281
- icon: 'ticket',
3282
- iconSize: size * 0.55,
3283
- iconColor: tokens.btnInk,
3263
+ coinArt: config.isSocial ? BUY_BONUS_SOCIAL_ART : BUY_BONUS_ART,
3284
3264
  label: '',
3285
3265
  tokens,
3286
3266
  ticker: this.host.ticker,
@@ -3343,15 +3323,7 @@ class BottomBar extends Container {
3343
3323
  this.autoBtn.setGlow(true);
3344
3324
  }
3345
3325
  if (config.features.turbo > 0) {
3346
- this.turboBtn = new TurboButton({
3347
- size: 40,
3348
- glyph: 22,
3349
- discFill: tokens.btn,
3350
- discBorder: 2,
3351
- accent: tokens.accent,
3352
- level: state.turbo,
3353
- onTap: () => this.onTurbo(),
3354
- });
3326
+ this.turboBtn = makeTurboButton(this.host, state.turbo, () => this.onTurbo(), 40, 22);
3355
3327
  }
3356
3328
  const buy = isBase ? (this.buildBuy(M_BUY, 9, 2) ?? undefined) : undefined;
3357
3329
  // level 1 — controls bar (dark)
@@ -3576,7 +3548,11 @@ class BottomBar extends Container {
3576
3548
  left.position.set(panelX + PANEL_PAD, panelCenterY - left.outerHeight / 2);
3577
3549
  right.position.set(panelRight - PANEL_PAD - right.outerWidth, panelCenterY - right.outerHeight / 2);
3578
3550
  this.inner.scale.set(s);
3579
- this.inner.position.set((W - barW * s) / 2, H - WIDE_PAD_BOTTOM - SPIN * s);
3551
+ // Scale the bottom gap WITH the bar (…* s), not a flat 8px. On a small/short popout the bar is
3552
+ // fit-scaled down but a constant 8px gap would look like too much air — so the bar floated off the
3553
+ // bottom edge. Scaling matches the HTML shell, whose whole barhost scales from transform-origin
3554
+ // bottom center (gap → 8·s). Desktop (s≈1) is unchanged.
3555
+ this.inner.position.set((W - barW * s) / 2, H - (WIDE_PAD_BOTTOM + SPIN) * s);
3580
3556
  this.measureFootprint();
3581
3557
  }
3582
3558
  applyFitMobile() {
@@ -4310,11 +4286,10 @@ function sectionControls(host, title, width) {
4310
4286
  const box = new Container();
4311
4287
  const badge = new BuyBonusBadge({
4312
4288
  size: 46,
4313
- fontSize: 8,
4314
4289
  border: 2,
4315
4290
  bg: host.tokens.accent,
4316
- fg: '#ffffff',
4317
- label: host.t('BUY BONUS').split(/\s+/).join('\n'),
4291
+ coinArt: host.config.isSocial ? BUY_BONUS_SOCIAL_ART : BUY_BONUS_ART,
4292
+ label: '',
4318
4293
  tokens: host.tokens,
4319
4294
  ticker: host.ticker,
4320
4295
  onTap: () => { },
@@ -5366,11 +5341,11 @@ function volatilityRow(_host, level, accent, size) {
5366
5341
  const n = Math.max(0, Math.min(5, level));
5367
5342
  let x = 0;
5368
5343
  for (let i = 0; i < 5; i++) {
5369
- // active = solid accent bolt; inactive = solid black bolt, dimmed (fill==ring, 1px hairline ring
5370
- // per the DOM: .ge-bonus-vol svg path { stroke-width:1; fill/stroke = accent (on) / #000 (off) })
5344
+ // active = solid accent bolt; inactive = solid black bolt, dimmed
5345
+ // (DOM: .ge-bonus-vol svg path fill = accent (on) / #000 (off))
5371
5346
  const active = i < n;
5372
5347
  const col = active ? accent : '#000000';
5373
- const bolt = makeRingedIcon('turbo1', size, col, col, 1);
5348
+ const bolt = makeIcon('turbo1', size, col);
5374
5349
  bolt.alpha = active ? 1 : 0.5;
5375
5350
  bolt.position.set(x, 0);
5376
5351
  c.addChild(bolt);