-
+
{entry.label}
{luckLabel} · {formatSignedPercent((successDelta / entry.draws) * 100, 4)}
-
+
0 ? "positive" : undefined}>
{entry.successes}
{textContent.successesLabel || "Réussites"}
@@ -583,14 +726,14 @@ function RngHistoryRow({ entry, index, textContent }) {
- {textContent.successesLabel || "Réussites"}{entry.successes}/{entry.draws}
- {textContent.failsLabel || "Fails"}{entry.fails}
- {textContent.observedRateLabel || "Taux de réussite"}{formatPercent((entry.successes / entry.draws) * 100, 6)}
- {textContent.expectedSuccessesLabel || "Réussites attendues"}{formatNumber(expectedSuccesses, 2)}
- {textContent.longestFailStreakLabel || "Pire série d'échec"}{entry.longestFailStreak}
- {textContent.longestSuccessStreakLabel || "Réussite consécutive"}{entry.longestSuccessStreak}
- {textContent.firstSuccessCostLabel || "Coût de la 1ere réussite"}{entry.firstSuccessDraw > 0 ? formatCost(costs?.simulationFirstSuccessCost) : "—"}
- {textContent.costPerSuccessLabel || "Coût moyen/réussite"}{entry.successes > 0 ? formatCost(costs?.simulationCostPerSuccess) : "—"}
+ 0 ? "positive" : ""} value={`${entry.successes}/${entry.draws}`} />
+ 0 ? "negative" : ""} value={entry.fails} />
+
+
+ 0 ? "negative" : ""} value={entry.longestFailStreak} />
+ 0 ? "positive" : ""} value={entry.longestSuccessStreak} />
+ 0 ? formatCost(costs?.simulationFirstSuccessCost) : "—"} />
+ 0 ? formatCost(costs?.simulationCostPerSuccess) : "—"} />
);
diff --git a/website/src/features/toolboxes/modules/rngSimulatorUtils.js b/website/src/features/toolboxes/modules/rngSimulatorUtils.js
index 8d3d6ba..e74bad2 100644
--- a/website/src/features/toolboxes/modules/rngSimulatorUtils.js
+++ b/website/src/features/toolboxes/modules/rngSimulatorUtils.js
@@ -8,14 +8,45 @@ export const RNG_SIMULATOR_LIMITS = {
multiplierMax: 1000000,
simulationDrawsMin: 1,
simulationDrawsMax: 1000000,
+ realDrawsMax: 1000000000,
occurrenceRateMin: 0,
occurrenceRateMax: 1000000000
};
-export const RNG_SIMULATOR_OCCURRENCE_UNITS = ["perHour", "perDay", "perMonth"];
+export const RNG_SIMULATOR_OCCURRENCE_UNITS = ["perHour", "perDay"];
+export const RNG_SIMULATOR_HOURLY_DURATION_PRESETS = [
+ { id: "1h", label: "1h", hours: 1 },
+ { id: "5h", label: "5h", hours: 5 },
+ { id: "10h", label: "10h", hours: 10 },
+ { id: "1j", label: "1j", hours: 24 }
+];
+export const RNG_SIMULATOR_DAILY_DURATION_PRESETS = [
+ { id: "1j", label: "1j", hours: 24 },
+ { id: "3j", label: "3j", hours: 72 },
+ { id: "7j", label: "7j", hours: 168 },
+ { id: "15j", label: "15j", hours: 360 }
+];
+export const RNG_SIMULATOR_MONTHLY_DURATION_PRESETS = [
+ { id: "1j", label: "1j", hours: 24 },
+ { id: "3j", label: "3j", hours: 72 },
+ { id: "7j", label: "7j", hours: 168 },
+ { id: "30j", label: "30j", hours: 720 }
+];
+export const RNG_SIMULATOR_YEARLY_DURATION_PRESETS = [
+ { id: "30j", label: "30j", hours: 720 },
+ { id: "90j", label: "90j", hours: 2160 },
+ { id: "1a", label: "1 an", hours: 8760 },
+ { id: "5a", label: "5 ans", hours: 43800 }
+];
+export const RNG_SIMULATOR_LONG_DURATION_PRESETS = [
+ { id: "1a", label: "1 an", hours: 8760 },
+ { id: "5a", label: "5 ans", hours: 43800 },
+ { id: "10a", label: "10 ans", hours: 87600 },
+ { id: "25a", label: "25 ans", hours: 219000 }
+];
export function clampInteger(value, min, max, fallback = min) {
- const parsed = Number.parseInt(value, 10);
+ const parsed = Number.parseInt(String(value ?? "").replace(/[\s\u00a0]/g, ""), 10);
if (!Number.isFinite(parsed)) return fallback;
return Math.min(max, Math.max(min, parsed));
}
@@ -76,6 +107,11 @@ export function getEffectiveRate(rate, multiplier = 0) {
export function calculateAtLeastOneProbability(rate, draws, multiplier = 0) {
const probability = getEffectiveRate(rate, multiplier) / 100;
const cleanDraws = clampInteger(draws, 0, Number.MAX_SAFE_INTEGER, 0);
+ return calculateProbabilityForDrawCount(probability, cleanDraws);
+}
+
+function calculateProbabilityForDrawCount(probability, draws) {
+ const cleanDraws = clampDecimal(draws, 0, Number.MAX_SAFE_INTEGER, 0);
if (probability <= 0 || cleanDraws <= 0) return 0;
if (probability >= 1) return 1;
return -Math.expm1(cleanDraws * Math.log1p(-probability));
@@ -91,19 +127,32 @@ export function calculateDrawsForChance(rate, targetChance, multiplier = 0) {
}
export function normalizeOccurrenceUnit(value) {
+ if (value === "perMonth") return "perDay";
return RNG_SIMULATOR_OCCURRENCE_UNITS.includes(value) ? value : "perHour";
}
+export function normalizeOccurrenceSettings(value, unit) {
+ const cleanValue = clampDecimal(value, RNG_SIMULATOR_LIMITS.occurrenceRateMin, RNG_SIMULATOR_LIMITS.occurrenceRateMax, 0);
+ if (unit === "perMonth") {
+ return {
+ occurrenceValue: cleanValue / 30,
+ occurrenceUnit: "perDay"
+ };
+ }
+ return {
+ occurrenceValue: cleanValue,
+ occurrenceUnit: normalizeOccurrenceUnit(unit)
+ };
+}
+
export function calculateRngSummary({ rate, multiplier = 0, simulationDraws, occurrenceValue = 0, occurrenceUnit = "perHour" }) {
const effectiveRate = getEffectiveRate(rate, multiplier);
const probability = effectiveRate / 100;
const averageDraws = probability > 0 ? 1 / probability : 0;
- const cleanOccurrenceValue = clampDecimal(occurrenceValue, RNG_SIMULATOR_LIMITS.occurrenceRateMin, RNG_SIMULATOR_LIMITS.occurrenceRateMax, 0);
- const cleanOccurrenceUnit = normalizeOccurrenceUnit(occurrenceUnit);
+ const { occurrenceValue: cleanOccurrenceValue, occurrenceUnit: cleanOccurrenceUnit } = normalizeOccurrenceSettings(occurrenceValue, occurrenceUnit);
let averageHours = 0;
if (cleanOccurrenceValue > 0 && cleanOccurrenceUnit === "perHour") averageHours = averageDraws / cleanOccurrenceValue;
else if (cleanOccurrenceValue > 0 && cleanOccurrenceUnit === "perDay") averageHours = (averageDraws / cleanOccurrenceValue) * 24;
- else if (cleanOccurrenceValue > 0 && cleanOccurrenceUnit === "perMonth") averageHours = (averageDraws / cleanOccurrenceValue) * 24 * 30;
return {
probability,
@@ -112,9 +161,9 @@ export function calculateRngSummary({ rate, multiplier = 0, simulationDraws, occ
chanceForSimulationDraws: calculateAtLeastOneProbability(rate, simulationDraws, multiplier),
timeByFrequency: {
hour: averageHours,
- day: averageHours > 0 ? averageHours / 24 : 0,
- month: averageHours > 0 ? averageHours / 24 / 30 : 0
+ day: averageHours > 0 ? averageHours / 24 : 0
},
+ durationChances: calculateDurationChances(rate, multiplier, cleanOccurrenceValue, cleanOccurrenceUnit),
drawsForChance: {
50: calculateDrawsForChance(rate, 0.5, multiplier),
90: calculateDrawsForChance(rate, 0.9, multiplier),
@@ -124,6 +173,79 @@ export function calculateRngSummary({ rate, multiplier = 0, simulationDraws, occ
};
}
+export function calculateDurationChances(rate, multiplier = 0, occurrenceValue = 0, occurrenceUnit = "perHour") {
+ const { occurrenceValue: cleanOccurrenceValue, occurrenceUnit: cleanOccurrenceUnit } = normalizeOccurrenceSettings(occurrenceValue, occurrenceUnit);
+ const drawsPerHour = cleanOccurrenceUnit === "perDay" ? cleanOccurrenceValue / 24 : cleanOccurrenceValue;
+ const averageHours = drawsPerHour > 0 ? (1 / (getEffectiveRate(rate, multiplier) / 100)) / drawsPerHour : 0;
+ const presets = getDurationPresetsForAverageHours(averageHours, cleanOccurrenceUnit);
+ return presets.map((preset) => ({
+ ...preset,
+ draws: drawsPerHour > 0 ? drawsPerHour * preset.hours : 0,
+ chance: drawsPerHour > 0 ? calculateProbabilityForDrawCount(getEffectiveRate(rate, multiplier) / 100, drawsPerHour * preset.hours) : 0
+ }));
+}
+
+export function getDurationPresetsForAverageHours(averageHours, occurrenceUnit = "perHour") {
+ if (!Number.isFinite(averageHours) || averageHours <= 0) {
+ return occurrenceUnit === "perDay" ? RNG_SIMULATOR_DAILY_DURATION_PRESETS : RNG_SIMULATOR_HOURLY_DURATION_PRESETS;
+ }
+ if (occurrenceUnit === "perDay" && averageHours <= 24 * 365) return RNG_SIMULATOR_MONTHLY_DURATION_PRESETS;
+ if (averageHours <= 24 * 7) return RNG_SIMULATOR_HOURLY_DURATION_PRESETS;
+ if (averageHours <= 24 * 365) return RNG_SIMULATOR_MONTHLY_DURATION_PRESETS;
+ if (averageHours <= 24 * 365 * 10) return RNG_SIMULATOR_YEARLY_DURATION_PRESETS;
+ return RNG_SIMULATOR_LONG_DURATION_PRESETS;
+}
+
+export function getAveragePositionStatus(averageRatio) {
+ const ratio = Number.isFinite(averageRatio) && averageRatio >= 0 ? averageRatio : 0;
+ if (ratio < 0.25) return { id: "start", tone: "positive" };
+ if (ratio < 0.5) return { id: "farBelow", tone: "positive" };
+ if (ratio < 0.9) return { id: "below", tone: "positive" };
+ if (ratio < 1.1) return { id: "average", tone: "" };
+ if (ratio < 1.5) return { id: "slightlyAbove", tone: "negative" };
+ if (ratio < 3) return { id: "high", tone: "negative" };
+ return { id: "catastrophic", tone: "negative" };
+}
+
+export function calculateRngRealDrawsSummary({ rate, multiplier = 0, realDraws = 0, averageDraws = 0, drawsForChance = {}, costPerDraw = "" }) {
+ const cleanRealDraws = clampInteger(realDraws, 0, RNG_SIMULATOR_LIMITS.realDrawsMax, 0);
+ const cumulativeChance = calculateAtLeastOneProbability(rate, cleanRealDraws, multiplier);
+ const noSuccessChance = 1 - cumulativeChance;
+ const cleanAverageDraws = Number.isFinite(averageDraws) && averageDraws > 0 ? averageDraws : 0;
+ const averageRatio = cleanAverageDraws > 0 ? cleanRealDraws / cleanAverageDraws : 0;
+ const averagePosition = getAveragePositionStatus(averageRatio);
+ const drawsUntilAverage = cleanAverageDraws > cleanRealDraws ? Math.ceil(cleanAverageDraws - cleanRealDraws) : 0;
+ const cleanCost = Number(normalizeCostString(costPerDraw));
+ const hasCost = Number.isFinite(cleanCost) && cleanCost > 0;
+ const averageSuccessCost = hasCost && cleanAverageDraws > 0 ? cleanAverageDraws * cleanCost : 0;
+ const spentCost = hasCost ? cleanRealDraws * cleanCost : 0;
+ const targets = {};
+ Object.entries(drawsForChance || {}).forEach(([target, draws]) => {
+ const remainingDraws = Number.isFinite(draws) ? Math.max(0, draws - cleanRealDraws) : Number.POSITIVE_INFINITY;
+ targets[target] = {
+ draws,
+ remainingDraws,
+ progress: Number.isFinite(draws) && draws > 0 ? Math.min(1, cleanRealDraws / draws) : 0,
+ remainingCost: hasCost && Number.isFinite(remainingDraws) ? remainingDraws * cleanCost : 0
+ };
+ });
+ return {
+ realDraws: cleanRealDraws,
+ hasCost,
+ cumulativeChance,
+ noSuccessChance,
+ averageRatio,
+ averagePosition,
+ drawsUntilAverage,
+ averageExceeded: cleanAverageDraws > 0 && cleanRealDraws >= cleanAverageDraws,
+ spentCost,
+ averageSuccessCost,
+ averageCostDelta: hasCost && cleanAverageDraws > 0 ? spentCost - averageSuccessCost : 0,
+ costUntilAverage: hasCost && drawsUntilAverage > 0 ? drawsUntilAverage * cleanCost : 0,
+ targets
+ };
+}
+
export function calculateRngCostSummary({ costPerDraw, averageDraws, drawsForChance, simulationDraws, simulationSuccesses, firstSuccessDraw = 0 }) {
const cleanCost = Number(normalizeCostString(costPerDraw));
if (!Number.isFinite(cleanCost) || cleanCost <= 0) return null;
diff --git a/website/src/features/toolboxes/modules/textImport/rngSimulator.js b/website/src/features/toolboxes/modules/textImport/rngSimulator.js
index ec8d819..9825026 100644
--- a/website/src/features/toolboxes/modules/textImport/rngSimulator.js
+++ b/website/src/features/toolboxes/modules/textImport/rngSimulator.js
@@ -8,7 +8,8 @@ export function exportRngSimulatorText(data) {
(data.rates || []).forEach((rate) => {
const cost = rate.costPerDraw ? ` | costPerDraw=${rate.costPerDraw}` : "";
const occurrences = rate.occurrenceValue ? ` | occurrenceValue=${rate.occurrenceValue} | occurrenceUnit=${rate.occurrenceUnit || "perHour"}` : "";
- lines.push(`- ${rate.label} | rate=${rate.rate}${rate.id === data.activeRateId ? " | active=true" : ""}${cost} | multiplier=${rate.multiplier ?? 0}${occurrences}`);
+ const realDraws = rate.realDraws ? ` | realDraws=${rate.realDraws}` : "";
+ lines.push(`- ${rate.label} | rate=${rate.rate}${rate.id === data.activeRateId ? " | active=true" : ""}${cost} | multiplier=${rate.multiplier ?? 0}${occurrences}${realDraws}`);
});
lines.push(
"",
@@ -56,7 +57,8 @@ export function importRngSimulatorText(text, context) {
costPerDraw: pipeMeta.costPerDraw,
multiplier: pipeMeta.multiplier,
occurrenceValue: pipeMeta.occurrenceValue,
- occurrenceUnit: pipeMeta.occurrenceUnit
+ occurrenceUnit: pipeMeta.occurrenceUnit,
+ realDraws: pipeMeta.realDraws
});
if (pipeMeta.active === "true") data.activeRateId = id;
return;
diff --git a/website/src/features/toolboxes/storage/modules/rngSimulator.js b/website/src/features/toolboxes/storage/modules/rngSimulator.js
index b3bfbf8..e35c195 100644
--- a/website/src/features/toolboxes/storage/modules/rngSimulator.js
+++ b/website/src/features/toolboxes/storage/modules/rngSimulator.js
@@ -4,7 +4,7 @@ import {
clampDecimal,
clampInteger,
normalizeCostString,
- normalizeOccurrenceUnit,
+ normalizeOccurrenceSettings,
normalizeRateString
} from "../../modules/rngSimulatorUtils.js";
import { uid } from "./shared.js";
@@ -12,14 +12,16 @@ import { uid } from "./shared.js";
function normalizeRateEntry(entry) {
const label = String(entry?.label || "").trim();
if (!label) return null;
+ const occurrence = normalizeOccurrenceSettings(entry?.occurrenceValue, entry?.occurrenceUnit);
return {
id: entry?.id || uid("rng"),
label,
rate: normalizeRateString(entry?.rate),
costPerDraw: normalizeCostString(entry?.costPerDraw),
multiplier: clampDecimal(entry?.multiplier, RNG_SIMULATOR_LIMITS.multiplierMin, RNG_SIMULATOR_LIMITS.multiplierMax, 0),
- occurrenceValue: clampDecimal(entry?.occurrenceValue, RNG_SIMULATOR_LIMITS.occurrenceRateMin, RNG_SIMULATOR_LIMITS.occurrenceRateMax, 0),
- occurrenceUnit: normalizeOccurrenceUnit(entry?.occurrenceUnit)
+ occurrenceValue: occurrence.occurrenceValue,
+ occurrenceUnit: occurrence.occurrenceUnit,
+ realDraws: clampInteger(entry?.realDraws, 0, RNG_SIMULATOR_LIMITS.realDrawsMax, 0)
};
}
@@ -54,6 +56,7 @@ export function compactRngSimulatorDataForStorage(value) {
compactRate.occurrenceValue = rate.occurrenceValue;
compactRate.occurrenceUnit = rate.occurrenceUnit;
}
+ if (rate.realDraws !== 0) compactRate.realDraws = rate.realDraws;
return compactRate;
});
}
diff --git a/website/src/pages/LibraryPage.jsx b/website/src/pages/LibraryPage.jsx
index c8c79af..053db14 100644
--- a/website/src/pages/LibraryPage.jsx
+++ b/website/src/pages/LibraryPage.jsx
@@ -27,6 +27,7 @@ import {
} from "../features/toolboxes/storage/toolboxStorage.js";
const LIBRARY_TOOLBOX_ID = "library";
+const LIBRARY_ANCHOR_RETRY_DELAYS = [0, 80, 240, 600];
const MODULE_COMPONENTS = {
memos: lazy(() => import("../features/toolboxes/modules/MemosModule.jsx").then((module) => ({ default: module.MemosModule }))),
@@ -83,12 +84,23 @@ function getCurrentLibraryAnchor() {
return match ? decodeURIComponent(match[1]) : "";
}
-function scrollToLibraryTool(anchorId, replace = false) {
- const element = document.getElementById(anchorId);
- if (!element) return;
+function getLibraryAnchorOffset() {
+ const topbar = document.querySelector(".topbar");
+ const topbarHeight = topbar?.getBoundingClientRect?.().height || 0;
+ return topbarHeight + 16;
+}
+
+function scrollToLibraryTool(anchorId, replace = false, behavior = "smooth") {
+ if (!anchorId) return;
if (replace) history.replaceState(null, "", `#/library#${encodeURIComponent(anchorId)}`);
- const top = element.getBoundingClientRect().top + window.scrollY - 24;
- window.scrollTo({ top: Math.max(0, top), behavior: "smooth" });
+ LIBRARY_ANCHOR_RETRY_DELAYS.forEach((delay, index) => {
+ window.setTimeout(() => {
+ const element = document.getElementById(anchorId);
+ if (!element) return;
+ const top = element.getBoundingClientRect().top + window.scrollY - getLibraryAnchorOffset();
+ window.scrollTo({ top: Math.max(0, top), behavior: index === 0 ? behavior : "auto" });
+ }, delay);
+ });
}
function findCategoryForAnchor(categories, anchorId) {
@@ -149,7 +161,7 @@ export function LibraryPage({ siteContent, actions }) {
const category = findCategoryForAnchor(libraryCategories, anchorId) || libraryCategories[0];
setActiveCategoryKey(category.key);
if (!anchorId) return;
- requestAnimationFrame(() => scrollToLibraryTool(anchorId));
+ requestAnimationFrame(() => scrollToLibraryTool(anchorId, false, "auto"));
}, [libraryPayload, libraryCategories]);
const moduleContext = useMemo(() => ({
diff --git a/website/src/styles/toolboxes/_rng-simulator.scss b/website/src/styles/toolboxes/_rng-simulator.scss
index c2f9632..cfc55e0 100644
--- a/website/src/styles/toolboxes/_rng-simulator.scss
+++ b/website/src/styles/toolboxes/_rng-simulator.scss
@@ -179,11 +179,29 @@
text-align: center;
}
+.rng-simulator-history-score small,
+.rng-simulator-history-rate small {
+ overflow: hidden;
+ color: var(--color-text-primary);
+ font-size: var(--font-size-xs);
+ font-weight: 800;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
.rng-simulator-active-summary small,
-.rng-simulator-table > span:nth-child(-n + 3),
+.rng-simulator-table-heading {
+ overflow: hidden;
+ color: var(--color-text-primary);
+ font-size: var(--font-size-xs);
+ font-weight: 800;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
.rng-simulator-history-metrics small {
overflow: hidden;
- color: var(--color-text-secondary);
+ color: var(--color-accent-gold);
font-size: var(--font-size-xs);
font-weight: 800;
text-overflow: ellipsis;
@@ -191,7 +209,7 @@
}
.rng-simulator-stat span {
- color: var(--color-text-secondary);
+ color: var(--color-accent-gold);
font-size: var(--font-size-xs);
font-weight: 800;
line-height: 1.25;
@@ -203,11 +221,19 @@
.rng-simulator-history-row b,
.rng-simulator-history-rate > span,
.rng-simulator-history-metrics b {
- color: var(--color-accent-gold);
+ color: var(--color-text-primary);
font-weight: 900;
font-variant-numeric: tabular-nums;
}
+.rng-simulator-history-rate > span {
+ color: var(--color-accent-gold);
+}
+
+.rng-simulator-table .rng-simulator-chance-value {
+ color: var(--color-accent-gold);
+}
+
.rng-simulator-active-summary strong {
color: var(--color-accent-gold);
font-size: 24px;
@@ -222,7 +248,7 @@
display: grid;
gap: 6px;
min-width: 0;
- color: var(--color-text-secondary);
+ color: var(--color-accent-gold);
font-size: var(--font-size-xs);
font-weight: 800;
}
@@ -316,7 +342,7 @@
}
.rng-simulator-percent-input > span {
- color: var(--color-text-secondary);
+ color: var(--color-accent-gold);
font-size: var(--font-size-xs);
font-weight: 900;
}
@@ -331,14 +357,28 @@
.rng-simulator-time-results {
display: grid;
- grid-template-columns: repeat(3, minmax(0, 1fr));
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+ gap: 8px;
+}
+
+.rng-simulator-stat-grid,
+.rng-simulator-duration-grid,
+.rng-simulator-tracking-grid {
+ display: grid;
gap: 8px;
}
.rng-simulator-stat-grid {
- display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
- gap: 8px;
+}
+
+.rng-simulator-duration-grid,
+.rng-simulator-tracking-grid {
+ grid-template-columns: repeat(4, minmax(0, 1fr));
+}
+
+.rng-simulator-tracking-control {
+ max-width: 360px;
}
.rng-simulator-stat strong {
@@ -347,6 +387,14 @@
font-size: var(--font-size-sm);
}
+.rng-simulator-stat[data-tone="positive"] strong {
+ color: var(--color-accent-cyan);
+}
+
+.rng-simulator-stat[data-tone="negative"] strong {
+ color: #fca5a5;
+}
+
.rng-simulator-table {
display: grid;
align-items: center;
@@ -374,6 +422,10 @@
grid-template-columns: minmax(88px, 0.6fr) minmax(140px, 1fr) minmax(120px, 1fr);
}
+.rng-simulator-tracking-target-table {
+ grid-template-columns: minmax(88px, 0.55fr) minmax(120px, 0.8fr) minmax(140px, 1fr) minmax(120px, 1fr);
+}
+
.rng-simulator-simulation-overview {
display: grid;
grid-template-columns: repeat(2, minmax(0, 1fr));
@@ -478,6 +530,18 @@
line-height: 1;
}
+.rng-simulator-history-identity[data-tone="positive"] small,
+.rng-simulator-history-metrics > span[data-tone="positive"] b,
+.rng-simulator-history-score[data-tone="positive"] b {
+ color: var(--color-accent-cyan);
+}
+
+.rng-simulator-history-identity[data-tone="negative"] small,
+.rng-simulator-history-metrics > span[data-tone="negative"] b,
+.rng-simulator-history-score[data-tone="negative"] b {
+ color: #fca5a5;
+}
+
.rng-simulator-history-rate {
display: grid;
gap: 2px;
@@ -513,10 +577,13 @@
.rng-simulator-active-summary,
.rng-simulator-primary-controls,
.rng-simulator-stat-grid,
+ .rng-simulator-duration-grid,
+ .rng-simulator-tracking-grid,
.rng-simulator-simulation-summary,
.rng-simulator-occurrence-control,
.rng-simulator-time-results,
- .rng-simulator-target-table {
+ .rng-simulator-target-table,
+ .rng-simulator-tracking-target-table {
grid-template-columns: 1fr;
}