Game/Idle/IdleFormat.cs
using System;

/// <summary>
/// Number formatting helpers for the Reactor (Idle) mode.
/// Idle economies overflow int/long fast, so every currency is a double and
/// gets rendered through here — never with ToString("N0").
/// </summary>
public static class IdleFormat
{
	static readonly string[] Suffixes =
	{
		"", "K", "M", "B", "T", "Qa", "Qi", "Sx", "Sp", "Oc", "No",
		"Dc", "Ud", "Dd", "Td", "Qad", "Qid", "Sxd", "Spd", "Ocd", "Nod", "Vg"
	};

	/// <summary>Compact currency label — 1234 => "1.23K", 5.2e9 => "5.20B".</summary>
	public static string Short( double v )
	{
		if ( double.IsNaN( v ) || double.IsInfinity( v ) ) return "0";
		if ( v < 0 ) return "-" + Short( -v );
		if ( v < 1000 ) return v < 10 && v % 1 != 0 ? v.ToString( "F1" ) : ((long)v).ToString();

		int tier = (int)MathF.Floor( (float)Math.Log10( v ) / 3f );
		if ( tier >= Suffixes.Length ) tier = Suffixes.Length - 1;

		double scaled = v / Math.Pow( 1000, tier );

		// Keep 3 significant digits so the number never jitters in width
		string num = scaled >= 100 ? scaled.ToString( "F0" )
				   : scaled >= 10  ? scaled.ToString( "F1" )
				   :                 scaled.ToString( "F2" );

		return num + Suffixes[tier];
	}

	/// <summary>Rate label — "12.4K /s".</summary>
	public static string Rate( double perSecond ) => Short( perSecond ) + " /s";

	/// <summary>"2h 14m", "48s" — used by the offline report and cooldowns.</summary>
	public static string Duration( double seconds )
	{
		if ( seconds < 0 ) seconds = 0;

		int total = (int)seconds;
		int d = total / 86400;
		int h = (total % 86400) / 3600;
		int m = (total % 3600) / 60;
		int s = total % 60;

		if ( d > 0 ) return $"{d}d {h}h";
		if ( h > 0 ) return $"{h}h {m}m";
		if ( m > 0 ) return $"{m}m {s}s";
		return $"{s}s";
	}

	/// <summary>Clock style "04:31" — used for short live countdowns.</summary>
	public static string Clock( double seconds )
	{
		if ( seconds < 0 ) seconds = 0;
		int m = (int)(seconds / 60);
		int s = (int)(seconds % 60);
		return $"{m:00}:{s:00}";
	}

	/// <summary>Percentage with no decimals — 0.35 => "35%".</summary>
	public static string Pct( float f ) => $"{(int)MathF.Round( f * 100f )}%";
}