@open-slot-ui/core 0.10.0 → 0.11.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.cts CHANGED
@@ -2091,6 +2091,41 @@ declare function auditRulesDoc(doc: RulesDoc, opts?: {
2091
2091
  facts?: GameFacts;
2092
2092
  }): RulesAuditIssue[];
2093
2093
 
2094
+ /**
2095
+ * The ONE in-game rules/menu HTML renderer — the white-card-with-gold-accents
2096
+ * design the info menu shows in every game — as PURE string builders (zero deps,
2097
+ * no DOM required), so the SAME renderer that draws the menu in-game also powers
2098
+ * external tooling (the stakeplate rules editor's WYSIWYG preview, docs, CI
2099
+ * snapshots). `@open-slot-ui/pixi`'s `mountInfoMenu` consumes these — there is
2100
+ * exactly one source of truth for how a rules block looks.
2101
+ */
2102
+
2103
+ /** Escape text for safe HTML interpolation. */
2104
+ declare const escapeHtml: (s: string) => string;
2105
+ /** Escape, then turn `**bold**` runs into `<b>` — the shared inline syntax. */
2106
+ declare const richHtml: (s: string) => string;
2107
+ /**
2108
+ * Render a declarative rules `BlockSpec[]` to the in-game HTML — the full modular
2109
+ * palette. `tr` is the translate function (pass a translate-and-interpolate so
2110
+ * `{{rtp.base}}`-style tokens resolve — see `factsVars`); `facts` feeds the auto
2111
+ * `mode-stats` block.
2112
+ */
2113
+ declare function renderBlocksHtml(blocks: BlockSpec[], tr: (s: string) => string, facts?: GameFacts): string;
2114
+ /**
2115
+ * The "⚠ Rules incomplete" warning card the info menu shows at the top of Rules
2116
+ * when the audit finds forgotten declarations. `tr` localizes the chrome strings
2117
+ * (`openui.rulesAudit.*`) + the finding messages. Empty issues → ''.
2118
+ */
2119
+ declare function renderRulesAuditHtml(issues: RulesAuditIssue[], tr: (s: string) => string): string;
2120
+ /**
2121
+ * The in-game info-menu stylesheet (the white card + gold accents design). One
2122
+ * copy, used by `mountInfoMenu` AND any external preview. Expects the CSS custom
2123
+ * properties `--accent --accent-text --surface --surface-alt --text --text-dim
2124
+ * --card-radius --font` on the `.ohm-root` (see {@link INFO_MENU_VARS}).
2125
+ */
2126
+ declare const INFO_MENU_VARS: Readonly<Record<string, string>>;
2127
+ declare const INFO_MENU_CSS = "\n.ohm-root { position: fixed; inset: 0; z-index: 10000; display: grid; place-items: center; font-family: var(--font); opacity: 0; pointer-events: none; transition: opacity .18s ease; }\n.ohm-root.open { opacity: 1; pointer-events: auto; }\n.ohm-backdrop { position: absolute; inset: 0; background: rgba(8,6,4,0); backdrop-filter: blur(0px) saturate(1); -webkit-backdrop-filter: blur(0px) saturate(1); transition: background .4s ease, backdrop-filter .4s ease, -webkit-backdrop-filter .4s ease; }\n.ohm-root.open .ohm-backdrop { background: rgba(8,6,4,.34); backdrop-filter: blur(6px) saturate(1.1); -webkit-backdrop-filter: blur(6px) saturate(1.1); }\n.ohm-card { position: relative; width: min(92%, 1100px); max-height: 86vh; display: flex; flex-direction: column; background: var(--surface); color: var(--text); border: 1.5px solid #000; border-radius: var(--card-radius); box-shadow: 0 30px 80px rgba(0,0,0,.5); overflow: hidden; transform: translateY(8px) scale(.99); transition: transform .18s ease; }\n.ohm-root.open .ohm-card { transform: none; }\n.ohm-x { position: absolute; top: 18px; right: 22px; width: 46px; height: 46px; border-radius: 999px; border: 0; background: rgba(18,14,10,.82); color: #fff; font-size: 18px; cursor: pointer; display: grid; place-items: center; box-shadow: 0 6px 18px rgba(0,0,0,.45); z-index: 2; transition: transform .12s, background .12s; }\n.ohm-x:hover { transform: scale(1.08); background: rgba(18,14,10,.95); }\n.ohm-body { padding: 24px 26px 26px; overflow-y: scroll; }\n.ohm-body::-webkit-scrollbar { width: 18px; }\n.ohm-body::-webkit-scrollbar-track { background: transparent; margin: 12px 0; }\n.ohm-body::-webkit-scrollbar-thumb { background-color: #111; border: 6px solid transparent; background-clip: padding-box; border-radius: 999px; min-height: 44px; }\n.ohm-body::-webkit-scrollbar-thumb:hover { background-color: #000; }\n.ohm-logo { display: block; margin: 6px auto 18px; max-width: 64%; height: auto; }\n.ohm-logo-text { margin: 6px 0 18px; text-align: center; font-size: 30px; font-weight: 900; letter-spacing: 1px; color: var(--text); }\n.ohm-sec { display: flex; align-items: center; gap: 14px; margin: 26px 0 14px; color: var(--text); font-weight: 800; letter-spacing: 1px; }\n.ohm-sec::before, .ohm-sec::after { content: \"\"; flex: 1; height: 2px; background: color-mix(in srgb, var(--text) 80%, transparent); border-radius: 2px; }\n.ohm-root *, .ohm-root *::before, .ohm-root *::after { box-sizing: border-box; }\n.ohm-row { display: flex; align-items: center; gap: 16px; margin: 14px 0; font-weight: 700; }\n.ohm-setting { margin: 14px 0; }\n.ohm-setting .ohm-row { margin: 0; }\n.ohm-hint { margin: 3px 0 0; font-size: 12.5px; font-weight: 500; color: var(--text-dim); }\n.ohm-row > span:first-child { flex: none; min-width: 110px; }\n.ohm-row input[type=range] { flex: 1; min-width: 0; max-width: min(440px, 60dvw); accent-color: var(--accent); height: 6px; }\n.ohm-row select { flex: 1; min-width: 0; max-width: min(440px, 60dvw); padding: 11px 14px; border-radius: 4px; border: 2px solid var(--accent); background: var(--surface-alt); color: var(--text); font-weight: 700; font-size: 15px; cursor: pointer; }\n.ohm-row select option { background: var(--surface); color: var(--text); font-weight: 600; }\n.ohm-row select option:checked { background: var(--accent); color: var(--accent-text); }\n.ohm-ctl { flex: 1; min-width: 0; max-width: min(440px, 60dvw); display: flex; align-items: center; justify-content: flex-end; }\n.ohm-check input[type=checkbox] { appearance: none; -webkit-appearance: none; width: 50px; height: 28px; border-radius: 999px; background: color-mix(in srgb, var(--text-dim) 38%, transparent); position: relative; cursor: pointer; transition: background .15s; flex: none; }\n.ohm-check input[type=checkbox]:checked { background: var(--accent); }\n.ohm-check input[type=checkbox]::before { content: \"\"; position: absolute; top: 3px; left: 3px; width: 22px; height: 22px; border-radius: 999px; background: #fff; transition: left .15s; box-shadow: 0 1px 3px rgba(0,0,0,.3); }\n.ohm-check input[type=checkbox]:checked::before { left: 25px; }\n.ohm-segmented { display: inline-flex; gap: 4px; padding: 4px; background: var(--surface-alt); border-radius: 999px; border: 1px solid color-mix(in srgb, var(--text-dim) 30%, transparent); }\n.ohm-seg { border: 0; background: transparent; color: var(--text-dim); font-weight: 700; font-size: 14px; padding: 8px 18px; border-radius: 999px; cursor: pointer; transition: background .12s, color .12s; }\n.ohm-seg.active { background: var(--accent); color: var(--accent-text); }\n.ohm-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; }\n.ohm-sym { display: flex; align-items: center; gap: 16px; padding: 6px 4px; }\n.ohm-emoji { font-size: 48px; line-height: 1; filter: drop-shadow(0 2px 4px rgba(0,0,0,.3)); }\n.ohm-symimg { width: 56px; height: 56px; object-fit: contain; flex: none; }\n.ohm-pay { font-size: 13px; line-height: 1.6; }\n.ohm-pay div { display: flex; gap: 6px; }\n.ohm-pay b { min-width: 42px; }\n.ohm-pay span { color: var(--accent); font-weight: 700; }\n.ohm-body p { color: var(--text-dim); line-height: 1.6; }\n.ohm-body p b { color: var(--text); }\n.ohm-feature { display: block; width: 100%; height: auto; margin: 10px 0; }\n.ohm-stats { margin: 12px 0; display: grid; grid-template-columns: 1fr 1fr; gap: 0 28px; }\n.ohm-stats > div { display: flex; justify-content: space-between; padding: 9px 0; border-bottom: 1px solid color-mix(in srgb, var(--text-dim) 20%, transparent); }\n.ohm-stats dt { color: var(--text-dim); margin: 0; } .ohm-stats dd { margin: 0; font-weight: 700; }\n.ohm-audit { margin: 12px 0 18px; padding: 14px 16px; border-radius: 12px; border: 2px solid #d03131; background: color-mix(in srgb, #d03131 8%, transparent); }\n.ohm-audit-title { font-weight: 900; letter-spacing: .5px; color: #b31d1d; }\n.ohm-audit-sub { margin-top: 8px; font-size: 13px; font-weight: 800; color: #b31d1d; }\n.ohm-audit-sub--rec { color: #b07d09; }\n.ohm-audit ul { margin: 6px 0 0; padding-left: 20px; color: var(--text); font-size: 13.5px; line-height: 1.55; }\n.ohm-audit li { margin: 3px 0; }\n.ohm-audit-rec ul { color: var(--text-dim); }\n.ohm-callout { margin: 16px 0 4px; padding: 14px 16px; border-radius: 12px; border-left: 4px solid var(--accent); background: color-mix(in srgb, var(--accent) 9%, transparent); }\n.ohm-callout b { color: var(--accent); } .ohm-callout p { margin: 4px 0 0; color: var(--text); }\n.ohm-callout--warning { border-left-color: #e0a106; background: color-mix(in srgb, #e0a106 10%, transparent); }\n.ohm-callout--warning b { color: #b07d09; }\n.ohm-subh { margin: 22px 0 8px; font-size: 15px; font-weight: 800; letter-spacing: .5px; color: var(--text); }\n.ohm-legal { font-size: 12px; line-height: 1.6; color: var(--text-dim); opacity: .85; }\n.ohm-hr { border: 0; border-top: 1px solid color-mix(in srgb, var(--text-dim) 30%, transparent); margin: 18px 0; }\n.ohm-media { display: flex; align-items: center; gap: 18px; margin: 14px 0; }\n.ohm-media--right { flex-direction: row-reverse; }\n.ohm-media > img { width: 40%; max-width: 320px; height: auto; flex: none; }\n.ohm-media-body { flex: 1; }\n.ohm-media-body h4 { margin: 0 0 6px; font-size: 16px; color: var(--text); }\n.ohm-media-body p { margin: 0; }\n.ohm-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin: 12px 0; }\n.ohm-fcard { padding: 8px 6px; text-align: center; }\n.ohm-fcard img { display: block; width: 48px; height: 48px; margin: 0 auto 8px; }\n.ohm-fcard h5 { margin: 0 0 4px; font-size: 14px; color: var(--text); }\n.ohm-fcard p { margin: 0; font-size: 12px; line-height: 1.5; }\n.ohm-table { width: 100%; border-collapse: collapse; margin: 12px 0; font-size: 14px; }\n.ohm-table th { text-align: left; padding: 8px 10px; font-weight: 800; color: var(--text); border-bottom: 2px solid color-mix(in srgb, var(--text-dim) 35%, transparent); }\n.ohm-table td { padding: 8px 10px; border-bottom: 1px solid color-mix(in srgb, var(--text-dim) 18%, transparent); }\n.ohm-table td:first-child { color: var(--text); font-weight: 700; }\n.ohm-table td:not(:first-child) { color: var(--accent); font-weight: 700; }\n.ohm-steps { margin: 12px 0; padding-left: 22px; color: var(--text-dim); line-height: 1.7; }\n.ohm-steps li { margin: 5px 0; }\n.ohm-steps b { color: var(--text); }\n.ohm-group { margin: 8px 0; }\n";
2128
+
2094
2129
  /**
2095
2130
  * The reference HUD expressed as data — the knobs `new OpenUI()` ships with.
2096
2131
  * `createUI(defaultHudSpec)` is byte-equivalent to zero-arg `createUI()` (a test
@@ -2247,4 +2282,4 @@ declare function resolveBetLadder(availableBets: number[] | undefined, defaultBe
2247
2282
  /** Clamp a stake (major units) to the RGS min/max and snap to `stepBet`. Pure. */
2248
2283
  declare function clampBet(amount: number, cfg: RgsBetConfig, divisor?: number): number;
2249
2284
 
2250
- export { API_AMOUNT_DIVISOR, type Anchor, AutoplayControl, type AutoplayLimits, type AutoplayMode, type AutoplayOptions, BLOCK_KINDS, type BlockFactory, type BlockSpec, type Breakpoint, type BuiltPanel, ButtonControl, type ButtonOptions, CURRENCY_TABLE, CardControl, type CardOptions, type ComposeMenuOptions, Control, type ControlOptions, type ControlOverride, ControlRegistry, type ControlSnapshot, type CurrencyInfo, type CurrencySpec, DEFAULT_MENU_TITLES, DEFAULT_NOTICE_ACTION, type DeepPartial, DictionaryTranslator, type Dispose, EventBus, EventLog, type EventLogOptions, type EventMap, Fade, type ForbiddenMatch, type FreeSpinsFact, type GameFacts, type GameModeFact, type HostHooks, type JurisdictionConfig, type KnownControlId, LOCALE_LABELS, type LayoutConfig, type LayoutSpec, type LoggedEvent, type MenuSpec, type NoticeAction, type NoticeOptions, OpenUI, type OpenUIEvents, type OpenUIOptions, type Orientation, PanelControl, type PanelOptions, type PanelSpec, type PanelVariant, Parallel, type Placement, Pulse, RGS_ERROR_KEYS, ReadoutControl, type ReadoutKind, type ReadoutOptions, type RealityCheckOptions, type Rect, type ResponsiveKey, type ResponsiveOverride, type RgsBetConfig, type RgsErrorCode, type RgsErrorOptions, type RulesAuditIssue, type RulesDoc, type ScreenState, SelectControl, type SelectControlOptions, type SelectOption, Sequence, Signal, SliderControl, type SliderOptions, type SocialPhraseIssue, type SoundControls, type SpecIssue, SpinControl, type SpinPress, type SpinSpec, Squish, type StageRect, type StateDef, type StateMap, StepperControl, type StepperOptions, type Subscribable, type Theme, type ThemeChoice, type ThemeIssue, type ThemeOverrides, type ThemePreset, ToggleControl, type ToggleOptions, type Transition, type Translator, TurboControl, type TurboOptions, type TurboSpec, Turn, type UISpec, ValueDisplay, type ValueDisplayOptions, type ViewInspect, applyJurisdiction, applyRulesDoc, auditRules, auditRulesDoc, breakpointFor, buildBetLadder, buildBlocks, buildPanel, buttonBlocks, checkSocialPhrases, clampBet, clampDecimals, clampDigits, clampMinDigits, collectSocialCopy, composeMenu, computeScreen, createUI, defaultHudSpec, defaultLayout, defaultLayoutConfig, defaultTheme, defineUI, dictionary, displayDigits, effect, errorBlocks, extendTheme, factsVars, findForbiddenPhrases, formatAmount, formatAmountPrecise, formatRtp, formatTimes, installResponsive, integerDigits, isRulesDoc, isSafeColor, isSocialCurrency, landscapeDefaultLayouts, mergeFacts, modeStatsItems, neededDecimals, openuiDefaults, openuiSocialDefaults, portraitDefaultLayouts, resolveBetLadder, resolveCurrency, resolvePlacement, resolveTheme, resolveTurboModes, safeAmount, sanitizeThemeOverrides, themePresets, validateSpec, valueFitMaxWidth };
2285
+ export { API_AMOUNT_DIVISOR, type Anchor, AutoplayControl, type AutoplayLimits, type AutoplayMode, type AutoplayOptions, BLOCK_KINDS, type BlockFactory, type BlockSpec, type Breakpoint, type BuiltPanel, ButtonControl, type ButtonOptions, CURRENCY_TABLE, CardControl, type CardOptions, type ComposeMenuOptions, Control, type ControlOptions, type ControlOverride, ControlRegistry, type ControlSnapshot, type CurrencyInfo, type CurrencySpec, DEFAULT_MENU_TITLES, DEFAULT_NOTICE_ACTION, type DeepPartial, DictionaryTranslator, type Dispose, EventBus, EventLog, type EventLogOptions, type EventMap, Fade, type ForbiddenMatch, type FreeSpinsFact, type GameFacts, type GameModeFact, type HostHooks, INFO_MENU_CSS, INFO_MENU_VARS, type JurisdictionConfig, type KnownControlId, LOCALE_LABELS, type LayoutConfig, type LayoutSpec, type LoggedEvent, type MenuSpec, type NoticeAction, type NoticeOptions, OpenUI, type OpenUIEvents, type OpenUIOptions, type Orientation, PanelControl, type PanelOptions, type PanelSpec, type PanelVariant, Parallel, type Placement, Pulse, RGS_ERROR_KEYS, ReadoutControl, type ReadoutKind, type ReadoutOptions, type RealityCheckOptions, type Rect, type ResponsiveKey, type ResponsiveOverride, type RgsBetConfig, type RgsErrorCode, type RgsErrorOptions, type RulesAuditIssue, type RulesDoc, type ScreenState, SelectControl, type SelectControlOptions, type SelectOption, Sequence, Signal, SliderControl, type SliderOptions, type SocialPhraseIssue, type SoundControls, type SpecIssue, SpinControl, type SpinPress, type SpinSpec, Squish, type StageRect, type StateDef, type StateMap, StepperControl, type StepperOptions, type Subscribable, type Theme, type ThemeChoice, type ThemeIssue, type ThemeOverrides, type ThemePreset, ToggleControl, type ToggleOptions, type Transition, type Translator, TurboControl, type TurboOptions, type TurboSpec, Turn, type UISpec, ValueDisplay, type ValueDisplayOptions, type ViewInspect, applyJurisdiction, applyRulesDoc, auditRules, auditRulesDoc, breakpointFor, buildBetLadder, buildBlocks, buildPanel, buttonBlocks, checkSocialPhrases, clampBet, clampDecimals, clampDigits, clampMinDigits, collectSocialCopy, composeMenu, computeScreen, createUI, defaultHudSpec, defaultLayout, defaultLayoutConfig, defaultTheme, defineUI, dictionary, displayDigits, effect, errorBlocks, escapeHtml, extendTheme, factsVars, findForbiddenPhrases, formatAmount, formatAmountPrecise, formatRtp, formatTimes, installResponsive, integerDigits, isRulesDoc, isSafeColor, isSocialCurrency, landscapeDefaultLayouts, mergeFacts, modeStatsItems, neededDecimals, openuiDefaults, openuiSocialDefaults, portraitDefaultLayouts, renderBlocksHtml, renderRulesAuditHtml, resolveBetLadder, resolveCurrency, resolvePlacement, resolveTheme, resolveTurboModes, richHtml, safeAmount, sanitizeThemeOverrides, themePresets, validateSpec, valueFitMaxWidth };
package/dist/index.d.ts CHANGED
@@ -2091,6 +2091,41 @@ declare function auditRulesDoc(doc: RulesDoc, opts?: {
2091
2091
  facts?: GameFacts;
2092
2092
  }): RulesAuditIssue[];
2093
2093
 
2094
+ /**
2095
+ * The ONE in-game rules/menu HTML renderer — the white-card-with-gold-accents
2096
+ * design the info menu shows in every game — as PURE string builders (zero deps,
2097
+ * no DOM required), so the SAME renderer that draws the menu in-game also powers
2098
+ * external tooling (the stakeplate rules editor's WYSIWYG preview, docs, CI
2099
+ * snapshots). `@open-slot-ui/pixi`'s `mountInfoMenu` consumes these — there is
2100
+ * exactly one source of truth for how a rules block looks.
2101
+ */
2102
+
2103
+ /** Escape text for safe HTML interpolation. */
2104
+ declare const escapeHtml: (s: string) => string;
2105
+ /** Escape, then turn `**bold**` runs into `<b>` — the shared inline syntax. */
2106
+ declare const richHtml: (s: string) => string;
2107
+ /**
2108
+ * Render a declarative rules `BlockSpec[]` to the in-game HTML — the full modular
2109
+ * palette. `tr` is the translate function (pass a translate-and-interpolate so
2110
+ * `{{rtp.base}}`-style tokens resolve — see `factsVars`); `facts` feeds the auto
2111
+ * `mode-stats` block.
2112
+ */
2113
+ declare function renderBlocksHtml(blocks: BlockSpec[], tr: (s: string) => string, facts?: GameFacts): string;
2114
+ /**
2115
+ * The "⚠ Rules incomplete" warning card the info menu shows at the top of Rules
2116
+ * when the audit finds forgotten declarations. `tr` localizes the chrome strings
2117
+ * (`openui.rulesAudit.*`) + the finding messages. Empty issues → ''.
2118
+ */
2119
+ declare function renderRulesAuditHtml(issues: RulesAuditIssue[], tr: (s: string) => string): string;
2120
+ /**
2121
+ * The in-game info-menu stylesheet (the white card + gold accents design). One
2122
+ * copy, used by `mountInfoMenu` AND any external preview. Expects the CSS custom
2123
+ * properties `--accent --accent-text --surface --surface-alt --text --text-dim
2124
+ * --card-radius --font` on the `.ohm-root` (see {@link INFO_MENU_VARS}).
2125
+ */
2126
+ declare const INFO_MENU_VARS: Readonly<Record<string, string>>;
2127
+ declare const INFO_MENU_CSS = "\n.ohm-root { position: fixed; inset: 0; z-index: 10000; display: grid; place-items: center; font-family: var(--font); opacity: 0; pointer-events: none; transition: opacity .18s ease; }\n.ohm-root.open { opacity: 1; pointer-events: auto; }\n.ohm-backdrop { position: absolute; inset: 0; background: rgba(8,6,4,0); backdrop-filter: blur(0px) saturate(1); -webkit-backdrop-filter: blur(0px) saturate(1); transition: background .4s ease, backdrop-filter .4s ease, -webkit-backdrop-filter .4s ease; }\n.ohm-root.open .ohm-backdrop { background: rgba(8,6,4,.34); backdrop-filter: blur(6px) saturate(1.1); -webkit-backdrop-filter: blur(6px) saturate(1.1); }\n.ohm-card { position: relative; width: min(92%, 1100px); max-height: 86vh; display: flex; flex-direction: column; background: var(--surface); color: var(--text); border: 1.5px solid #000; border-radius: var(--card-radius); box-shadow: 0 30px 80px rgba(0,0,0,.5); overflow: hidden; transform: translateY(8px) scale(.99); transition: transform .18s ease; }\n.ohm-root.open .ohm-card { transform: none; }\n.ohm-x { position: absolute; top: 18px; right: 22px; width: 46px; height: 46px; border-radius: 999px; border: 0; background: rgba(18,14,10,.82); color: #fff; font-size: 18px; cursor: pointer; display: grid; place-items: center; box-shadow: 0 6px 18px rgba(0,0,0,.45); z-index: 2; transition: transform .12s, background .12s; }\n.ohm-x:hover { transform: scale(1.08); background: rgba(18,14,10,.95); }\n.ohm-body { padding: 24px 26px 26px; overflow-y: scroll; }\n.ohm-body::-webkit-scrollbar { width: 18px; }\n.ohm-body::-webkit-scrollbar-track { background: transparent; margin: 12px 0; }\n.ohm-body::-webkit-scrollbar-thumb { background-color: #111; border: 6px solid transparent; background-clip: padding-box; border-radius: 999px; min-height: 44px; }\n.ohm-body::-webkit-scrollbar-thumb:hover { background-color: #000; }\n.ohm-logo { display: block; margin: 6px auto 18px; max-width: 64%; height: auto; }\n.ohm-logo-text { margin: 6px 0 18px; text-align: center; font-size: 30px; font-weight: 900; letter-spacing: 1px; color: var(--text); }\n.ohm-sec { display: flex; align-items: center; gap: 14px; margin: 26px 0 14px; color: var(--text); font-weight: 800; letter-spacing: 1px; }\n.ohm-sec::before, .ohm-sec::after { content: \"\"; flex: 1; height: 2px; background: color-mix(in srgb, var(--text) 80%, transparent); border-radius: 2px; }\n.ohm-root *, .ohm-root *::before, .ohm-root *::after { box-sizing: border-box; }\n.ohm-row { display: flex; align-items: center; gap: 16px; margin: 14px 0; font-weight: 700; }\n.ohm-setting { margin: 14px 0; }\n.ohm-setting .ohm-row { margin: 0; }\n.ohm-hint { margin: 3px 0 0; font-size: 12.5px; font-weight: 500; color: var(--text-dim); }\n.ohm-row > span:first-child { flex: none; min-width: 110px; }\n.ohm-row input[type=range] { flex: 1; min-width: 0; max-width: min(440px, 60dvw); accent-color: var(--accent); height: 6px; }\n.ohm-row select { flex: 1; min-width: 0; max-width: min(440px, 60dvw); padding: 11px 14px; border-radius: 4px; border: 2px solid var(--accent); background: var(--surface-alt); color: var(--text); font-weight: 700; font-size: 15px; cursor: pointer; }\n.ohm-row select option { background: var(--surface); color: var(--text); font-weight: 600; }\n.ohm-row select option:checked { background: var(--accent); color: var(--accent-text); }\n.ohm-ctl { flex: 1; min-width: 0; max-width: min(440px, 60dvw); display: flex; align-items: center; justify-content: flex-end; }\n.ohm-check input[type=checkbox] { appearance: none; -webkit-appearance: none; width: 50px; height: 28px; border-radius: 999px; background: color-mix(in srgb, var(--text-dim) 38%, transparent); position: relative; cursor: pointer; transition: background .15s; flex: none; }\n.ohm-check input[type=checkbox]:checked { background: var(--accent); }\n.ohm-check input[type=checkbox]::before { content: \"\"; position: absolute; top: 3px; left: 3px; width: 22px; height: 22px; border-radius: 999px; background: #fff; transition: left .15s; box-shadow: 0 1px 3px rgba(0,0,0,.3); }\n.ohm-check input[type=checkbox]:checked::before { left: 25px; }\n.ohm-segmented { display: inline-flex; gap: 4px; padding: 4px; background: var(--surface-alt); border-radius: 999px; border: 1px solid color-mix(in srgb, var(--text-dim) 30%, transparent); }\n.ohm-seg { border: 0; background: transparent; color: var(--text-dim); font-weight: 700; font-size: 14px; padding: 8px 18px; border-radius: 999px; cursor: pointer; transition: background .12s, color .12s; }\n.ohm-seg.active { background: var(--accent); color: var(--accent-text); }\n.ohm-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; }\n.ohm-sym { display: flex; align-items: center; gap: 16px; padding: 6px 4px; }\n.ohm-emoji { font-size: 48px; line-height: 1; filter: drop-shadow(0 2px 4px rgba(0,0,0,.3)); }\n.ohm-symimg { width: 56px; height: 56px; object-fit: contain; flex: none; }\n.ohm-pay { font-size: 13px; line-height: 1.6; }\n.ohm-pay div { display: flex; gap: 6px; }\n.ohm-pay b { min-width: 42px; }\n.ohm-pay span { color: var(--accent); font-weight: 700; }\n.ohm-body p { color: var(--text-dim); line-height: 1.6; }\n.ohm-body p b { color: var(--text); }\n.ohm-feature { display: block; width: 100%; height: auto; margin: 10px 0; }\n.ohm-stats { margin: 12px 0; display: grid; grid-template-columns: 1fr 1fr; gap: 0 28px; }\n.ohm-stats > div { display: flex; justify-content: space-between; padding: 9px 0; border-bottom: 1px solid color-mix(in srgb, var(--text-dim) 20%, transparent); }\n.ohm-stats dt { color: var(--text-dim); margin: 0; } .ohm-stats dd { margin: 0; font-weight: 700; }\n.ohm-audit { margin: 12px 0 18px; padding: 14px 16px; border-radius: 12px; border: 2px solid #d03131; background: color-mix(in srgb, #d03131 8%, transparent); }\n.ohm-audit-title { font-weight: 900; letter-spacing: .5px; color: #b31d1d; }\n.ohm-audit-sub { margin-top: 8px; font-size: 13px; font-weight: 800; color: #b31d1d; }\n.ohm-audit-sub--rec { color: #b07d09; }\n.ohm-audit ul { margin: 6px 0 0; padding-left: 20px; color: var(--text); font-size: 13.5px; line-height: 1.55; }\n.ohm-audit li { margin: 3px 0; }\n.ohm-audit-rec ul { color: var(--text-dim); }\n.ohm-callout { margin: 16px 0 4px; padding: 14px 16px; border-radius: 12px; border-left: 4px solid var(--accent); background: color-mix(in srgb, var(--accent) 9%, transparent); }\n.ohm-callout b { color: var(--accent); } .ohm-callout p { margin: 4px 0 0; color: var(--text); }\n.ohm-callout--warning { border-left-color: #e0a106; background: color-mix(in srgb, #e0a106 10%, transparent); }\n.ohm-callout--warning b { color: #b07d09; }\n.ohm-subh { margin: 22px 0 8px; font-size: 15px; font-weight: 800; letter-spacing: .5px; color: var(--text); }\n.ohm-legal { font-size: 12px; line-height: 1.6; color: var(--text-dim); opacity: .85; }\n.ohm-hr { border: 0; border-top: 1px solid color-mix(in srgb, var(--text-dim) 30%, transparent); margin: 18px 0; }\n.ohm-media { display: flex; align-items: center; gap: 18px; margin: 14px 0; }\n.ohm-media--right { flex-direction: row-reverse; }\n.ohm-media > img { width: 40%; max-width: 320px; height: auto; flex: none; }\n.ohm-media-body { flex: 1; }\n.ohm-media-body h4 { margin: 0 0 6px; font-size: 16px; color: var(--text); }\n.ohm-media-body p { margin: 0; }\n.ohm-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin: 12px 0; }\n.ohm-fcard { padding: 8px 6px; text-align: center; }\n.ohm-fcard img { display: block; width: 48px; height: 48px; margin: 0 auto 8px; }\n.ohm-fcard h5 { margin: 0 0 4px; font-size: 14px; color: var(--text); }\n.ohm-fcard p { margin: 0; font-size: 12px; line-height: 1.5; }\n.ohm-table { width: 100%; border-collapse: collapse; margin: 12px 0; font-size: 14px; }\n.ohm-table th { text-align: left; padding: 8px 10px; font-weight: 800; color: var(--text); border-bottom: 2px solid color-mix(in srgb, var(--text-dim) 35%, transparent); }\n.ohm-table td { padding: 8px 10px; border-bottom: 1px solid color-mix(in srgb, var(--text-dim) 18%, transparent); }\n.ohm-table td:first-child { color: var(--text); font-weight: 700; }\n.ohm-table td:not(:first-child) { color: var(--accent); font-weight: 700; }\n.ohm-steps { margin: 12px 0; padding-left: 22px; color: var(--text-dim); line-height: 1.7; }\n.ohm-steps li { margin: 5px 0; }\n.ohm-steps b { color: var(--text); }\n.ohm-group { margin: 8px 0; }\n";
2128
+
2094
2129
  /**
2095
2130
  * The reference HUD expressed as data — the knobs `new OpenUI()` ships with.
2096
2131
  * `createUI(defaultHudSpec)` is byte-equivalent to zero-arg `createUI()` (a test
@@ -2247,4 +2282,4 @@ declare function resolveBetLadder(availableBets: number[] | undefined, defaultBe
2247
2282
  /** Clamp a stake (major units) to the RGS min/max and snap to `stepBet`. Pure. */
2248
2283
  declare function clampBet(amount: number, cfg: RgsBetConfig, divisor?: number): number;
2249
2284
 
2250
- export { API_AMOUNT_DIVISOR, type Anchor, AutoplayControl, type AutoplayLimits, type AutoplayMode, type AutoplayOptions, BLOCK_KINDS, type BlockFactory, type BlockSpec, type Breakpoint, type BuiltPanel, ButtonControl, type ButtonOptions, CURRENCY_TABLE, CardControl, type CardOptions, type ComposeMenuOptions, Control, type ControlOptions, type ControlOverride, ControlRegistry, type ControlSnapshot, type CurrencyInfo, type CurrencySpec, DEFAULT_MENU_TITLES, DEFAULT_NOTICE_ACTION, type DeepPartial, DictionaryTranslator, type Dispose, EventBus, EventLog, type EventLogOptions, type EventMap, Fade, type ForbiddenMatch, type FreeSpinsFact, type GameFacts, type GameModeFact, type HostHooks, type JurisdictionConfig, type KnownControlId, LOCALE_LABELS, type LayoutConfig, type LayoutSpec, type LoggedEvent, type MenuSpec, type NoticeAction, type NoticeOptions, OpenUI, type OpenUIEvents, type OpenUIOptions, type Orientation, PanelControl, type PanelOptions, type PanelSpec, type PanelVariant, Parallel, type Placement, Pulse, RGS_ERROR_KEYS, ReadoutControl, type ReadoutKind, type ReadoutOptions, type RealityCheckOptions, type Rect, type ResponsiveKey, type ResponsiveOverride, type RgsBetConfig, type RgsErrorCode, type RgsErrorOptions, type RulesAuditIssue, type RulesDoc, type ScreenState, SelectControl, type SelectControlOptions, type SelectOption, Sequence, Signal, SliderControl, type SliderOptions, type SocialPhraseIssue, type SoundControls, type SpecIssue, SpinControl, type SpinPress, type SpinSpec, Squish, type StageRect, type StateDef, type StateMap, StepperControl, type StepperOptions, type Subscribable, type Theme, type ThemeChoice, type ThemeIssue, type ThemeOverrides, type ThemePreset, ToggleControl, type ToggleOptions, type Transition, type Translator, TurboControl, type TurboOptions, type TurboSpec, Turn, type UISpec, ValueDisplay, type ValueDisplayOptions, type ViewInspect, applyJurisdiction, applyRulesDoc, auditRules, auditRulesDoc, breakpointFor, buildBetLadder, buildBlocks, buildPanel, buttonBlocks, checkSocialPhrases, clampBet, clampDecimals, clampDigits, clampMinDigits, collectSocialCopy, composeMenu, computeScreen, createUI, defaultHudSpec, defaultLayout, defaultLayoutConfig, defaultTheme, defineUI, dictionary, displayDigits, effect, errorBlocks, extendTheme, factsVars, findForbiddenPhrases, formatAmount, formatAmountPrecise, formatRtp, formatTimes, installResponsive, integerDigits, isRulesDoc, isSafeColor, isSocialCurrency, landscapeDefaultLayouts, mergeFacts, modeStatsItems, neededDecimals, openuiDefaults, openuiSocialDefaults, portraitDefaultLayouts, resolveBetLadder, resolveCurrency, resolvePlacement, resolveTheme, resolveTurboModes, safeAmount, sanitizeThemeOverrides, themePresets, validateSpec, valueFitMaxWidth };
2285
+ export { API_AMOUNT_DIVISOR, type Anchor, AutoplayControl, type AutoplayLimits, type AutoplayMode, type AutoplayOptions, BLOCK_KINDS, type BlockFactory, type BlockSpec, type Breakpoint, type BuiltPanel, ButtonControl, type ButtonOptions, CURRENCY_TABLE, CardControl, type CardOptions, type ComposeMenuOptions, Control, type ControlOptions, type ControlOverride, ControlRegistry, type ControlSnapshot, type CurrencyInfo, type CurrencySpec, DEFAULT_MENU_TITLES, DEFAULT_NOTICE_ACTION, type DeepPartial, DictionaryTranslator, type Dispose, EventBus, EventLog, type EventLogOptions, type EventMap, Fade, type ForbiddenMatch, type FreeSpinsFact, type GameFacts, type GameModeFact, type HostHooks, INFO_MENU_CSS, INFO_MENU_VARS, type JurisdictionConfig, type KnownControlId, LOCALE_LABELS, type LayoutConfig, type LayoutSpec, type LoggedEvent, type MenuSpec, type NoticeAction, type NoticeOptions, OpenUI, type OpenUIEvents, type OpenUIOptions, type Orientation, PanelControl, type PanelOptions, type PanelSpec, type PanelVariant, Parallel, type Placement, Pulse, RGS_ERROR_KEYS, ReadoutControl, type ReadoutKind, type ReadoutOptions, type RealityCheckOptions, type Rect, type ResponsiveKey, type ResponsiveOverride, type RgsBetConfig, type RgsErrorCode, type RgsErrorOptions, type RulesAuditIssue, type RulesDoc, type ScreenState, SelectControl, type SelectControlOptions, type SelectOption, Sequence, Signal, SliderControl, type SliderOptions, type SocialPhraseIssue, type SoundControls, type SpecIssue, SpinControl, type SpinPress, type SpinSpec, Squish, type StageRect, type StateDef, type StateMap, StepperControl, type StepperOptions, type Subscribable, type Theme, type ThemeChoice, type ThemeIssue, type ThemeOverrides, type ThemePreset, ToggleControl, type ToggleOptions, type Transition, type Translator, TurboControl, type TurboOptions, type TurboSpec, Turn, type UISpec, ValueDisplay, type ValueDisplayOptions, type ViewInspect, applyJurisdiction, applyRulesDoc, auditRules, auditRulesDoc, breakpointFor, buildBetLadder, buildBlocks, buildPanel, buttonBlocks, checkSocialPhrases, clampBet, clampDecimals, clampDigits, clampMinDigits, collectSocialCopy, composeMenu, computeScreen, createUI, defaultHudSpec, defaultLayout, defaultLayoutConfig, defaultTheme, defineUI, dictionary, displayDigits, effect, errorBlocks, escapeHtml, extendTheme, factsVars, findForbiddenPhrases, formatAmount, formatAmountPrecise, formatRtp, formatTimes, installResponsive, integerDigits, isRulesDoc, isSafeColor, isSocialCurrency, landscapeDefaultLayouts, mergeFacts, modeStatsItems, neededDecimals, openuiDefaults, openuiSocialDefaults, portraitDefaultLayouts, renderBlocksHtml, renderRulesAuditHtml, resolveBetLadder, resolveCurrency, resolvePlacement, resolveTheme, resolveTurboModes, richHtml, safeAmount, sanitizeThemeOverrides, themePresets, validateSpec, valueFitMaxWidth };
package/dist/index.js CHANGED
@@ -2860,6 +2860,201 @@ function auditRulesDoc(doc, opts = {}) {
2860
2860
  return auditRules(facts, doc.blocks, { resolve });
2861
2861
  }
2862
2862
 
2863
+ // src/spec/rulesHtml.ts
2864
+ var escapeHtml = (s) => s.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
2865
+ var richHtml = (s) => escapeHtml(s).replace(/\*\*(.+?)\*\*/g, "<b>$1</b>");
2866
+ var esc = escapeHtml;
2867
+ var rich = richHtml;
2868
+ function statGridHtml(items, tr) {
2869
+ const rows = items.map((it) => `<div><dt>${esc(tr(it.label))}</dt><dd>${esc(tr(it.value))}</dd></div>`).join("");
2870
+ return `<dl class="ohm-stats">${rows}</dl>`;
2871
+ }
2872
+ function renderPaytable(rows, tr) {
2873
+ return rows.map((r) => {
2874
+ const icon = r.icon ? `<img class="ohm-symimg" src="${r.icon}" alt="" loading="lazy">` : `<span class="ohm-emoji">${esc(tr(r.symbol ?? ""))}</span>`;
2875
+ const lines = r.payouts.split("\n").map((line) => {
2876
+ const i = line.indexOf(":");
2877
+ return i >= 0 ? `<div><b>${esc(line.slice(0, i))}</b><span>${esc(line.slice(i + 1))}</span></div>` : `<div><span>${esc(line)}</span></div>`;
2878
+ }).join("");
2879
+ return `<div class="ohm-sym">${icon}<div class="ohm-pay">${lines}</div></div>`;
2880
+ }).join("");
2881
+ }
2882
+ function renderBlocksHtml(blocks, tr, facts) {
2883
+ const out = [];
2884
+ for (const b of blocks) {
2885
+ switch (b.kind) {
2886
+ case "text":
2887
+ out.push(`<p>${rich(tr(b.text))}</p>`);
2888
+ break;
2889
+ case "heading":
2890
+ out.push(`<div class="ohm-sec"><span>${esc(tr(b.text))}</span></div>`);
2891
+ break;
2892
+ case "subheading":
2893
+ out.push(`<h4 class="ohm-subh">${esc(tr(b.text))}</h4>`);
2894
+ break;
2895
+ case "legal":
2896
+ out.push(`<p class="ohm-legal">${rich(tr(b.text))}</p>`);
2897
+ break;
2898
+ case "divider":
2899
+ out.push('<hr class="ohm-hr">');
2900
+ break;
2901
+ case "image":
2902
+ out.push(`<img class="ohm-feature" alt="${esc(tr(b.alt ?? ""))}" src="${b.src}" loading="lazy">`);
2903
+ break;
2904
+ case "media": {
2905
+ const img = `<img alt="${esc(tr(b.alt ?? ""))}" src="${b.src}" loading="lazy">`;
2906
+ const body = `<div class="ohm-media-body">${b.title ? `<h4>${esc(tr(b.title))}</h4>` : ""}<p>${rich(tr(b.text))}</p></div>`;
2907
+ out.push(`<div class="ohm-media ohm-media--${b.side ?? "left"}">${img}${body}</div>`);
2908
+ break;
2909
+ }
2910
+ case "cards": {
2911
+ const cards = b.items.map((it) => `<div class="ohm-fcard">${it.icon ? `<img src="${it.icon}" alt="" loading="lazy">` : ""}<h5>${esc(tr(it.title))}</h5>${it.text ? `<p>${rich(tr(it.text))}</p>` : ""}</div>`).join("");
2912
+ out.push(`<div class="ohm-cards">${cards}</div>`);
2913
+ break;
2914
+ }
2915
+ case "paytable":
2916
+ out.push(`<div class="ohm-grid">${renderPaytable(b.rows, tr)}</div>`);
2917
+ break;
2918
+ case "table": {
2919
+ const head = b.columns?.length ? `<thead><tr>${b.columns.map((c) => `<th>${esc(tr(c))}</th>`).join("")}</tr></thead>` : "";
2920
+ const body = b.rows.map((r) => `<tr>${r.map((c) => `<td>${esc(tr(c))}</td>`).join("")}</tr>`).join("");
2921
+ out.push(`<table class="ohm-table">${head}<tbody>${body}</tbody></table>`);
2922
+ break;
2923
+ }
2924
+ case "stat-grid":
2925
+ out.push(statGridHtml(b.items, tr));
2926
+ break;
2927
+ // The AUTO per-mode RTP / Max-win grid — rendered straight from the declared
2928
+ // game facts (+ any host extras), so the table can never drift from the config.
2929
+ // `modeStatsItems` localizes the label PARTS itself ("Max win" · the mode name),
2930
+ // so the grid renderer gets identity-tr — no double translation.
2931
+ case "mode-stats": {
2932
+ const extras = (b.extras ?? []).map((e) => ({ label: tr(e.label), value: tr(e.value) }));
2933
+ out.push(statGridHtml([...modeStatsItems(facts, tr), ...extras], (s) => s));
2934
+ break;
2935
+ }
2936
+ case "steps": {
2937
+ const items = b.items.map((s) => `<li>${rich(tr(s))}</li>`).join("");
2938
+ out.push(b.ordered ? `<ol class="ohm-steps">${items}</ol>` : `<ul class="ohm-steps">${items}</ul>`);
2939
+ break;
2940
+ }
2941
+ case "callout":
2942
+ out.push(`<div class="ohm-callout ohm-callout--${b.tone ?? "info"}">${b.title ? `<b>${esc(tr(b.title))}</b>` : ""}<p>${rich(tr(b.text))}</p></div>`);
2943
+ break;
2944
+ case "group":
2945
+ out.push(`<div class="ohm-group">${b.title ? `<h4 class="ohm-subh">${esc(tr(b.title))}</h4>` : ""}${renderBlocksHtml(b.children, tr, facts)}</div>`);
2946
+ break;
2947
+ }
2948
+ }
2949
+ return out.join("\n");
2950
+ }
2951
+ function renderRulesAuditHtml(issues, tr) {
2952
+ if (!issues.length) return "";
2953
+ const req = issues.filter((i) => i.level === "required");
2954
+ const rec = issues.filter((i) => i.level === "recommended");
2955
+ const list = (items) => `<ul>${items.map((i) => `<li>${esc(tr(i.message))}</li>`).join("")}</ul>`;
2956
+ return `<div class="ohm-audit" role="alert">
2957
+ <div class="ohm-audit-title">\u26A0 ${esc(tr("openui.rulesAudit.title"))}</div>
2958
+ ${req.length ? `<div class="ohm-audit-sub">${esc(tr("openui.rulesAudit.required"))}</div>${list(req)}` : ""}
2959
+ ${rec.length ? `<div class="ohm-audit-sub ohm-audit-sub--rec">${esc(tr("openui.rulesAudit.recommended"))}</div><div class="ohm-audit-rec">${list(rec)}</div>` : ""}
2960
+ </div>`;
2961
+ }
2962
+ var INFO_MENU_VARS = Object.freeze({
2963
+ "--accent": "#d99000",
2964
+ "--accent-text": "#1a1200",
2965
+ "--surface": "#ffffff",
2966
+ "--surface-alt": "#eef1f6",
2967
+ "--text": "#181b20",
2968
+ "--text-dim": "#5b6472",
2969
+ "--card-radius": "8px",
2970
+ "--font": "system-ui, sans-serif"
2971
+ });
2972
+ var INFO_MENU_CSS = `
2973
+ .ohm-root { position: fixed; inset: 0; z-index: 10000; display: grid; place-items: center; font-family: var(--font); opacity: 0; pointer-events: none; transition: opacity .18s ease; }
2974
+ .ohm-root.open { opacity: 1; pointer-events: auto; }
2975
+ .ohm-backdrop { position: absolute; inset: 0; background: rgba(8,6,4,0); backdrop-filter: blur(0px) saturate(1); -webkit-backdrop-filter: blur(0px) saturate(1); transition: background .4s ease, backdrop-filter .4s ease, -webkit-backdrop-filter .4s ease; }
2976
+ .ohm-root.open .ohm-backdrop { background: rgba(8,6,4,.34); backdrop-filter: blur(6px) saturate(1.1); -webkit-backdrop-filter: blur(6px) saturate(1.1); }
2977
+ .ohm-card { position: relative; width: min(92%, 1100px); max-height: 86vh; display: flex; flex-direction: column; background: var(--surface); color: var(--text); border: 1.5px solid #000; border-radius: var(--card-radius); box-shadow: 0 30px 80px rgba(0,0,0,.5); overflow: hidden; transform: translateY(8px) scale(.99); transition: transform .18s ease; }
2978
+ .ohm-root.open .ohm-card { transform: none; }
2979
+ .ohm-x { position: absolute; top: 18px; right: 22px; width: 46px; height: 46px; border-radius: 999px; border: 0; background: rgba(18,14,10,.82); color: #fff; font-size: 18px; cursor: pointer; display: grid; place-items: center; box-shadow: 0 6px 18px rgba(0,0,0,.45); z-index: 2; transition: transform .12s, background .12s; }
2980
+ .ohm-x:hover { transform: scale(1.08); background: rgba(18,14,10,.95); }
2981
+ .ohm-body { padding: 24px 26px 26px; overflow-y: scroll; }
2982
+ .ohm-body::-webkit-scrollbar { width: 18px; }
2983
+ .ohm-body::-webkit-scrollbar-track { background: transparent; margin: 12px 0; }
2984
+ .ohm-body::-webkit-scrollbar-thumb { background-color: #111; border: 6px solid transparent; background-clip: padding-box; border-radius: 999px; min-height: 44px; }
2985
+ .ohm-body::-webkit-scrollbar-thumb:hover { background-color: #000; }
2986
+ .ohm-logo { display: block; margin: 6px auto 18px; max-width: 64%; height: auto; }
2987
+ .ohm-logo-text { margin: 6px 0 18px; text-align: center; font-size: 30px; font-weight: 900; letter-spacing: 1px; color: var(--text); }
2988
+ .ohm-sec { display: flex; align-items: center; gap: 14px; margin: 26px 0 14px; color: var(--text); font-weight: 800; letter-spacing: 1px; }
2989
+ .ohm-sec::before, .ohm-sec::after { content: ""; flex: 1; height: 2px; background: color-mix(in srgb, var(--text) 80%, transparent); border-radius: 2px; }
2990
+ .ohm-root *, .ohm-root *::before, .ohm-root *::after { box-sizing: border-box; }
2991
+ .ohm-row { display: flex; align-items: center; gap: 16px; margin: 14px 0; font-weight: 700; }
2992
+ .ohm-setting { margin: 14px 0; }
2993
+ .ohm-setting .ohm-row { margin: 0; }
2994
+ .ohm-hint { margin: 3px 0 0; font-size: 12.5px; font-weight: 500; color: var(--text-dim); }
2995
+ .ohm-row > span:first-child { flex: none; min-width: 110px; }
2996
+ .ohm-row input[type=range] { flex: 1; min-width: 0; max-width: min(440px, 60dvw); accent-color: var(--accent); height: 6px; }
2997
+ .ohm-row select { flex: 1; min-width: 0; max-width: min(440px, 60dvw); padding: 11px 14px; border-radius: 4px; border: 2px solid var(--accent); background: var(--surface-alt); color: var(--text); font-weight: 700; font-size: 15px; cursor: pointer; }
2998
+ .ohm-row select option { background: var(--surface); color: var(--text); font-weight: 600; }
2999
+ .ohm-row select option:checked { background: var(--accent); color: var(--accent-text); }
3000
+ .ohm-ctl { flex: 1; min-width: 0; max-width: min(440px, 60dvw); display: flex; align-items: center; justify-content: flex-end; }
3001
+ .ohm-check input[type=checkbox] { appearance: none; -webkit-appearance: none; width: 50px; height: 28px; border-radius: 999px; background: color-mix(in srgb, var(--text-dim) 38%, transparent); position: relative; cursor: pointer; transition: background .15s; flex: none; }
3002
+ .ohm-check input[type=checkbox]:checked { background: var(--accent); }
3003
+ .ohm-check input[type=checkbox]::before { content: ""; position: absolute; top: 3px; left: 3px; width: 22px; height: 22px; border-radius: 999px; background: #fff; transition: left .15s; box-shadow: 0 1px 3px rgba(0,0,0,.3); }
3004
+ .ohm-check input[type=checkbox]:checked::before { left: 25px; }
3005
+ .ohm-segmented { display: inline-flex; gap: 4px; padding: 4px; background: var(--surface-alt); border-radius: 999px; border: 1px solid color-mix(in srgb, var(--text-dim) 30%, transparent); }
3006
+ .ohm-seg { border: 0; background: transparent; color: var(--text-dim); font-weight: 700; font-size: 14px; padding: 8px 18px; border-radius: 999px; cursor: pointer; transition: background .12s, color .12s; }
3007
+ .ohm-seg.active { background: var(--accent); color: var(--accent-text); }
3008
+ .ohm-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 14px; }
3009
+ .ohm-sym { display: flex; align-items: center; gap: 16px; padding: 6px 4px; }
3010
+ .ohm-emoji { font-size: 48px; line-height: 1; filter: drop-shadow(0 2px 4px rgba(0,0,0,.3)); }
3011
+ .ohm-symimg { width: 56px; height: 56px; object-fit: contain; flex: none; }
3012
+ .ohm-pay { font-size: 13px; line-height: 1.6; }
3013
+ .ohm-pay div { display: flex; gap: 6px; }
3014
+ .ohm-pay b { min-width: 42px; }
3015
+ .ohm-pay span { color: var(--accent); font-weight: 700; }
3016
+ .ohm-body p { color: var(--text-dim); line-height: 1.6; }
3017
+ .ohm-body p b { color: var(--text); }
3018
+ .ohm-feature { display: block; width: 100%; height: auto; margin: 10px 0; }
3019
+ .ohm-stats { margin: 12px 0; display: grid; grid-template-columns: 1fr 1fr; gap: 0 28px; }
3020
+ .ohm-stats > div { display: flex; justify-content: space-between; padding: 9px 0; border-bottom: 1px solid color-mix(in srgb, var(--text-dim) 20%, transparent); }
3021
+ .ohm-stats dt { color: var(--text-dim); margin: 0; } .ohm-stats dd { margin: 0; font-weight: 700; }
3022
+ .ohm-audit { margin: 12px 0 18px; padding: 14px 16px; border-radius: 12px; border: 2px solid #d03131; background: color-mix(in srgb, #d03131 8%, transparent); }
3023
+ .ohm-audit-title { font-weight: 900; letter-spacing: .5px; color: #b31d1d; }
3024
+ .ohm-audit-sub { margin-top: 8px; font-size: 13px; font-weight: 800; color: #b31d1d; }
3025
+ .ohm-audit-sub--rec { color: #b07d09; }
3026
+ .ohm-audit ul { margin: 6px 0 0; padding-left: 20px; color: var(--text); font-size: 13.5px; line-height: 1.55; }
3027
+ .ohm-audit li { margin: 3px 0; }
3028
+ .ohm-audit-rec ul { color: var(--text-dim); }
3029
+ .ohm-callout { margin: 16px 0 4px; padding: 14px 16px; border-radius: 12px; border-left: 4px solid var(--accent); background: color-mix(in srgb, var(--accent) 9%, transparent); }
3030
+ .ohm-callout b { color: var(--accent); } .ohm-callout p { margin: 4px 0 0; color: var(--text); }
3031
+ .ohm-callout--warning { border-left-color: #e0a106; background: color-mix(in srgb, #e0a106 10%, transparent); }
3032
+ .ohm-callout--warning b { color: #b07d09; }
3033
+ .ohm-subh { margin: 22px 0 8px; font-size: 15px; font-weight: 800; letter-spacing: .5px; color: var(--text); }
3034
+ .ohm-legal { font-size: 12px; line-height: 1.6; color: var(--text-dim); opacity: .85; }
3035
+ .ohm-hr { border: 0; border-top: 1px solid color-mix(in srgb, var(--text-dim) 30%, transparent); margin: 18px 0; }
3036
+ .ohm-media { display: flex; align-items: center; gap: 18px; margin: 14px 0; }
3037
+ .ohm-media--right { flex-direction: row-reverse; }
3038
+ .ohm-media > img { width: 40%; max-width: 320px; height: auto; flex: none; }
3039
+ .ohm-media-body { flex: 1; }
3040
+ .ohm-media-body h4 { margin: 0 0 6px; font-size: 16px; color: var(--text); }
3041
+ .ohm-media-body p { margin: 0; }
3042
+ .ohm-cards { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin: 12px 0; }
3043
+ .ohm-fcard { padding: 8px 6px; text-align: center; }
3044
+ .ohm-fcard img { display: block; width: 48px; height: 48px; margin: 0 auto 8px; }
3045
+ .ohm-fcard h5 { margin: 0 0 4px; font-size: 14px; color: var(--text); }
3046
+ .ohm-fcard p { margin: 0; font-size: 12px; line-height: 1.5; }
3047
+ .ohm-table { width: 100%; border-collapse: collapse; margin: 12px 0; font-size: 14px; }
3048
+ .ohm-table th { text-align: left; padding: 8px 10px; font-weight: 800; color: var(--text); border-bottom: 2px solid color-mix(in srgb, var(--text-dim) 35%, transparent); }
3049
+ .ohm-table td { padding: 8px 10px; border-bottom: 1px solid color-mix(in srgb, var(--text-dim) 18%, transparent); }
3050
+ .ohm-table td:first-child { color: var(--text); font-weight: 700; }
3051
+ .ohm-table td:not(:first-child) { color: var(--accent); font-weight: 700; }
3052
+ .ohm-steps { margin: 12px 0; padding-left: 22px; color: var(--text-dim); line-height: 1.7; }
3053
+ .ohm-steps li { margin: 5px 0; }
3054
+ .ohm-steps b { color: var(--text); }
3055
+ .ohm-group { margin: 8px 0; }
3056
+ `;
3057
+
2863
3058
  // src/spec/defaultHudSpec.ts
2864
3059
  var defaultHudSpec = Object.freeze({
2865
3060
  meta: { id: "open-ui-reference-hud", version: 1 },
@@ -2964,6 +3159,6 @@ function clampBet(amount, cfg, divisor = API_AMOUNT_DIVISOR) {
2964
3159
  return Math.min(max, Math.max(min, v));
2965
3160
  }
2966
3161
 
2967
- export { API_AMOUNT_DIVISOR, AutoplayControl, BLOCK_KINDS, ButtonControl, CURRENCY_TABLE, CardControl, Control, ControlRegistry, DEFAULT_MENU_TITLES, DEFAULT_NOTICE_ACTION, DictionaryTranslator, EventBus, EventLog, Fade, LOCALE_LABELS, OpenUI, PanelControl, Parallel, Pulse, RGS_ERROR_KEYS, ReadoutControl, SelectControl, Sequence, Signal, SliderControl, SpinControl, Squish, StepperControl, ToggleControl, TurboControl, Turn, ValueDisplay, applyJurisdiction, applyRulesDoc, auditRules, auditRulesDoc, breakpointFor, buildBetLadder, buildBlocks, buildPanel, buttonBlocks, checkSocialPhrases, clampBet, clampDecimals, clampDigits, clampMinDigits, collectSocialCopy, composeMenu, computeScreen, createUI, defaultHudSpec, defaultLayout, defaultLayoutConfig, defaultTheme, defineUI, dictionary, displayDigits, effect, errorBlocks, extendTheme, factsVars, findForbiddenPhrases, formatAmount, formatAmountPrecise, formatRtp, formatTimes, installResponsive, integerDigits, isRulesDoc, isSafeColor, isSocialCurrency, landscapeDefaultLayouts, mergeFacts, modeStatsItems, neededDecimals, openuiDefaults, openuiSocialDefaults, portraitDefaultLayouts, resolveBetLadder, resolveCurrency, resolvePlacement, resolveTheme, resolveTurboModes, safeAmount, sanitizeThemeOverrides, themePresets, validateSpec, valueFitMaxWidth };
3162
+ export { API_AMOUNT_DIVISOR, AutoplayControl, BLOCK_KINDS, ButtonControl, CURRENCY_TABLE, CardControl, Control, ControlRegistry, DEFAULT_MENU_TITLES, DEFAULT_NOTICE_ACTION, DictionaryTranslator, EventBus, EventLog, Fade, INFO_MENU_CSS, INFO_MENU_VARS, LOCALE_LABELS, OpenUI, PanelControl, Parallel, Pulse, RGS_ERROR_KEYS, ReadoutControl, SelectControl, Sequence, Signal, SliderControl, SpinControl, Squish, StepperControl, ToggleControl, TurboControl, Turn, ValueDisplay, applyJurisdiction, applyRulesDoc, auditRules, auditRulesDoc, breakpointFor, buildBetLadder, buildBlocks, buildPanel, buttonBlocks, checkSocialPhrases, clampBet, clampDecimals, clampDigits, clampMinDigits, collectSocialCopy, composeMenu, computeScreen, createUI, defaultHudSpec, defaultLayout, defaultLayoutConfig, defaultTheme, defineUI, dictionary, displayDigits, effect, errorBlocks, escapeHtml, extendTheme, factsVars, findForbiddenPhrases, formatAmount, formatAmountPrecise, formatRtp, formatTimes, installResponsive, integerDigits, isRulesDoc, isSafeColor, isSocialCurrency, landscapeDefaultLayouts, mergeFacts, modeStatsItems, neededDecimals, openuiDefaults, openuiSocialDefaults, portraitDefaultLayouts, renderBlocksHtml, renderRulesAuditHtml, resolveBetLadder, resolveCurrency, resolvePlacement, resolveTheme, resolveTurboModes, richHtml, safeAmount, sanitizeThemeOverrides, themePresets, validateSpec, valueFitMaxWidth };
2968
3163
  //# sourceMappingURL=index.js.map
2969
3164
  //# sourceMappingURL=index.js.map