/** * Challenge floor: reads window.CHALLENGE_FLOOR_BOOT { currencyRates, programData, challengeFeeDiscount? }, * drives UI via appState (programType, instantMode, aifoliteMode, currency, feeDiscount). * List price = round(fee × rate); sale price = round(fee × rate × feeDiscount) to 2 decimal places (cents); integers display without “.00”. */ (function ($) { var boot = window.CHALLENGE_FLOOR_BOOT; if (!boot || !boot.programData) { console.warn("CHALLENGE_FLOOR_BOOT missing; load challenge-floor-program-data.js first."); return; } var programData = boot.programData; var currencyRates = boot.currencyRates; var defaultFeeDiscount = boot && boot.challengeFeeDiscount != null && !isNaN(Number(boot.challengeFeeDiscount)) ? Number(boot.challengeFeeDiscount) : 0.5; var appState = { programType: "2step", instantMode: "NoCommission", aifoliteMode: "lite", accountSize: "10k", expandedParam: null, currency: "USD", feeDiscount: defaultFeeDiscount, }; if (typeof window.CHALLENGE_FLOOR_APP_STATE === "object" && window.CHALLENGE_FLOOR_APP_STATE) { $.extend(appState, window.CHALLENGE_FLOOR_APP_STATE); } if (appState.feeDiscount == null || (typeof appState.feeDiscount === "number" && isNaN(appState.feeDiscount))) { appState.feeDiscount = defaultFeeDiscount; } else { appState.feeDiscount = normalizeFeeDiscount(appState.feeDiscount); } /** Theme URL for ladder SVGs etc.; set via CHALLENGE_FLOOR_BOOT.assetBase or global assets_url (header.php). */ var CHALLENGE_FLOOR_ASSET_BASE = (function () { if (boot && boot.assetBase) { return String(boot.assetBase).replace(/\/?$/, "/"); } if (typeof assets_url !== "undefined" && assets_url) { return String(assets_url).replace(/\/?$/, "/") + "assets/images/challenge/"; } return "../assets/images/challenge/"; })(); var parameterDescriptions = { "Challenge Fee": "One-time fee to participate in the challenge program", "Daily Drawdown": "Maximum percentage loss allowed in a single trading day based on the previous day’s closing balance or equity.", "Max Drawdown": "The Maximum Loss Limit is the maximum allowable loss calculated based on the highest equity value your account reaches during the evaluation or trading phase.", "Profit Target": "The percentage gain required to successfully complete each phase of the challenge.", "Enhanced Payout Target": "Higher target threshold used to unlock enhanced payout benefits in this program.", "Enhanced Payout": "≥ 6% profit target for applying for 95% profit split and advance settlement.", "Challenge Window": "After account activation, users are granted a 24-hour trading window. The countdown begins upon successful challenge activation.", "Max Floating Loss": "The total floating loss of all open positions must not exceed 1% of the initial account balance.", "Consistency Score": "A profit-distribution metric that measures how evenly your profits are generated over time.", "withdrawable profit": "The Profit Target refers to the required percentage of withdrawable profit that the account must achieve before a payout can be requested.", "Profit split": "Percentage of profits you keep. The firm keeps the remainder.", "Payout period": "Minimum time required before you can request a withdrawal of profits.", "Trading days": "Minimum trading days required to pass the phase.", "News Trading": "", }; var parameterDescriptionsLite = { "Challenge Fee": "One-time fee to participate in the challenge program.", "Profit Target": "The percentage gain required to successfully complete each phase of the challenge.", "Enhanced Payout": "≥ 6% profit target for applying for 95% profit split and advance settlement.", "Challenge Window": "After account activation, users are granted a 24-hour trading window. The countdown begins upon successful challenge activation.", "Max Floating Loss": "The total floating loss of all open positions must not exceed 1% of the initial account balance.", "Max Drawdown": "The maximum loss limit follows a static drawdown model. If account equity falls below 98% of the initial balance, the account will be immediately terminated.", "Profit split": "Percentage of profits you keep. The firm keeps the remainder.", }; var parameterNames = [ "Challenge Fee", "Daily Drawdown", "Max Drawdown", "Profit Target", "Profit split", "Trading days", "Payout period", ]; var parameterNames_instant = [ "Challenge Fee", "Daily Drawdown", "Max Drawdown", "Consistency Score", "Profit split", "Payout period", ]; var parameterNames_lite = [ "Challenge Fee", "Profit Target", "Enhanced Payout", "Challenge Window", "Max Floating Loss", "Max Drawdown", "Profit split", ]; var PARAM_TO_FIELD = { "Challenge Fee": "fee", "Daily Drawdown": "daily", "Max Drawdown": "max", "Profit Target": "target", "withdrawable profit": "target", "Consistency Score": "consistency", "Enhanced Payout Target": "enhanced_target", "Enhanced Payout": "enhanced_target", "Challenge Window": "window", "Max Floating Loss": "floating", "Profit split": "split", "Profit Split": "split", "Trading days": "tradingDays", "Payout period": "payout", }; var ICON_BY_PARAM = { "Challenge Fee": "Component 26.svg", "Daily Drawdown": "Component 27.svg", "Max Drawdown": "Component 28.svg", "Profit Target": "Component 29.svg", "Enhanced Payout Target": "Component 29.svg", "Enhanced Payout": "Component 29.svg", "Challenge Window": "Component 29.svg", "Max Floating Loss": "Component 28.svg", "Consistency Score": "challenge_icon_consistency_score.png", "withdrawable profit": "Component 29.svg", "Profit split": "Component 30.svg", "Profit Split": "Component 30.svg", "Trading days": "Component 32.svg", "Payout period": "Component 31.svg", "News Trading": "Component 32.svg", }; function isAifoliteEliteMode() { return appState.programType === "aifolite" && appState.aifoliteMode === "aifo"; } function getParameterDescription(paramName) { if (appState.programType === "aifolite" && !isAifoliteEliteMode() && parameterDescriptionsLite[paramName]) { return parameterDescriptionsLite[paramName]; } if ( (appState.programType === "instant" || isAifoliteEliteMode()) && paramName === "Consistency Score" ) { return ( "A profit-distribution metric that measures how evenly your profits are generated over time, requiring that no single day contributes 15% or more of total profit and that the two most profitable days combined contribute less than 25% of total profit." ); } return parameterDescriptions[paramName] || ""; } function normalizeInstantMode(mode) { if (!programData.instant) return "NoCommission"; if (mode && programData.instant[mode]) return mode; if (programData.instant.NoCommission) return "NoCommission"; if (programData.instant.pro) return "pro"; if (programData.instant.aifo) return "aifo"; var keys = Object.keys(programData.instant); return keys.length ? keys[0] : "NoCommission"; } function normalizeAifoliteMode(mode) { if (mode === "aifo" && programData.instant && programData.instant.aifo) return "aifo"; return "lite"; } function getCurrentProgramConfig() { var cur = appState.currency; var p = appState.programType; if (p === "instant") { var mode = normalizeInstantMode(appState.instantMode); appState.instantMode = mode; if (!programData.instant[mode] || !programData.instant[mode][cur]) { return null; } return programData.instant[mode][cur]; } if (p === "aifolite") { var aMode = normalizeAifoliteMode(appState.aifoliteMode); appState.aifoliteMode = aMode; if (aMode === "aifo") { if (!programData.instant || !programData.instant.aifo || !programData.instant.aifo[cur]) { return null; } return programData.instant.aifo[cur]; } if (!programData.aifolite || !programData.aifolite[cur]) return null; return programData.aifolite[cur]; } if (!programData[p] || !programData[p][cur]) return null; return programData[p][cur]; } function ensureValidCurrency() { var cfg = getCurrentProgramConfig(); if (cfg) return cfg; var codes = Object.keys(currencyRates); for (var i = 0; i < codes.length; i++) { appState.currency = codes[i]; if (getCurrentProgramConfig()) return getCurrentProgramConfig(); } return null; } function escapeHtml(s) { return String(s) .replace(/&/g, "&") .replace(//g, ">") .replace(/"/g, """); } function formatFee(currency, feeDigits) { var meta = currencyRates[currency] || currencyRates.USD; var sym = meta.symbol; var rate = meta.rate != null ? meta.rate : 1; var n = parseFloat(String(feeDigits).replace(/,/g, "")); if (isNaN(n)) return sym + feeDigits; var v = n * rate; return sym + v.toLocaleString("en-US", { maximumFractionDigits: 0 }); } function parseFeeNumber(feeDigits) { var n = parseFloat(String(feeDigits).replace(/,/g, "")); return isNaN(n) ? NaN : n; } var FEE_SALE_DECIMAL_PLACES = 2; var FEE_AMOUNT_EPS = 1e-9; function roundToCurrencyDecimals(value, decimalPlaces) { if (value == null || isNaN(value)) return NaN; var d = decimalPlaces == null ? FEE_SALE_DECIMAL_PLACES : Math.max(0, Math.floor(Number(decimalPlaces))); if (d === 0) return Math.round(Number(value)); var m = Math.pow(10, d); return Math.round(Number(value) * m) / m; } function isEffectivelyIntegerAmount(amount) { if (amount == null || isNaN(amount)) return false; var r = Math.round(amount); return Math.abs(amount - r) < FEE_AMOUNT_EPS * Math.max(1, Math.abs(amount)); } function stripTrailingZerosFromLocalizedNumber(str) { if (str.indexOf(".") < 0) return str; return str.replace(/(\.\d*?)0+$/, "$1").replace(/\.$/, ""); } function formatFeeCurrencyAmount(currency, amount) { var meta = currencyRates[currency] || currencyRates.USD; var sym = meta.symbol; if (amount == null || isNaN(amount)) return sym + "—"; var neg = amount < 0; var x = neg ? -amount : amount; var core; if (isEffectivelyIntegerAmount(x)) { core = Math.round(x).toLocaleString("en-US", { maximumFractionDigits: 0 }); } else { core = stripTrailingZerosFromLocalizedNumber( x.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: 14 }) ); } return (neg ? "-" : "") + sym + core; } function normalizeFeeDiscount(mult) { var d = mult == null || isNaN(Number(mult)) ? 1 : Number(mult); if (d <= 0) return 0.01; if (d > 1) return 1; return d; } function feeDisplayAmounts(currency, feeDigits, discountMult) { var meta = currencyRates[currency] || currencyRates.USD; var rate = meta.rate != null ? meta.rate : 1; var d = normalizeFeeDiscount(discountMult); var n = parseFeeNumber(feeDigits); if (isNaN(n)) return { listNum: NaN, saleNum: NaN }; var listNum = Math.round(n * rate); var rawSale = n * rate * d; var saleNum = roundToCurrencyDecimals(rawSale, FEE_SALE_DECIMAL_PLACES); return { listNum: listNum, saleNum: saleNum }; } function formatFeeRoundedAmount(currency, roundedInt) { var meta = currencyRates[currency] || currencyRates.USD; var sym = meta.symbol; if (roundedInt == null || isNaN(roundedInt)) return sym + "—"; return sym + roundedInt.toLocaleString("en-US", { maximumFractionDigits: 0 }); } function formatAccountSize(sizeKey) { var n = parseInt(String(sizeKey).replace(/k$/i, ""), 10); if (isNaN(n)) return sizeKey; return "$" + n.toLocaleString("en-US") + ",000"; } /** Mobile size pills: "10k" → "10K" (matches design, not currency-prefixed). */ function formatMobileSizeShort(sizeKey) { var m = String(sizeKey).match(/^(\d+)k$/i); if (m) return m[1] + "K"; return String(sizeKey).toUpperCase(); } function normalizeRow(raw) { var o = $.extend({}, raw); o.tradingDays = o.miniday != null ? o.miniday : o.tradingDays != null ? o.tradingDays : "—"; return o; } function getActiveParameterNames() { if (appState.programType === "aifolite") { return isAifoliteEliteMode() ? parameterNames_instant.slice() : parameterNames_lite.slice(); } if (appState.programType === "instant") return parameterNames_instant.slice(); return parameterNames.slice(); } function profitTargetRowClass() { if (appState.programType === "3step") return " phase-target-row phase-target-row--3"; if (appState.programType === "2step") return " phase-target-row phase-target-row--2"; if (appState.programType === "1step") return " phase-target-row phase-target-row--1"; return ""; } function getLadderDefinitions() { return getActiveParameterNames() .filter(function (name) { return name !== "Challenge Fee"; }) .map(function (paramName, idx) { return { paramName: paramName, label: paramName, icon: ICON_BY_PARAM[paramName] || "Component 29.svg", tooltip: getParameterDescription(paramName), rowClass: paramName === "Profit Target" ? profitTargetRowClass() : "", ladderIndex: idx, ladderKey: String(paramName) .replace(/\s+/g, "-") .replace(/[^a-zA-Z0-9-]/g, "") .toLowerCase(), }; }); } function getCellRaw(row, paramName) { var key = PARAM_TO_FIELD[paramName]; if (!key) return ""; if (key === "tradingDays") return row.tradingDays != null ? String(row.tradingDays) : "—"; var v = row[key]; return v == null ? "" : String(v); } function cellAllowsHtml(paramName) { return ( paramName === "Profit Target" || paramName === "Consistency Score" || paramName === "withdrawable profit" ); } function metricHelpMarkup(label, tooltip, opts) { opts = opts || {}; var mobileCard = !!opts.mobileCard; var btnClass = "metric-help-btn" + (mobileCard ? " metric-help-btn--sr-only" : ""); var btnTab = mobileCard ? "-1" : "0"; var btnAriaHidden = mobileCard ? ' aria-hidden="true"' : ""; return ( '' + '' + '' + "" + '' + '' + escapeHtml(tooltip) + "" ); } var LADDER_HEAD = { label: "Account Size", icon: "Component 33.svg" }; var LADDER_FOOT = { line1: "One-time", line2: "Challenge Fee", icon: "Component 34.svg", }; function renderMetricsFootItem() { return ( '
' + '' + '' + '' + '' + escapeHtml(LADDER_FOOT.line1) + "" + "" + '' + escapeHtml(LADDER_FOOT.line2) + "" + "" + "
" ); } function renderMetricsAnchorItem(label, iconFile, anchorClass, sectionClass) { return ( '
' + '' + '' + escapeHtml(label) + "" + "
" ); } function renderMetricsColumn(rows) { var body = rows .map(function (m) { var rowCls = m.rowClass || ""; var dataAttrs = ' data-ladder-row="' + m.ladderIndex + '" data-ladder-key="' + escapeHtml(m.ladderKey) + '"'; return ( '
" + '' + '' + escapeHtml(m.label) + "" + metricHelpMarkup(m.label, m.tooltip) + "
" ); }) .join(""); return ( renderMetricsAnchorItem(LADDER_HEAD.label, LADDER_HEAD.icon, "metric-item--ladder-head", "head") + '
' + body + "
" + '
' + renderMetricsFootItem() + '' + "
" ); } function priceRowHtml(acc) { var highlight = acc.highlight ? " highlight" : ""; var rowModifier = acc.best ? " price-row--best" : ""; var bestIcon = acc.best ? '' : ""; var oldPart = acc.oldPrice ? '' + escapeHtml(acc.oldPrice) + "" : ""; return ( '
' + bestIcon + '' + escapeHtml(acc.priceFormatted) + "" + oldPart + "
" ); } function renderMobileMetricRows(acc, rows) { return rows .map(function (m) { var raw = getCellRaw(acc, m.paramName); var inner = cellAllowsHtml(m.paramName) ? raw : escapeHtml(raw); var rowCls = m.rowClass || ""; var dataAttrs = ' data-ladder-row="' + m.ladderIndex + '" data-ladder-key="' + escapeHtml(m.ladderKey) + '"'; return ( '
" + '' + '' + '' + escapeHtml(m.label) + "" + metricHelpMarkup(m.label, m.tooltip, { mobileCard: true }) + "" + '' + inner + "" + "
" ); }) .join(""); } function renderDesktopAccountCard(acc, rows) { var best = acc.best ? " best" : ""; var tag = acc.best ? '
BEST VALUE
' : ""; var discount = acc.discountTag ? '' : ""; var values = rows .map(function (m) { var raw = getCellRaw(acc, m.paramName); var inner = cellAllowsHtml(m.paramName) ? raw : escapeHtml(raw); var rowCls = m.rowClass || ""; var dataAttrs = ' data-ladder-row="' + m.ladderIndex + '" data-ladder-key="' + escapeHtml(m.ladderKey) + '"'; return '
" + inner + "
"; }) .join(""); var pid = acc.product_id != null ? acc.product_id : ""; var vid = acc.variant_id != null ? acc.variant_id : ""; return ( '
' + '
' + tag + '" + '
' + values + "
" + '
' + priceRowHtml(acc) + '
' + "
" ); } function renderMobileAccountCard(acc, rows, cardIndex) { var best = acc.best ? " best" : ""; var tag = acc.best ? '
BEST VALUE
' : ""; var discount = acc.discountTag ? '' : ""; var pid = acc.product_id != null ? acc.product_id : ""; var vid = acc.variant_id != null ? acc.variant_id : ""; var idxAttr = cardIndex != null && !isNaN(Number(cardIndex)) ? ' data-challenge-card-index="' + String(Number(cardIndex)) + '"' : ""; return ( '
" + '
' + tag + '" + '
' + '' + priceRowHtml(acc) + "
" + '
' + renderMobileMetricRows(acc, rows) + "
" + "
" ); } function buildAccountsFromConfig(cfg) { if (!cfg || !cfg.availableSizes) return []; var best = cfg.best_value_lable || []; var discount = cfg.discount_lable || []; return cfg.availableSizes .map(function (sizeKey) { var raw = cfg.data[sizeKey]; if (!raw) return null; var row = normalizeRow(raw); row.sizeKey = sizeKey; row.sizeLabel = formatAccountSize(sizeKey); row.best = best.indexOf(sizeKey) >= 0; row.discountTag = discount.indexOf(sizeKey) >= 0; var amounts = feeDisplayAmounts(appState.currency, row.fee, appState.feeDiscount); if (isNaN(amounts.saleNum)) { row.priceFormatted = formatFee(appState.currency, row.fee); row.oldPrice = ""; } else { row.priceFormatted = formatFeeCurrencyAmount(appState.currency, amounts.saleNum); var listVal = amounts.listNum; var saleVal = amounts.saleNum; var showOld = !isNaN(listVal) && !isNaN(saleVal) && Math.abs(listVal - saleVal) > FEE_AMOUNT_EPS * Math.max(1, Math.abs(listVal)); row.oldPrice = showOld ? formatFeeRoundedAmount(appState.currency, listVal) : ""; } row.highlight = row.best; row.product_id = raw.product_id; row.variant_id = raw.variant_id; return row; }) .filter(Boolean); } function buildAifolite24hNoteHtml() { return ( '
' + '
' + '
' + '

Note:

' + "

" + "The challenge begins immediately after purchase. To ensure you have the maximum time to complete the challenge. Please take consider market closing time and holidays." + "

" + "

" + "The instrument used in the first executed trade will be considered the only permitted trading instrument for the account. Any subsequent trading of other instruments will be deemed a violation." + "

" + "

" + "Please refer to the FAQs for more information." + "

" + "
" ); } function buildFloorHtml() { var cfg = ensureValidCurrency(); if (!cfg) { return '
No program data for this selection.
'; } var rows = getLadderDefinitions(); var accounts = buildAccountsFromConfig(cfg); if (!accounts.length) { return '
No tiers available.
'; } var metricsCol = renderMetricsColumn(rows); var desktopCards = accounts.map(function (a) { return renderDesktopAccountCard(a, rows); }).join(""); var mobileCards = accounts .map(function (a, i) { return renderMobileAccountCard(a, rows, i); }) .join(""); var mobileSizeTabs = '
' + accounts .map(function (a, i) { var activeCls = i === 0 ? " active" : ""; var dot = a.best ? '' : ""; return ( '" ); }) .join("") + "
"; var rowLayoutExtras = ""; if (accounts.length === 1) { rowLayoutExtras = " challenge-floor-row--single-card"; } else if (accounts.length === 5) { rowLayoutExtras = " challenge-floor-row--five-cards-pc"; } else if (accounts.length === 4) { rowLayoutExtras = " challenge-floor-row--four-cards-pc"; } else if ( (appState.programType === "instant" && appState.instantMode === "aifo") || isAifoliteEliteMode() ) { rowLayoutExtras = " challenge-floor-row--instant-aifo-two-pc"; } else if (appState.programType === "aifolite") { rowLayoutExtras = " challenge-floor-row--aifolite-24h-pc"; } var mainRow = '
' + '
' + metricsCol + "
" + '
' + '
' + '
' + desktopCards + "
" + mobileSizeTabs + '
' + '
' + mobileCards + "
" + "
"; var note = appState.programType === "aifolite" && !isAifoliteEliteMode() ? buildAifolite24hNoteHtml() : ""; return mainRow + note; } function bindChallengeCardsScroll($ctx) { $ctx.find(".js-challenge-cards-scroll").each(function () { var $el = $(this); $el.off("scroll.challengeCards").on("scroll.challengeCards", function () { $el.addClass("is-scrolling"); window.clearTimeout($el.data("challengeScrollTimer")); $el.data( "challengeScrollTimer", window.setTimeout(function () { $el.removeClass("is-scrolling"); }, 900) ); }); }); } /** 992px~1200px:与 CSS 卡片固定宽区间一致 */ function isMidDesktopCardsViewport() { return window.matchMedia("(min-width: 992px) and (max-width: 1199.98px)").matches; } var challengeCardsDragDocBound = false; var challengeCardsDragActive = null; function unbindChallengeCardsDragScroll($ctx) { $ctx.find(".js-challenge-cards-scroll").each(function () { $(this) .off(".challengeCardsDrag") .removeClass("challenge-cards-scroll--drag-enabled is-drag-scrolling") .css("cursor", ""); }); challengeCardsDragActive = null; if (challengeCardsDragDocBound) { $(document).off(".challengeCardsDrag"); challengeCardsDragDocBound = false; } } function bindChallengeCardsDragScroll($ctx) { unbindChallengeCardsDragScroll($ctx); if (!isMidDesktopCardsViewport()) return; function onDocMouseMove(e) { if (!challengeCardsDragActive) return; var drag = challengeCardsDragActive; var dx = e.pageX - drag.startX; if (Math.abs(dx) > 4) drag.moved = true; drag.el.scrollLeft = drag.startScroll - dx; } function onDocMouseUp() { if (!challengeCardsDragActive) return; var drag = challengeCardsDragActive; drag.$el.removeClass("is-drag-scrolling"); if (drag.moved) { drag.$el.data("challengeCardsDragMoved", true); window.setTimeout(function () { drag.$el.removeData("challengeCardsDragMoved"); }, 0); } challengeCardsDragActive = null; } if (!challengeCardsDragDocBound) { $(document).on("mousemove.challengeCardsDrag", onDocMouseMove); $(document).on("mouseup.challengeCardsDrag", onDocMouseUp); challengeCardsDragDocBound = true; } $ctx.find(".js-challenge-cards-scroll").each(function () { var el = this; var $el = $(el); $el.addClass("challenge-cards-scroll--drag-enabled"); $el.on("mousedown.challengeCardsDrag", function (e) { if (e.button !== 0) return; if ( $(e.target).closest( "button, a, input, select, textarea, label, .metric-help-btn, .metric-label-dotted" ).length ) { return; } challengeCardsDragActive = { el: el, $el: $el, startX: e.pageX, startScroll: el.scrollLeft, moved: false, }; $el.addClass("is-drag-scrolling"); e.preventDefault(); }); $el.on("click.challengeCardsDrag", "button, a", function (e) { if ($el.data("challengeCardsDragMoved")) { e.preventDefault(); e.stopImmediatePropagation(); } }); }); } function syncMobileSizeTabFromScroll($mount) { var $scroll = $mount.find(".js-challenge-cards-scroll.d-lg-none"); var $tabs = $mount.find(".js-challenge-mobile-size-tabs"); if (!$scroll.length || !$tabs.length) return; var $cards = $scroll.find(".challenge-cards-inner > [class*='col-']"); if (!$cards.length) return; var scrollEl = $scroll[0]; var scRect = scrollEl.getBoundingClientRect(); var mid = scRect.left + scRect.width / 2; var bestI = 0; var bestD = Infinity; $cards.each(function (idx) { var r = this.getBoundingClientRect(); var c = r.left + r.width / 2; var d = Math.abs(c - mid); if (d < bestD) { bestD = d; bestI = idx; } }); $tabs.find(".challenge-mobile-size-tab").each(function (idx) { var on = idx === bestI; $(this).toggleClass("active", on); $(this).attr("aria-selected", on ? "true" : "false"); }); } function bindMobileSizeTabSync($mount) { var $scroll = $mount.find(".js-challenge-cards-scroll.d-lg-none"); var $tabs = $mount.find(".js-challenge-mobile-size-tabs"); if (!$scroll.length || !$tabs.length) return; function sync() { syncMobileSizeTabFromScroll($mount); } $scroll.off("scroll.mobileSizeTabs").on("scroll.mobileSizeTabs", function () { window.requestAnimationFrame(sync); }); $tabs .off("click.mobileSizeTabs") .on("click.mobileSizeTabs", ".challenge-mobile-size-tab", function () { var idx = parseInt(String($(this).attr("data-size-index")), 10); if (isNaN(idx)) return; var card = $scroll.find(".challenge-cards-inner > [class*='col-']").get(idx); if (card) { card.scrollIntoView({ behavior: "smooth", inline: "center", block: "nearest" }); } }); $(window).off("resize.mobileSizeTabsChallenge").on("resize.mobileSizeTabsChallenge", sync); } /** 防止阶梯行被负 margin / 过大 padding 顶出左列(992~1200 + overflow:hidden 时会整列看似空白) */ function clampMetricsColLadderInView($metricsCol) { if (!$metricsCol.length || !$metricsCol[0]) return; var colTop = $metricsCol[0].getBoundingClientRect().top; var slack = 2; var $body = $metricsCol.find(".metrics-col__body"); if ($body.length) { var pt = parseFloat($body.css("padding-top")) || 0; if (pt > 240) { $body.css("padding-top", ""); } } $metricsCol.find(".metric-item").each(function () { var rect = this.getBoundingClientRect(); if (rect.height < 1) return; if (rect.top < colTop - slack) { var $el = $(this); var mt = parseFloat($el.css("margin-top")) || 0; $el.css("margin-top", mt + Math.ceil(colTop - rect.top) + "px"); } }); } /** PC:按设计稿对齐——Account Size↔金额、参数行↔value、Fee↔price-row、左右列底↔卡片底;Profit Target 行高逻辑不变。 */ function syncChallengeDesktopLadderHeights() { if (isCompactChallengeFloorViewport()) return; var $mount = $("#challenge-floor-mount"); var $row = $mount.children(".row.no-gutters").first(); if (!$row.length) return; var $metricsCol = $row.find(".metrics-col"); var $head = $metricsCol.find(".metric-item--ladder-head"); var $foot = $metricsCol.find(".metric-item--ladder-foot"); var $footTail = $metricsCol.find(".challenge-ladder__foot-tail"); var $metrics = $metricsCol.find(".metrics-col__body .metric-item"); var $scroll = $row.find(".js-challenge-cards-scroll.d-none.d-lg-block"); if (!$metricsCol.length || !$scroll.length) return; if (!$scroll.is(":visible")) return; var $cards = $scroll.find("article.account-card"); var $card = $cards.first(); if (!$card.length) return; var $accountHead = $card.find("> .account-head"); var $size = $accountHead.find(".size").first(); var $priceRow = $card.find("> .price-area > .price-row").first(); var $valueCols = $scroll.find("article.account-card > .values"); if (!$valueCols.length) return; $metricsCol.css({ marginTop: "", paddingBottom: "" }); $footTail.css({ height: "", minHeight: "" }); $metricsCol.find(".metrics-col__body").css("padding-top", ""); $metrics.css("margin-top", ""); $head.add($foot).css({ minHeight: "", height: "", marginTop: "", paddingTop: "", paddingBottom: "", }); if ($head.length && $accountHead.length && $size.length) { var headBoxH = Math.ceil($accountHead.outerHeight()); if (headBoxH < 1) headBoxH = 1; var sizeH = Math.ceil($size.outerHeight()); if (sizeH < 1) sizeH = 1; $head.css({ boxSizing: "border-box", display: "flex", alignItems: "center", minHeight: headBoxH, height: headBoxH, paddingTop: "0", paddingBottom: "0", marginTop: "0", }); if ($head[0]) void $head[0].offsetHeight; var headTopDelta = Math.round( $accountHead[0].getBoundingClientRect().top - $head[0].getBoundingClientRect().top ); if (headTopDelta !== 0) { $head.css("margin-top", headTopDelta + "px"); } if ($head[0]) void $head[0].offsetHeight; var ladderHeadTop = $head[0].getBoundingClientRect().top; var sizeTop = $size[0].getBoundingClientRect().top; var padTop = Math.max(0, Math.round(sizeTop - ladderHeadTop)); var padBottom = Math.max(0, headBoxH - padTop - sizeH); $head.css({ paddingTop: padTop + "px", paddingBottom: padBottom + "px", }); function nudgeHeadPaddingToSize() { if (!$head[0] || !$size[0]) return; var sizeRect = $size[0].getBoundingClientRect(); var sizeMidY = (sizeRect.top + sizeRect.bottom) / 2; var $label = $head.find(".metric-label").first(); var alignEl = $label.length ? $label[0] : $head[0]; var alignRect = alignEl.getBoundingClientRect(); var alignMidY = (alignRect.top + alignRect.bottom) / 2; var centerDelta = Math.round(sizeMidY - alignMidY); if (centerDelta === 0) return; var headPadT = parseFloat($head.css("padding-top")) || 0; var headPadB = parseFloat($head.css("padding-bottom")) || 0; if (centerDelta > 0 && headPadB >= centerDelta) { $head.css({ paddingTop: headPadT + centerDelta + "px", paddingBottom: headPadB - centerDelta + "px", }); } else if (centerDelta < 0 && headPadT >= -centerDelta) { $head.css({ paddingTop: headPadT + centerDelta + "px", paddingBottom: headPadB - centerDelta + "px", }); } } if ($head[0]) void $head[0].offsetHeight; nudgeHeadPaddingToSize(); if ($head[0]) void $head[0].offsetHeight; var headBottomFix = Math.round( $accountHead[0].getBoundingClientRect().bottom - $head[0].getBoundingClientRect().bottom ); if (headBottomFix !== 0) { var headPadB = parseFloat($head.css("padding-bottom")) || 0; $head.css("padding-bottom", Math.max(0, headPadB + headBottomFix) + "px"); if ($head[0]) void $head[0].offsetHeight; nudgeHeadPaddingToSize(); } } var $metricsBody = $metricsCol.find(".metrics-col__body"); var $firstValCell = $valueCols.first().children(".value-item").first(); if ($metricsBody.length && $firstValCell.length && $head.length && $accountHead.length) { var cardBodyGap = Math.round( $firstValCell[0].getBoundingClientRect().top - $accountHead[0].getBoundingClientRect().bottom ); if (cardBodyGap < 0) cardBodyGap = 0; $metricsBody.css("padding-top", cardBodyGap + "px"); if ($metrics.length && $head[0]) { if ($metrics[0]) void $metrics[0].offsetHeight; var leftBodyGap = Math.round( $metrics.first()[0].getBoundingClientRect().top - $head[0].getBoundingClientRect().bottom ); if (leftBodyGap !== cardBodyGap) { $metricsBody.css("padding-top", cardBodyGap + (cardBodyGap - leftBodyGap) + "px"); } } } var nValues = $valueCols.first().children(".value-item").length; var rowCount = Math.min($metrics.length, nValues); if (rowCount >= 1) { $metrics.css({ minHeight: "", height: "" }); $valueCols.children(".value-item").css({ minHeight: "", height: "" }); var r; for (r = 0; r < rowCount; r++) { var $mRow = $metrics.eq(r); var isPhaseTarget = $mRow.hasClass("phase-target-row"); var maxH = Math.ceil($mRow.outerHeight()); $valueCols.each(function () { var $cell = $(this).children(".value-item").eq(r); if ($cell.length) { var h = Math.ceil($cell.outerHeight()); if (h > maxH) maxH = h; } }); if (maxH < 1) maxH = 1; if (isPhaseTarget) { $mRow.css({ minHeight: "", height: "" }); $valueCols.each(function () { $(this).children(".value-item").eq(r).css({ minHeight: "", height: "" }); }); if ($mRow[0]) void $mRow[0].offsetHeight; maxH = Math.ceil($mRow.outerHeight()); $valueCols.each(function () { var $cell = $(this).children(".value-item").eq(r); if ($cell.length) { var phaseH = Math.ceil($cell.outerHeight()); if (phaseH > maxH) maxH = phaseH; } }); if (maxH < 1) maxH = 1; $mRow.css({ minHeight: maxH, height: "" }); $valueCols.each(function () { $(this).children(".value-item").eq(r).css({ minHeight: maxH, height: "" }); }); } else { $mRow.css({ minHeight: maxH, height: maxH }); $valueCols.each(function () { $(this).children(".value-item").eq(r).css({ minHeight: maxH, height: maxH }); }); } } /* 逐行:左侧 label 顶边与首张卡 value 顶边严格对齐 */ for (r = 0; r < rowCount; r++) { var $mRowAlign = $metrics.eq(r); var $vRef = $valueCols.first().children(".value-item").eq(r); if (!$mRowAlign.length || !$vRef.length) continue; if ($mRowAlign[0]) void $mRowAlign[0].offsetHeight; var rowDelta = Math.round( $vRef[0].getBoundingClientRect().top - $mRowAlign[0].getBoundingClientRect().top ); if (rowDelta !== 0) { var rowMt = parseFloat($mRowAlign.css("margin-top")) || 0; $mRowAlign.css("margin-top", rowMt + rowDelta + "px"); } } } if ($foot.length && $priceRow.length) { $foot.css({ minHeight: "", height: "auto", marginTop: "" }); var $oneTimeLine = $foot.find(".ladder-foot-line--primary").first(); var $priceEl = $priceRow.find(".price").first(); if (!$priceEl.length) { $priceEl = $priceRow; } if ($oneTimeLine.length && $priceEl.length) { if ($foot[0]) void $foot[0].offsetHeight; var oneTimeRect = $oneTimeLine[0].getBoundingClientRect(); var priceRect = $priceEl[0].getBoundingClientRect(); var oneTimeMidY = (oneTimeRect.top + oneTimeRect.bottom) / 2; var priceMidY = (priceRect.top + priceRect.bottom) / 2; var push = Math.round(priceMidY - oneTimeMidY); if (push !== 0) { $foot.css("margin-top", push + "px"); } } } /* 992~1200px:foot-tail 补齐至卡片底,消除左下角空白 */ if ($footTail.length) { if (isMidDesktopCardsViewport() && $foot.length) { var cardBottomMid = 0; $cards.each(function () { var b = this.getBoundingClientRect().bottom; if (b > cardBottomMid) cardBottomMid = b; }); if (cardBottomMid > 0) { if ($foot[0]) void $foot[0].offsetHeight; var footBottomMid = $foot[0].getBoundingClientRect().bottom; var tailH = Math.round(cardBottomMid - footBottomMid); if (tailH < 0) tailH = 0; $footTail.css({ display: "block", minHeight: tailH, height: tailH > 0 ? tailH : "", }); } } else { $footTail.css({ minHeight: "", height: "" }); } } clampMetricsColLadderInView($metricsCol); syncMetricsColGradientPanel($metricsCol, $cards); } /** PC:左侧渐变背景高度 ≈ 卡片高度,顶部略高出卡片顶(不撑满整列) */ function syncMetricsColGradientPanel($metricsCol, $cards) { if (!$metricsCol.length || !$cards.length) return; $metricsCol.css({ "--metrics-col-bg-h": "", "--metrics-col-bg-top": "" }); var tallestH = 0; var cardTop = Infinity; var cardBottom = 0; $cards.each(function () { var rect = this.getBoundingClientRect(); if (rect.height > tallestH) tallestH = Math.ceil(rect.height); if (rect.top < cardTop) cardTop = rect.top; if (rect.bottom > cardBottom) cardBottom = rect.bottom; }); if (tallestH < 1 || !isFinite(cardTop)) return; if ($metricsCol[0]) void $metricsCol[0].offsetHeight; var metricsTop = $metricsCol[0].getBoundingClientRect().top; var overhang = 4; var $floor = $metricsCol.closest(".challenge-floor"); if ($floor.length) { var v = getComputedStyle($floor[0]).getPropertyValue("--challenge-metrics-bg-overhang-top"); if (v) overhang = parseFloat(v.trim()) || 4; } var bgTop = Math.round(cardTop - metricsTop - overhang); /* 992~1200px:渐变底边与拉伸后的左列底(≈ 卡片底)对齐 */ if (isMidDesktopCardsViewport()) { var metricsH = Math.ceil($metricsCol[0].getBoundingClientRect().height); var bgH = metricsH - bgTop; if (cardBottom > 0) { var cardAlignedH = Math.round(cardBottom - metricsTop - bgTop); if (cardAlignedH > bgH) bgH = cardAlignedH; } if (bgH < tallestH + overhang) bgH = tallestH + overhang; $metricsCol.css({ "--metrics-col-bg-h": bgH + "px", "--metrics-col-bg-top": bgTop + "px", }); return; } $metricsCol.css({ "--metrics-col-bg-h": tallestH + overhang + "px", "--metrics-col-bg-top": bgTop + "px", }); } var challengeLadderResizeTimer = null; function scheduleChallengeDesktopLadderSync() { window.clearTimeout(challengeLadderResizeTimer); challengeLadderResizeTimer = window.setTimeout(function () { syncChallengeDesktopLadderHeights(); }, 120); } $(window).on("resize.challengeFloorLadder", scheduleChallengeDesktopLadderSync); function renderCurrencyBar() { var html = Object.keys(currencyRates) .map(function (code) { var active = code === appState.currency ? " active" : ""; return ( '" ); }) .join(""); $("#challengeCurrencyTabs").html(html); } function updateInstantSubtabsVisibility() { var show = appState.programType === "instant"; $("#challengeInstantSubtabs").toggleClass("d-none", !show); if (show) { $("#challengeInstantSubtabs .challenge-instant-subtab").removeClass("active"); $( '#challengeInstantSubtabs .challenge-instant-subtab[data-instant-mode="' + appState.instantMode + '"]' ).addClass("active"); } } function updateAifoliteSubtabsVisibility() { var show = appState.programType === "aifolite"; $("#challengeAifoliteSubtabs").toggleClass("d-none", !show); if (show) { appState.aifoliteMode = normalizeAifoliteMode(appState.aifoliteMode); $("#challengeAifoliteSubtabs .challenge-instant-subtab").removeClass("active"); $( '#challengeAifoliteSubtabs .challenge-instant-subtab[data-aifolite-mode="' + appState.aifoliteMode + '"]' ).addClass("active"); } } function updateAddonsVisibility() { var hide = appState.programType === "instant" || appState.programType === "aifolite"; $(".challenge-floor-addons").toggleClass("d-none", hide); } function renderChallengeFloor() { ensureValidCurrency(); closeAllMetricTooltips(); var $mount = $("#challenge-floor-mount"); $mount.html(buildFloorHtml()); bindChallengeCardsScroll($mount); bindChallengeCardsDragScroll($mount); bindMobileSizeTabSync($mount); window.requestAnimationFrame(function () { syncChallengeDesktopLadderHeights(); window.requestAnimationFrame(function () { syncChallengeDesktopLadderHeights(); syncMobileSizeTabFromScroll($mount); if (isMidDesktopCardsViewport()) { window.setTimeout(syncChallengeDesktopLadderHeights, 280); } }); }); } function tabIdToProgramType(tabId) { if (tabId === "24h") return "aifolite"; return tabId; } function programTypeToTabId(p) { if (p === "aifolite") return "24h"; return p; } function syncTabsFromAppState() { var tab = programTypeToTabId(appState.programType); $(".challenge-floor .tab-btn[data-tab]").removeClass("active"); $('.challenge-floor .tab-btn[data-tab="' + tab + '"]').addClass("active"); updateInstantSubtabsVisibility(); updateAifoliteSubtabsVisibility(); updateAddonsVisibility(); } $(document).on("click", ".challenge-floor .tab-btn[data-tab]", function () { var tab = $(this).data("tab"); appState.programType = tabIdToProgramType(tab); if (appState.programType === "instant") { appState.instantMode = normalizeInstantMode(appState.instantMode); } if (appState.programType === "aifolite") { appState.aifoliteMode = normalizeAifoliteMode(appState.aifoliteMode); } syncTabsFromAppState(); renderCurrencyBar(); renderChallengeFloor(); }); $(document).on("click", "#challengeInstantSubtabs .challenge-instant-subtab", function () { appState.instantMode = normalizeInstantMode($(this).data("instant-mode")); syncTabsFromAppState(); renderCurrencyBar(); renderChallengeFloor(); }); $(document).on("click", "#challengeAifoliteSubtabs .challenge-instant-subtab", function () { appState.aifoliteMode = normalizeAifoliteMode($(this).data("aifolite-mode")); syncTabsFromAppState(); renderCurrencyBar(); renderChallengeFloor(); }); $(document).on("click", ".challenge-currency-btn", function () { appState.currency = $(this).data("currency"); renderCurrencyBar(); renderChallengeFloor(); }); /** Viewport width at/above this uses CSS absolute tooltip (desktop ladder/cards). */ var METRIC_TOOLTIP_PC_MIN_PX = 992; var metricTooltipSuppressDocClose = false; /** 手机窄屏(<992):使用卡片内指标 + 横向滑动楼层;992+ 含竖屏平板走 PC 楼层 */ function isCompactChallengeFloorViewport() { return window.matchMedia("(max-width: 991.98px)").matches; } function isMobileMetricTooltipViewport() { return isCompactChallengeFloorViewport(); } function isMobileCardMetricHelp($wrap) { return $wrap && $wrap.length && $wrap.closest(".card-mobile-metrics").length > 0; } function getMetricTooltipForWrap($wrap) { var $portaled = $wrap.data("metricTooltipPortaled"); if ($portaled && $portaled.length && $.contains(document.documentElement, $portaled[0])) { return $portaled; } return $wrap.children(".metric-tooltip").first(); } function restoreMetricTooltipDom($wrap) { if (!$wrap || !$wrap.length) return; var $tip = getMetricTooltipForWrap($wrap); $wrap.removeClass("metric-help--tooltip-fixed"); $wrap.removeData("metricTooltipPortaled"); if (!$tip.length) return; $tip.removeClass("metric-tooltip--mobile-portal metric-tooltip--positioned metric-tooltip--flip-below"); $tip[0].style.removeProperty("--metric-tooltip-callout-x"); $tip.css({ position: "", left: "", top: "", right: "", bottom: "", width: "", maxWidth: "", transform: "", margin: "", }); if ($tip.parent()[0] === document.body) { $wrap.append($tip); } } function closeAllMetricTooltips() { $(".challenge-floor .metric-help.show").each(function () { var $w = $(this); $w.removeClass("show"); restoreMetricTooltipDom($w); }); $("body > .metric-tooltip.metric-tooltip--mobile-portal").remove(); } function portalMetricTooltipToBody($wrap) { var $tip = $wrap.children(".metric-tooltip").first(); if (!$tip.length) return $(); if ($tip.parent()[0] !== document.body) { $wrap.data("metricTooltipPortaled", $tip); document.body.appendChild($tip[0]); } $tip.addClass("metric-tooltip--mobile-portal"); $tip.removeClass("metric-tooltip--positioned"); return $tip; } function clearMobileMetricTooltipPosition($wrap) { restoreMetricTooltipDom($wrap); } function metricHelpToggleFromWrap($wrap) { var isMobileCard = isMobileCardMetricHelp($wrap); var isMobileViewport = isMobileMetricTooltipViewport(); var willOpen = !$wrap.hasClass("show"); $(".challenge-floor .metric-help") .not($wrap) .each(function () { var $other = $(this); $other.removeClass("show"); restoreMetricTooltipDom($other); }); if (willOpen) { if (isMobileCard && isMobileViewport) { metricTooltipSuppressDocClose = true; window.setTimeout(function () { metricTooltipSuppressDocClose = false; }, 0); $wrap.addClass("metric-help--tooltip-fixed show"); portalMetricTooltipToBody($wrap); positionMobileMetricTooltip($wrap); schedulePositionMobileMetricTooltip($wrap); } else { $wrap.addClass("show"); } } else { $wrap.removeClass("show"); if (isMobileCard) { restoreMetricTooltipDom($wrap); } } } function positionMobileMetricTooltip($wrap) { if (!$wrap || !$wrap.length || !$wrap.hasClass("show")) return; if (!isMobileCardMetricHelp($wrap)) return; if (!isMobileMetricTooltipViewport()) { restoreMetricTooltipDom($wrap); return; } var $tip = getMetricTooltipForWrap($wrap); if (!$tip.length) { $tip = portalMetricTooltipToBody($wrap); } if (!$tip.length) return; var $left = $wrap.closest(".card-mobile-metric-left"); var $anchor = $left.length ? $left.find(".metric-label-dotted").first() : $wrap.find(".metric-help-btn"); if (!$anchor.length) return; var tipEl = $tip[0]; var $card = $wrap.closest(".account-card"); var cardEl = $card.length ? $card[0] : null; $wrap.addClass("metric-help--tooltip-fixed"); tipEl.classList.remove("metric-tooltip--flip-below", "metric-tooltip--positioned"); var edgePad = 12; var vw = window.innerWidth; var vh = window.innerHeight; var margin = 12; var gapToAnchor = 8; var arrowH = 7; var cardRect = cardEl ? cardEl.getBoundingClientRect() : null; var maxW = cardRect ? Math.max(120, cardRect.width - edgePad * 2) : Math.min(320, vw - margin * 2); $tip.css({ position: "fixed", transform: "none", margin: 0, right: "auto", bottom: "auto", width: maxW + "px", maxWidth: maxW + "px", }); var anchor = $anchor[0].getBoundingClientRect(); var tipRect = tipEl.getBoundingClientRect(); var tipW = tipRect.width; var tipH = tipRect.height; var minLeft = cardRect ? cardRect.left + edgePad : margin; var maxLeft = cardRect ? cardRect.right - edgePad - tipW : vw - margin - tipW; var anchorCx = anchor.left + anchor.width / 2; var left = anchorCx - tipW / 2; if (maxLeft < minLeft) { left = minLeft; } else { left = Math.max(minLeft, Math.min(left, maxLeft)); } var tail = gapToAnchor + arrowH; var top = anchor.top - tipH - tail; var flipBelow = false; if (top < margin) { top = anchor.bottom + tail; flipBelow = true; } if (top + tipH > vh - margin) { top = Math.max(margin, vh - margin - tipH); } if (flipBelow) { tipEl.classList.add("metric-tooltip--flip-below"); } $tip.css({ left: left + "px", top: top + "px" }); tipRect = tipEl.getBoundingClientRect(); tipW = tipRect.width; var arrowCenterX = anchorCx - tipRect.left; var minCx = 18; var maxCx = tipW - minCx; arrowCenterX = Math.max(minCx, Math.min(maxCx, arrowCenterX)); tipEl.style.setProperty("--metric-tooltip-callout-x", arrowCenterX + "px"); $tip.addClass("metric-tooltip--positioned"); } function schedulePositionMobileMetricTooltip($wrap) { if (!$wrap || !$wrap.length || !$wrap.hasClass("show")) return; requestAnimationFrame(function () { requestAnimationFrame(function () { positionMobileMetricTooltip($wrap); }); }); } var metricTooltipRepositionTimer = null; function repositionOpenMetricTooltips() { clearTimeout(metricTooltipRepositionTimer); metricTooltipRepositionTimer = setTimeout(function () { $(".challenge-floor .metric-help.show").each(function () { positionMobileMetricTooltip($(this)); }); }, 40); } $(document).on("click", ".challenge-floor .metric-help-btn", function (e) { e.preventDefault(); e.stopPropagation(); if ($(this).attr("aria-hidden") === "true") return; metricHelpToggleFromWrap($(this).closest(".metric-help")); }); $(document).on("keydown", ".challenge-floor .metric-help-btn", function (e) { if (e.key !== "Enter" && e.key !== " ") return; e.preventDefault(); if ($(this).attr("aria-hidden") === "true") return; metricHelpToggleFromWrap($(this).closest(".metric-help")); }); function metricHelpWrapFromMobileMetricLeft($left) { var $label = $left.find(".metric-label-dotted").first(); if (!$label.length) return $(); return $label.siblings(".metric-help"); } $(document).on("click", ".challenge-floor .card-mobile-metrics .metric-label-dotted", function (e) { e.preventDefault(); e.stopPropagation(); var $wrap = $(this).siblings(".metric-help"); if (!$wrap.length) return; metricHelpToggleFromWrap($wrap); }); $(document).on("click", ".challenge-floor .card-mobile-metrics .card-mobile-metric-left", function (e) { if ($(e.target).closest(".metric-label-dotted").length) return; var $wrap = metricHelpWrapFromMobileMetricLeft($(this)); if (!$wrap.length) return; e.preventDefault(); e.stopPropagation(); metricHelpToggleFromWrap($wrap); }); $(document).on("keydown", ".challenge-floor .card-mobile-metrics .metric-label-dotted", function (e) { if (e.key !== "Enter" && e.key !== " ") return; e.preventDefault(); var $wrap = $(this).siblings(".metric-help"); if (!$wrap.length) return; metricHelpToggleFromWrap($wrap); }); $(document).on("click", function (e) { if (metricTooltipSuppressDocClose) return; var $t = $(e.target); if ($t.closest(".metric-tooltip--mobile-portal").length) return; if ($t.closest(".challenge-floor .metric-help").length) return; if ($t.closest(".challenge-floor .card-mobile-metrics .card-mobile-metric-left").length) return; $(".challenge-floor .metric-help.show").each(function () { var $w = $(this); $w.removeClass("show"); restoreMetricTooltipDom($w); }); }); var CHALLENGE_CHECKOUT_COUPON = "AIFO50"; var CHALLENGE_CHECKOUT_BASE = "https://dashboard.aifo.com/checkout"; function buildChallengeCheckoutUrl(productId, variantId) { return ( CHALLENGE_CHECKOUT_BASE + "?product_id=" + encodeURIComponent(productId) + "&variant_id=" + encodeURIComponent(variantId) ); } function copyChallengeCouponCode(code) { var text = String(code || ""); if (!text) return Promise.resolve(); if (navigator.clipboard && typeof navigator.clipboard.writeText === "function") { return navigator.clipboard.writeText(text); } return new Promise(function (resolve, reject) { var $ta = $("