"use client";

import React, { useState, useMemo, useRef, useEffect, useCallback } from "react";
import { Chart, ChartConfiguration, registerables } from "chart.js";

Chart.register(...registerables);

const VERSION = "v0.5";

// --- Types ---

interface Destination {
  name: string;
  distanceLy: number;
  category: "stellar" | "galactic" | "extragalactic" | "cosmological";
  tableOnly?: boolean;
}

interface TravelResults {
  shipTimeYears: number;
  earthTimeYears: number;
  peakVelocityFractionC: number;
  peakGamma: number;
  timeDilationRatio: number;
  massRatioOneWay: number;
  massRatioBrach: number;
}

// --- Constants ---

// In natural units: c = 1 ly/yr
// 1g ≈ 1.03227 ly/yr² (exact: 9.80665 m/s² converted)
const G_TO_LY_YR2 = 1.03227;
const G_STANDARD_MS2 = 9.80665;

const DESTINATIONS: Destination[] = [
  // Stellar neighborhood
  { name: "Proxima Centauri", distanceLy: 4.246, category: "stellar" },
  { name: "Alpha Centauri", distanceLy: 4.37, category: "stellar" },
  { name: "Tau Ceti", distanceLy: 11.91, category: "stellar" },
  { name: "40 Eridani", distanceLy: 16.34, category: "stellar" },
  { name: "Vega", distanceLy: 25.04, category: "stellar" },
  { name: "Zeta Reticuli", distanceLy: 39.3, category: "stellar", tableOnly: true },
  { name: "Aldebaran", distanceLy: 65.3, category: "stellar", tableOnly: true },
  { name: "Algol", distanceLy: 90, category: "stellar", tableOnly: true },
  { name: "Canopus", distanceLy: 310, category: "stellar", tableOnly: true },
  { name: "Betelgeuse", distanceLy: 500, category: "stellar" },
  { name: "Gaia BH1 (black hole)", distanceLy: 1_576, category: "stellar" },
  // Galactic
  { name: "Orion Nebula", distanceLy: 1344, category: "galactic" },
  { name: "Sagittarius A* (Galactic Center)", distanceLy: 26_000, category: "galactic" },
  { name: "Far side of Milky Way", distanceLy: 100_000, category: "galactic" },
  // Extragalactic
  { name: "Large Magellanic Cloud", distanceLy: 160_000, category: "extragalactic" },
  { name: "Andromeda Galaxy", distanceLy: 2_537_000, category: "extragalactic" },
  // Cosmological
  { name: "Virgo Cluster", distanceLy: 53_800_000, category: "cosmological" },
  { name: "Edge of Observable Universe", distanceLy: 46_500_000_000, category: "cosmological" },
  { name: "100 Trillion Light-Years", distanceLy: 100_000_000_000_000, category: "cosmological" },
];

const CATEGORY_LABELS: Record<string, string> = {
  stellar: "Stellar Neighborhood",
  galactic: "Milky Way",
  extragalactic: "Extragalactic",
  cosmological: "Cosmological",
};

const ACCELERATION_PRESETS = [
  { label: "0.1g", value: 0.1 },
  { label: "0.3g", value: 0.3 },
  { label: "1g", value: 1 },
  { label: "1.5g", value: 1.5 },
  { label: "2g", value: 2 },
  { label: "4g", value: 4 },
  { label: "10g", value: 10 },
];

// Reference masses (kg) for feasibility comparisons
const REFERENCE_MASSES = [
  { name: "the ISS", kg: 4.2e5 },
  { name: "the Great Pyramid of Giza", kg: 6e9 },
  { name: "Earth's oceans", kg: 1.4e21 },
  { name: "the Moon", kg: 7.342e22 },
  { name: "Earth", kg: 5.972e24 },
  { name: "Jupiter", kg: 1.898e27 },
  { name: "the Sun", kg: 1.989e30 },
  { name: "Sagittarius A*", kg: 8.26e36 },
  { name: "the Milky Way", kg: 1.5e42 },
  { name: "the observable universe", kg: 1.5e53 },
];

// Schwarzschild radius: r_s = 2GM/c² → factor ≈ 1.485e-27 m/kg
const SCHWARZSCHILD_FACTOR = (2 * 6.674e-11) / (2.998e8 * 2.998e8);

// Reference densities (kg/m³) for radius comparisons — lowest to highest
const DENSITY_COMPARISONS = [
  { name: "water", density: 1_000 },
  { name: "tungsten", density: 19_250 },
  { name: "neutronium", density: 4e17 },
];

// Tolman-Oppenheimer-Volkoff limit: ~3 solar masses — above this, gravitational collapse is inevitable
const TOV_LIMIT_KG = 3 * 1.989e30;

// c² in J/kg for energy calculations
const C_SQUARED = 2.998e8 * 2.998e8;

// Reference energies
const GLOBAL_ANNUAL_ENERGY = 5.8e20; // ~580 EJ, global energy consumption per year
const SUN_ANNUAL_ENERGY = 1.21e34; // Sun's total energy output per year

// --- Computation ---

function computeTravel(
  distanceLy: number,
  accelerationG: number,
  brachistochrone: boolean,
  coastFraction: number = 0,
): TravelResults {
  const a = accelerationG * G_TO_LY_YR2; // ly/yr²
  const d = distanceLy;
  const cf = Math.max(0, Math.min(1, coastFraction));

  // Acceleration distance per phase; brachistochrone has 2 symmetric phases
  const dAccel = brachistochrone ? (d * (1 - cf)) / 2 : d * (1 - cf);
  const dCoast = d * cf;
  const phases = brachistochrone ? 2 : 1;

  const gamma = 1 + a * dAccel;
  const beta = Math.sqrt(1 - 1 / (gamma * gamma));

  // Acceleration/deceleration phase times
  const tauAccel = (1 / a) * Math.acosh(gamma);
  const tAccel = (1 / a) * Math.sqrt(gamma * gamma - 1);

  // Coast phase times (constant velocity β)
  const tCoast = beta > 0 ? dCoast / beta : 0;
  const tauCoast = beta > 0 ? dCoast / (beta * gamma) : 0;

  const tau = phases * tauAccel + tauCoast;
  const t = phases * tAccel + tCoast;
  const oneWayRatio = gamma * (1 + beta); // γ(1+β) mass ratio

  return {
    shipTimeYears: tau,
    earthTimeYears: t,
    peakVelocityFractionC: beta,
    peakGamma: gamma,
    timeDilationRatio: t / tau,
    massRatioOneWay: oneWayRatio,
    massRatioBrach: brachistochrone ? oneWayRatio * oneWayRatio : oneWayRatio,
  };
}

// --- Formatting ---

function formatTime(years: number): string {
  if (!isFinite(years) || isNaN(years)) return "—";

  if (years < 1 / 365.25 / 24) {
    const minutes = years * 365.25 * 24 * 60;
    if (minutes < 1) return `${(minutes * 60).toFixed(1)} seconds`;
    return `${Math.round(minutes)} minutes`;
  } else if (years < 1 / 365.25) {
    return `${(years * 365.25 * 24).toFixed(1)} hours`;
  } else if (years < 1 / 12) {
    return `${Math.round(years * 365.25)} days`;
  } else if (years < 1) {
    const months = Math.floor(years * 12);
    const days = Math.round((years * 12 - months) * 30.44);
    return days > 0 ? `${months} months, ${days} days` : `${months} months`;
  } else if (years < 100) {
    const y = Math.floor(years);
    const months = Math.round((years - y) * 12);
    if (months === 12) return `${y + 1} years`;
    return months > 0 ? `${y} years, ${months} months` : `${y} years`;
  } else if (years < 1_000) {
    return `${Math.round(years).toLocaleString()} years`;
  } else if (years < 1e6) {
    return `${(years / 1_000).toFixed(1)} thousand years`;
  } else if (years < 1e9) {
    return `${(years / 1e6).toFixed(2)} million years`;
  } else if (years < 1e12) {
    return `${(years / 1e9).toFixed(2)} billion years`;
  } else {
    return `${(years / 1e12).toFixed(2)} trillion years`;
  }
}

function formatTimeShort(years: number): string {
  if (!isFinite(years) || isNaN(years)) return "—";

  if (years < 1 / 365.25 / 24) {
    const minutes = years * 365.25 * 24 * 60;
    if (minutes < 1) return `${(minutes * 60).toFixed(0)}s`;
    return `${Math.round(minutes)}m`;
  } else if (years < 1 / 365.25) {
    return `${(years * 365.25 * 24).toFixed(1)}h`;
  } else if (years < 1 / 12) {
    return `${Math.round(years * 365.25)}d`;
  } else if (years < 1) {
    return `${(years * 12).toFixed(1)} mo`;
  } else if (years < 1_000) {
    return `${years.toFixed(1)} yr`;
  } else if (years < 1e6) {
    return `${(years / 1_000).toFixed(1)}k yr`;
  } else if (years < 1e9) {
    return `${(years / 1e6).toFixed(1)}M yr`;
  } else if (years < 1e12) {
    return `${(years / 1e9).toFixed(1)}B yr`;
  } else {
    return `${(years / 1e12).toFixed(1)}T yr`;
  }
}

function formatVelocity(fractionC: number, gamma?: number): string {
  if (!isFinite(fractionC) || isNaN(fractionC)) return "—";

  if (fractionC < 0.001) {
    return `${(fractionC * 299_792.458).toFixed(1)} km/s`;
  } else if (fractionC > 0.9999) {
    // When float64 can't distinguish v from c, compute nines from γ: 1-β ≈ 1/(2γ²)
    const nines =
      fractionC >= 1.0 && gamma && gamma > 1 ? Math.log10(2) + 2 * Math.log10(gamma) : -Math.log10(1 - fractionC);
    if (nines >= 2) {
      return `0.${"9".repeat(Math.min(Math.floor(nines), 15))}…c (${Math.floor(nines)} nines)`;
    }
    return `${(fractionC * 100).toFixed(4)}% c`;
  } else {
    return `${(fractionC * 100).toFixed(2)}% c`;
  }
}

function formatDistance(ly: number): string {
  if (ly < 100) {
    return `${ly.toFixed(ly < 10 ? 2 : 1)} ly`;
  } else if (ly < 1e6) {
    return `${ly.toLocaleString(undefined, { maximumFractionDigits: 0 })} ly`;
  } else if (ly < 1e9) {
    return `${(ly / 1e6).toFixed(1)}M ly`;
  } else if (ly < 1e12) {
    return `${(ly / 1e9).toFixed(1)}B ly`;
  } else {
    return `${(ly / 1e12).toFixed(1)}T ly`;
  }
}

function formatMassRatio(ratio: number): string {
  if (!isFinite(ratio) || isNaN(ratio)) return "—";
  if (ratio < 100) return `${ratio.toFixed(2)} : 1`;
  if (ratio < 1e6) return `${ratio.toLocaleString(undefined, { maximumFractionDigits: 0 })} : 1`;
  return `${ratio.toExponential(2)} : 1`;
}

function formatMass(kg: number): string {
  if (!isFinite(kg) || isNaN(kg)) return "—";
  const tonnes = kg / 1000;
  if (kg < 1000) return `${kg.toFixed(1)} kg`;
  if (tonnes < 1e3) return `${tonnes.toFixed(1)} tonnes`;
  if (tonnes < 1e6) return `${(tonnes / 1e3).toFixed(2)} thousand tonnes`;
  if (tonnes < 1e9) return `${(tonnes / 1e6).toFixed(2)} million tonnes`;
  if (tonnes < 1e12) return `${(tonnes / 1e9).toFixed(2)} billion tonnes`;
  if (tonnes < 1e15) return `${(tonnes / 1e12).toFixed(2)} trillion tonnes`;
  if (tonnes < 1e18) return `${(tonnes / 1e15).toFixed(2)} quadrillion tonnes`;
  if (tonnes < 1e21) return `${(tonnes / 1e18).toFixed(2)} quintillion tonnes`;
  if (tonnes < 1e24) return `${(tonnes / 1e21).toFixed(2)} sextillion tonnes`;
  if (tonnes < 1e27) return `${(tonnes / 1e24).toFixed(2)} septillion tonnes`;
  if (tonnes < 1e30) return `${(tonnes / 1e27).toFixed(2)} octillion tonnes`;
  return `${tonnes.toExponential(2)} tonnes`;
}

function formatGamma(gamma: number): string {
  if (!isFinite(gamma) || isNaN(gamma)) return "—";
  if (gamma < 10) return gamma.toFixed(3);
  if (gamma < 1e6) return gamma.toLocaleString(undefined, { maximumFractionDigits: 0 });
  return gamma.toExponential(2);
}

function formatRapidity(phi: number): string {
  if (!isFinite(phi) || isNaN(phi)) return "—";
  if (phi < 10) return phi.toFixed(3);
  if (phi < 1e3) return phi.toFixed(1);
  return phi.toExponential(2);
}

function formatRadius(meters: number): string {
  if (!isFinite(meters) || isNaN(meters)) return "—";
  if (meters < 1e-15) return `${(meters * 1e18).toFixed(1)} am`;
  if (meters < 1e-12) return `${(meters * 1e15).toFixed(1)} fm`;
  if (meters < 1e-9) return `${(meters * 1e12).toFixed(1)} pm`;
  if (meters < 1e-6) return `${(meters * 1e9).toFixed(1)} nm`;
  if (meters < 1e-3) return `${(meters * 1e6).toFixed(1)} μm`;
  if (meters < 1) return `${(meters * 1e3).toFixed(1)} mm`;
  if (meters < 1e3) return `${meters.toFixed(1)} m`;
  if (meters < 1e9) {
    const km = meters / 1e3;
    return `${km < 100 ? km.toFixed(1) : Math.round(km).toLocaleString()} km`;
  }
  if (meters < 1.496e11) return `${(meters / 1e9).toFixed(1)} million km`;
  if (meters < 9.461e15) return `${(meters / 1.496e11).toFixed(1)} AU`;
  if (meters < 9.461e21) return `${(meters / 9.461e15).toFixed(1)} ly`;
  return `${(meters / 9.461e21).toFixed(1)} million ly`;
}

function getRadiusComparison(meters: number): string {
  if (meters < 1e-15) return "smaller than a proton";
  if (meters < 1e-10) return "sub-atomic";
  if (meters < 1e-4) return "microscopic";
  if (meters < 0.01) return "marble-sized";
  if (meters < 0.15) return "grapefruit-sized";
  if (meters < 2) return "human-sized";
  if (meters < 30) return "building-sized";
  if (meters < 1e4) return "city-sized";
  if (meters < 6.371e6) return "planet-sized";
  if (meters < 6.957e8) return "star-sized";
  if (meters < 1.496e11) return "larger than the Sun";
  if (meters < 9.461e15) return "solar-system-scale";
  return "interstellar-scale";
}

// Radius of a sphere: r = (3M / (4πρ))^(1/3)
function sphereRadius(massKg: number, densityKgM3: number): number {
  return Math.cbrt((3 * massKg) / (4 * Math.PI * densityKgM3));
}

function getMassComparison(kg: number): { name: string; multiple: number } | null {
  for (let i = REFERENCE_MASSES.length - 1; i >= 0; i--) {
    if (kg >= REFERENCE_MASSES[i].kg) {
      return { name: REFERENCE_MASSES[i].name, multiple: kg / REFERENCE_MASSES[i].kg };
    }
  }
  return null;
}

function formatMultiple(n: number): string {
  if (n < 10) return n.toFixed(1);
  if (n < 1e4) return Math.round(n).toLocaleString();
  if (n < 1e6) return `${(n / 1e3).toFixed(0)} thousand`;
  if (n < 1e9) return `${(n / 1e6).toFixed(1)} million`;
  if (n < 1e12) return `${(n / 1e9).toFixed(1)} billion`;
  if (n < 1e15) return `${(n / 1e12).toFixed(1)} trillion`;
  return n.toExponential(1);
}

function formatEnergy(joules: number): string {
  if (!isFinite(joules) || isNaN(joules)) return "—";
  if (joules < 1e12) return `${(joules / 1e9).toFixed(1)} GJ`;
  if (joules < 1e15) return `${(joules / 1e12).toFixed(1)} TJ`;
  if (joules < 1e18) return `${(joules / 1e15).toFixed(1)} PJ`;
  if (joules < 1e21) return `${(joules / 1e18).toFixed(1)} EJ`;
  if (joules < 1e24) return `${(joules / 1e21).toFixed(1)} ZJ`;
  if (joules < 1e27) return `${(joules / 1e24).toFixed(1)} YJ`;
  return `${joules.toExponential(2)} J`;
}

function getEnergyComparison(joules: number): string | null {
  if (joules >= SUN_ANNUAL_ENERGY) {
    return `${formatMultiple(joules / SUN_ANNUAL_ENERGY)}× the Sun's annual output`;
  }
  if (joules >= GLOBAL_ANNUAL_ENERGY) {
    return `${formatMultiple(joules / GLOBAL_ANNUAL_ENERGY)}× global annual energy use`;
  }
  return null;
}

// --- Component ---

const CalcRelativisticRocket = () => {
  const [distanceInput, setDistanceInput] = useState<string>("11.91");
  const [selectedDestination, setSelectedDestination] = useState<string>("Tau Ceti");
  const [gInput, setGInput] = useState<string>("1.5");
  const [brachistochrone, setBrachistochrone] = useState<boolean>(true);
  const [showAllDestinations, setShowAllDestinations] = useState<boolean>(false);
  const [showAccelChart, setShowAccelChart] = useState<boolean>(false);
  const [showPHM, setShowPHM] = useState<boolean>(false);
  const [dryMassInput, setDryMassInput] = useState<string>("100000");
  const [efficiencyInput, setEfficiencyInput] = useState<string>("100");
  const [coastInput, setCoastInput] = useState<string>("0");

  const distanceLy = parseFloat(distanceInput) || 0;
  const accelerationG = parseFloat(gInput) || 0;
  const dryMassKg = parseFloat(dryMassInput) || 0;
  const efficiency = Math.max(0.01, Math.min(100, parseFloat(efficiencyInput) || 100)) / 100;
  const coastFraction = brachistochrone ? Math.max(0, Math.min(99, parseFloat(coastInput) || 0)) / 100 : 0;
  const isPHM = selectedDestination === "Tau Ceti" && Math.abs(accelerationG - 1.5) < 0.01 && brachistochrone;

  // Compute results reactively
  const results = useMemo(() => {
    if (distanceLy <= 0 || accelerationG <= 0) return null;
    return computeTravel(distanceLy, accelerationG, brachistochrone, coastFraction);
  }, [distanceLy, accelerationG, brachistochrone, coastFraction]);

  // Efficiency-adjusted mass ratios: R_adjusted = R^(1/η)
  const adjustedOneWay = results ? Math.pow(results.massRatioOneWay, 1 / efficiency) : 0;
  const adjustedBrach = results ? Math.pow(results.massRatioBrach, 1 / efficiency) : 0;

  // Compute all-destinations table
  const allDestinationResults = useMemo(() => {
    if (accelerationG <= 0) return [];
    return DESTINATIONS.map((dest) => ({
      ...dest,
      results: computeTravel(dest.distanceLy, accelerationG, brachistochrone, coastFraction),
    }));
  }, [accelerationG, brachistochrone, coastFraction]);

  // --- Acceleration Comparison Chart ---
  const [accelChartLinear, setAccelChartLinear] = useState<boolean>(true);
  const [clockRateChartLinear, setClockRateChartLinear] = useState<boolean>(true);
  const [dilationChartLogX, setDilationChartLogX] = useState<boolean>(false);
  const [dilationChartLogY, setDilationChartLogY] = useState<boolean>(true);
  const [dilationZoomLogX, setDilationZoomLogX] = useState<boolean>(false);
  const [dilationZoomLogY, setDilationZoomLogY] = useState<boolean>(true);
  const accelChartRef = useRef<HTMLCanvasElement>(null);
  const accelChartInstanceRef = useRef<Chart | null>(null);
  const clockRateChartRef = useRef<HTMLCanvasElement>(null);
  const clockRateChartInstanceRef = useRef<Chart | null>(null);
  const velocityChartRef = useRef<HTMLCanvasElement>(null);
  const velocityChartInstanceRef = useRef<Chart | null>(null);
  const rapidityShipTimeChartRef = useRef<HTMLCanvasElement>(null);
  const rapidityShipTimeChartInstanceRef = useRef<Chart | null>(null);
  const rapidityAccelTimeChartRef = useRef<HTMLCanvasElement>(null);
  const rapidityAccelTimeChartInstanceRef = useRef<Chart | null>(null);
  const rapidityDistanceChartRef = useRef<HTMLCanvasElement>(null);
  const rapidityDistanceChartInstanceRef = useRef<Chart | null>(null);
  const dilationChartRef = useRef<HTMLCanvasElement>(null);
  const dilationChartInstanceRef = useRef<Chart | null>(null);
  const dilationZoomChartRef = useRef<HTMLCanvasElement>(null);
  const dilationZoomChartInstanceRef = useRef<Chart | null>(null);

  const COMPARISON_GS = [0.1, 0.3, 1, 1.5, 10];
  const COMPARISON_COLORS: Record<number, string> = {
    0.1: "#71717a", // zinc-500
    0.3: "#a1a1aa", // zinc-400
    1: "#488193", // muted blue
    1.5: "#49b7d8", // cyan
    10: "#e07753", // orange
  };

  const getComparisonDistances = (linear: boolean) => {
    const distances: number[] = [];
    if (linear) {
      const maxDist = distanceLy > 0 ? distanceLy * 3 : 100;
      const step = maxDist / 200;
      distances.push(0);
      for (let d = step; d <= maxDist; d += step) {
        distances.push(d);
      }
    } else {
      for (let exp = -2; exp <= 14; exp += 0.1) {
        distances.push(Math.pow(10, exp));
      }
    }
    return distances;
  };

  const getComparisonGValues = () => {
    const gValues = [...COMPARISON_GS];
    const userGIsPreset = COMPARISON_GS.some((g) => Math.abs(g - accelerationG) < 0.01);
    if (!userGIsPreset && accelerationG > 0) {
      gValues.push(accelerationG);
      gValues.sort((a, b) => a - b);
    }
    return gValues;
  };

  const fmtDistTick = (v: number) => {
    if (v < 1000) return `${v % 1 === 0 ? v : v.toPrecision(3)}`;
    if (v < 1e6) return `${(v / 1e3).toFixed(0)}k`;
    if (v < 1e9) return `${(v / 1e6).toFixed(0)}M`;
    if (v < 1e12) return `${(v / 1e9).toFixed(0)}B`;
    return `${(v / 1e12).toFixed(0)}T`;
  };

  const fmtTimeTick = (v: number) => {
    if (v < 1) return `${(v * 365.25).toFixed(0)}d`;
    if (v < 1000) return `${v % 1 === 0 ? v : v.toFixed(1)} yr`;
    if (v < 1e6) return `${(v / 1e3).toFixed(0)}k yr`;
    if (v < 1e9) return `${(v / 1e6).toFixed(0)}M yr`;
    return `${(v / 1e9).toFixed(0)}B yr`;
  };

  const fmtClockRateTick = (v: number) => {
    if (v >= 0.1) return `${(v * 100).toFixed(0)}%`;
    if (v >= 0.01) return `${(v * 100).toFixed(1)}%`;
    if (v >= 0.001) return `${(v * 100).toFixed(2)}%`;
    return `${v.toExponential(0)}`;
  };

  const fmtVelocityPercentTick = (v: number) => {
    if (v >= 1) return "c";
    if (v >= 0.9999) return `${(v * 100).toFixed(3)}%`;
    if (v >= 0.99) return `${(v * 100).toFixed(2)}%`;
    return `${(v * 100).toFixed(0)}%`;
  };

  const filterLogTicks = (axis: { ticks: { value: number }[] }) => {
    axis.ticks = axis.ticks.filter((t) => {
      const log = Math.log10(t.value);
      return Math.abs(log - Math.round(log)) < 0.01;
    });
  };

  const transformSpeedDatasetsForLogX = (datasets: ChartConfiguration["data"]["datasets"]) =>
    datasets.map((dataset) => ({
      ...dataset,
      data: Array.isArray(dataset.data)
        ? dataset.data
            .map((point) => {
              if (typeof point !== "object" || point === null || !("x" in point) || !("y" in point)) return null;
              const x = point.x as number;
              const y = point.y as number;
              return { x: Math.max(1 - x, 1e-16), y };
            })
            .filter((point): point is { x: number; y: number } => point !== null)
        : dataset.data,
    }));

  const getLogXMin = (datasets: ChartConfiguration["data"]["datasets"], visibleMax: number) => {
    let minGap = Number.POSITIVE_INFINITY;

    for (const dataset of datasets) {
      if (!Array.isArray(dataset.data)) continue;
      for (const point of dataset.data) {
        if (typeof point !== "object" || point === null || !("x" in point)) continue;
        const x = point.x as number;
        if (x > 0 && x <= visibleMax) {
          minGap = Math.min(minGap, x);
        }
      }
    }

    return Number.isFinite(minGap) ? Math.pow(10, Math.floor(Math.log10(minGap))) : 1e-12;
  };

  const buildAccelChart = useCallback(() => {
    if (!accelChartRef.current || !showAccelChart) return;

    if (accelChartInstanceRef.current) {
      accelChartInstanceRef.current.destroy();
    }

    const ctx = accelChartRef.current.getContext("2d");
    if (!ctx) return;

    const distances = getComparisonDistances(accelChartLinear);
    const gValues = getComparisonGValues();

    // Pre-compute travel results for all g-values and distances
    const travelData = gValues.map((g) => distances.map((d) => computeTravel(d, g, brachistochrone, coastFraction)));

    const datasets = gValues.map((g, gi) => {
      const isUserG = Math.abs(g - accelerationG) < 0.01;
      const isCustom = !COMPARISON_GS.some((pg) => Math.abs(pg - g) < 0.01);
      const color = isCustom ? "#7dd3fc" : COMPARISON_COLORS[g];
      return {
        label: `${g}g${isUserG ? " (selected)" : ""}`,
        data: travelData[gi].map((r) => r.shipTimeYears),
        borderColor: color,
        backgroundColor: "transparent",
        borderWidth: isUserG ? 3 : 1.5,
        borderDash: isUserG ? [] : [4, 2],
        pointRadius: 0,
        pointHitRadius: 8,
        fill: false,
        tension: 0.3,
      };
    });

    const config: ChartConfiguration = {
      type: "line",
      data: {
        labels: distances.map((d) => d.toString()),
        datasets,
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        animation: { duration: 600, easing: "easeOutQuart" },
        interaction: { mode: "index", intersect: false },
        plugins: {
          legend: {
            labels: { color: "#a1a1aa", font: { size: 12 }, usePointStyle: true, pointStyle: "line" },
          },
          tooltip: {
            backgroundColor: "#27272a",
            titleColor: "#fafafa",
            bodyColor: "#d4d4d8",
            borderColor: "#52525b",
            borderWidth: 1,
            callbacks: {
              title: (items) => {
                const d = parseFloat(items[0].label);
                return `Distance: ${formatDistance(d)}`;
              },
              label: (item) => {
                const y = item.parsed.y as number;
                const result = travelData[item.datasetIndex]?.[item.dataIndex];
                if (!result) return ` ${item.dataset.label ?? ""}`;

                const velStr = ` (${formatVelocity(result.peakVelocityFractionC, result.peakGamma)})`;
                return ` ${item.dataset.label ?? ""}: ${formatTime(y)}${velStr}`;
              },
            },
          },
        },
        scales: {
          x: {
            type: accelChartLinear ? "linear" : "logarithmic",
            ...(accelChartLinear ? { min: 0 } : {}),
            title: { display: true, text: "Distance (light-years)", color: "#d4d4d8", font: { size: 13 } },
            afterBuildTicks: accelChartLinear
              ? undefined
              : (axis: { ticks: { value: number }[] }) => {
                  axis.ticks = axis.ticks.filter((t) => {
                    const log = Math.log10(t.value);
                    return Math.abs(log - Math.round(log)) < 0.01;
                  });
                },
            ticks: {
              color: "#a1a1aa",
              font: { size: 11 },
              callback: function (value) {
                return fmtDistTick(value as number);
              },
            },
            grid: { color: "#3f3f46", lineWidth: 0.5 },
          },
          y: {
            type: accelChartLinear ? "linear" : "logarithmic",
            beginAtZero: accelChartLinear,
            title: {
              display: true,
              text: "Ship Time (years)",
              color: "#d4d4d8",
              font: { size: 13 },
            },
            afterBuildTicks: accelChartLinear ? undefined : filterLogTicks,
            ticks: {
              color: "#a1a1aa",
              font: { size: 11 },
              callback: function (value) {
                return fmtTimeTick(value as number);
              },
            },
            grid: { color: "#3f3f46", lineWidth: 0.5 },
          },
        },
      },
      plugins: [
        {
          id: "accelCurrentDist",
          afterDraw: (chart) => {
            if (distanceLy <= 0) return;
            const xScale = chart.scales.x;
            const xPixel = xScale.getPixelForValue(distanceLy);
            // Don't draw if outside visible range
            if (xPixel < xScale.left || xPixel > xScale.right) return;
            const drawCtx = chart.ctx;

            drawCtx.save();
            drawCtx.strokeStyle = "#a1a1aa";
            drawCtx.lineWidth = 1;
            drawCtx.setLineDash([4, 4]);
            drawCtx.beginPath();
            drawCtx.moveTo(xPixel, chart.scales.y.top);
            drawCtx.lineTo(xPixel, chart.scales.y.bottom);
            drawCtx.stroke();
            drawCtx.restore();
          },
        },
      ],
    };

    accelChartInstanceRef.current = new Chart(ctx, config);
  }, [accelerationG, brachistochrone, coastFraction, distanceLy, showAccelChart, accelChartLinear]);

  const buildClockRateChart = useCallback(() => {
    if (!clockRateChartRef.current || !showAccelChart) return;

    if (clockRateChartInstanceRef.current) {
      clockRateChartInstanceRef.current.destroy();
    }

    const ctx = clockRateChartRef.current.getContext("2d");
    if (!ctx) return;

    const distances = getComparisonDistances(clockRateChartLinear);
    const gValues = getComparisonGValues();
    const travelData = gValues.map((g) => distances.map((d) => computeTravel(d, g, brachistochrone, coastFraction)));
    const minClockRate = Math.min(...travelData.flatMap((series) => series.map((r) => 1 / r.peakGamma)));

    const datasets = gValues.map((g, gi) => {
      const isUserG = Math.abs(g - accelerationG) < 0.01;
      const isCustom = !COMPARISON_GS.some((pg) => Math.abs(pg - g) < 0.01);
      const color = isCustom ? "#7dd3fc" : COMPARISON_COLORS[g];
      return {
        label: `${g}g${isUserG ? " (selected)" : ""}`,
        data: travelData[gi].map((r) => 1 / r.peakGamma),
        borderColor: color,
        backgroundColor: "transparent",
        borderWidth: isUserG ? 3 : 1.5,
        borderDash: isUserG ? [] : [4, 2],
        pointRadius: 0,
        pointHitRadius: 8,
        fill: false,
        tension: 0.3,
      };
    });

    const config: ChartConfiguration = {
      type: "line",
      data: {
        labels: distances.map((d) => d.toString()),
        datasets,
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        animation: { duration: 600, easing: "easeOutQuart" },
        interaction: { mode: "index", intersect: false },
        plugins: {
          legend: {
            labels: { color: "#a1a1aa", font: { size: 12 }, usePointStyle: true, pointStyle: "line" },
          },
          tooltip: {
            backgroundColor: "#27272a",
            titleColor: "#fafafa",
            bodyColor: "#d4d4d8",
            borderColor: "#52525b",
            borderWidth: 1,
            callbacks: {
              title: (items) => {
                const d = parseFloat(items[0].label);
                return `Distance: ${formatDistance(d)}`;
              },
              label: (item) => {
                const result = travelData[item.datasetIndex]?.[item.dataIndex];
                if (!result) return ` ${item.dataset.label ?? ""}`;
                const clockRate = 1 / result.peakGamma;
                const tripRatio =
                  result.timeDilationRatio < 1000
                    ? `${result.timeDilationRatio.toFixed(2)}x`
                    : result.timeDilationRatio.toExponential(2);
                return ` ${item.dataset.label ?? ""}: ship clock ${fmtClockRateTick(clockRate)} of Earth at peak (γ=${formatGamma(result.peakGamma)}, trip avg ${tripRatio})`;
              },
            },
          },
        },
        scales: {
          x: {
            type: clockRateChartLinear ? "linear" : "logarithmic",
            ...(clockRateChartLinear ? { min: 0 } : {}),
            title: { display: true, text: "Distance (light-years)", color: "#d4d4d8", font: { size: 13 } },
            afterBuildTicks: clockRateChartLinear ? undefined : filterLogTicks,
            ticks: {
              color: "#a1a1aa",
              font: { size: 11 },
              callback: function (value) {
                return fmtDistTick(value as number);
              },
            },
            grid: { color: "#3f3f46", lineWidth: 0.5 },
          },
          y: {
            type: clockRateChartLinear ? "linear" : "logarithmic",
            ...(clockRateChartLinear ? { min: 0, max: 1 } : { min: minClockRate, max: 1 }),
            title: {
              display: true,
              text: "Ship Clock Rate (1/γ)",
              color: "#d4d4d8",
              font: { size: 13 },
            },
            afterBuildTicks: clockRateChartLinear ? undefined : filterLogTicks,
            ticks: {
              color: "#a1a1aa",
              font: { size: 11 },
              callback: function (value) {
                return fmtClockRateTick(value as number);
              },
            },
            grid: { color: "#3f3f46", lineWidth: 0.5 },
          },
        },
      },
      plugins: [
        {
          id: "clockRateCurrentDist",
          afterDraw: (chart) => {
            if (distanceLy <= 0) return;
            const xScale = chart.scales.x;
            const xPixel = xScale.getPixelForValue(distanceLy);
            if (xPixel < xScale.left || xPixel > xScale.right) return;
            const drawCtx = chart.ctx;

            drawCtx.save();
            drawCtx.strokeStyle = "#a1a1aa";
            drawCtx.lineWidth = 1;
            drawCtx.setLineDash([4, 4]);
            drawCtx.beginPath();
            drawCtx.moveTo(xPixel, chart.scales.y.top);
            drawCtx.lineTo(xPixel, chart.scales.y.bottom);
            drawCtx.stroke();
            drawCtx.restore();
          },
        },
      ],
    };

    clockRateChartInstanceRef.current = new Chart(ctx, config);
  }, [accelerationG, brachistochrone, coastFraction, distanceLy, showAccelChart, clockRateChartLinear]);

  // --- Velocity vs Distance Chart ---
  const buildVelocityChart = useCallback(() => {
    if (!velocityChartRef.current || !showAccelChart) return;

    if (velocityChartInstanceRef.current) {
      velocityChartInstanceRef.current.destroy();
    }

    const ctx = velocityChartRef.current.getContext("2d");
    if (!ctx) return;

    const distances: number[] = [];
    if (accelChartLinear) {
      const maxDist = distanceLy > 0 ? distanceLy * 3 : 100;
      const step = maxDist / 200;
      distances.push(0);
      for (let d = step; d <= maxDist; d += step) {
        distances.push(d);
      }
    } else {
      for (let exp = -2; exp <= 14; exp += 0.1) {
        distances.push(Math.pow(10, exp));
      }
    }

    const gValues = [...COMPARISON_GS];
    const userGIsPreset = COMPARISON_GS.some((g) => Math.abs(g - accelerationG) < 0.01);
    if (!userGIsPreset && accelerationG > 0) {
      gValues.push(accelerationG);
      gValues.sort((a, b) => a - b);
    }

    const fmtDistTick = (v: number) => {
      if (v < 1000) return `${v % 1 === 0 ? v : v.toPrecision(3)}`;
      if (v < 1e6) return `${(v / 1e3).toFixed(0)}k`;
      if (v < 1e9) return `${(v / 1e6).toFixed(0)}M`;
      if (v < 1e12) return `${(v / 1e9).toFixed(0)}B`;
      return `${(v / 1e12).toFixed(0)}T`;
    };

    const datasets = gValues.map((g) => {
      const isUserG = Math.abs(g - accelerationG) < 0.01;
      const isCustom = !COMPARISON_GS.some((pg) => Math.abs(pg - g) < 0.01);
      const color = isCustom ? "#7dd3fc" : COMPARISON_COLORS[g];
      return {
        label: `${g}g${isUserG ? " (selected)" : ""}`,
        data: distances.map((d) => computeTravel(d, g, brachistochrone, coastFraction).peakVelocityFractionC),
        borderColor: color,
        backgroundColor: "transparent",
        borderWidth: isUserG ? 3 : 1.5,
        borderDash: isUserG ? [] : [4, 2],
        pointRadius: 0,
        pointHitRadius: 8,
        fill: false,
        tension: 0.3,
      };
    });

    const velConfig: ChartConfiguration = {
      type: "line",
      data: {
        labels: distances.map((d) => d.toString()),
        datasets,
      },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        animation: { duration: 600, easing: "easeOutQuart" },
        interaction: { mode: "index", intersect: false },
        plugins: {
          legend: {
            labels: { color: "#a1a1aa", font: { size: 12 }, usePointStyle: true, pointStyle: "line" },
          },
          tooltip: {
            backgroundColor: "#27272a",
            titleColor: "#fafafa",
            bodyColor: "#d4d4d8",
            borderColor: "#52525b",
            borderWidth: 1,
            callbacks: {
              title: (items) => {
                const d = parseFloat(items[0].label);
                return `Distance: ${formatDistance(d)}`;
              },
              label: (item) => {
                const v = item.parsed.y as number;
                return ` ${item.dataset.label ?? ""}: ${formatVelocity(v)}`;
              },
            },
          },
        },
        scales: {
          x: {
            type: accelChartLinear ? "linear" : "logarithmic",
            ...(accelChartLinear ? { min: 0 } : {}),
            title: { display: true, text: "Distance (light-years)", color: "#d4d4d8", font: { size: 13 } },
            afterBuildTicks: accelChartLinear
              ? undefined
              : (axis: { ticks: { value: number }[] }) => {
                  axis.ticks = axis.ticks.filter((t) => {
                    const log = Math.log10(t.value);
                    return Math.abs(log - Math.round(log)) < 0.01;
                  });
                },
            ticks: {
              color: "#a1a1aa",
              font: { size: 11 },
              callback: function (value) {
                return fmtDistTick(value as number);
              },
            },
            grid: { color: "#3f3f46", lineWidth: 0.5 },
          },
          y: {
            type: "linear",
            min: 0,
            max: 1.05,
            title: { display: true, text: "Peak Velocity (v/c)", color: "#d4d4d8", font: { size: 13 } },
            ticks: {
              color: "#a1a1aa",
              font: { size: 11 },
              stepSize: 0.1,
              callback: function (value) {
                const v = value as number;
                if (v === 1) return "c";
                return `${(v * 100).toFixed(0)}%`;
              },
            },
            grid: { color: "#3f3f46", lineWidth: 0.5 },
          },
        },
      },
      plugins: [
        {
          id: "speedOfLight",
          afterDraw: (chart) => {
            const yScale = chart.scales.y;
            const yPixel = yScale.getPixelForValue(1.0);
            const drawCtx = chart.ctx;
            drawCtx.save();
            drawCtx.strokeStyle = "#ef4444";
            drawCtx.lineWidth = 1;
            drawCtx.setLineDash([6, 3]);
            drawCtx.beginPath();
            drawCtx.moveTo(chart.scales.x.left, yPixel);
            drawCtx.lineTo(chart.scales.x.right, yPixel);
            drawCtx.stroke();
            drawCtx.fillStyle = "#ef4444";
            drawCtx.font = "11px sans-serif";
            drawCtx.fillText("c", chart.scales.x.right + 4, yPixel + 4);
            drawCtx.restore();
          },
        },
        {
          id: "velCurrentDist",
          afterDraw: (chart) => {
            if (distanceLy <= 0) return;
            const xScale = chart.scales.x;
            const xPixel = xScale.getPixelForValue(distanceLy);
            if (xPixel < xScale.left || xPixel > xScale.right) return;
            const drawCtx = chart.ctx;
            drawCtx.save();
            drawCtx.strokeStyle = "#a1a1aa";
            drawCtx.lineWidth = 1;
            drawCtx.setLineDash([4, 4]);
            drawCtx.beginPath();
            drawCtx.moveTo(xPixel, chart.scales.y.top);
            drawCtx.lineTo(xPixel, chart.scales.y.bottom);
            drawCtx.stroke();
            drawCtx.restore();
          },
        },
      ],
    };

    velocityChartInstanceRef.current = new Chart(ctx, velConfig);
  }, [accelerationG, brachistochrone, coastFraction, distanceLy, showAccelChart, accelChartLinear]);

  const getSpeedClockRateChartData = useCallback(() => {
    const velocities: number[] = [];
    const clockRateData: number[] = [];
    for (let v = 0; v <= 0.9; v += 0.02) {
      velocities.push(v);
      clockRateData.push(Math.sqrt(1 - v * v));
    }

    const gammaMax = 1e5;
    for (let logG = Math.log10(2); logG <= Math.log10(gammaMax); logG += 0.02) {
      const g = Math.pow(10, logG);
      const v = Math.sqrt(1 - 1 / (g * g));
      velocities.push(v);
      clockRateData.push(1 / g);
    }

    const gValues = [...COMPARISON_GS];
    const userGIsPreset = COMPARISON_GS.some((g) => Math.abs(g - accelerationG) < 0.01);
    if (!userGIsPreset && accelerationG > 0) {
      gValues.push(accelerationG);
      gValues.sort((a, b) => a - b);
    }

    const markerPoints =
      distanceLy > 0
        ? gValues.map((g) => {
            const r = computeTravel(distanceLy, g, brachistochrone, coastFraction);
            const isUserG = Math.abs(g - accelerationG) < 0.01;
            const isCustom = !COMPARISON_GS.some((pg) => Math.abs(pg - g) < 0.01);
            const color = isCustom ? "#7dd3fc" : COMPARISON_COLORS[g];
            return { g, v: r.peakVelocityFractionC, gamma: r.peakGamma, clockRate: 1 / r.peakGamma, color, isUserG };
          })
        : [];

    const datasets: ChartConfiguration["data"]["datasets"] = [
      {
        label: "Ship clock rate (1/γ)",
        data: velocities.map((v, i) => ({ x: v, y: clockRateData[i] })),
        borderColor: "#49b7d8",
        backgroundColor: "transparent",
        borderWidth: 2,
        pointRadius: 0,
        pointHitRadius: 8,
        fill: false,
        tension: 0.3,
      },
      ...markerPoints.map((mp) => ({
        label: `${mp.g}g${mp.isUserG ? " (selected)" : ""} — ${fmtClockRateTick(mp.clockRate)}`,
        data: [{ x: mp.v, y: mp.clockRate }],
        borderColor: mp.color,
        backgroundColor: mp.color,
        borderWidth: mp.isUserG ? 3 : 2,
        pointRadius: mp.isUserG ? 7 : 5,
        pointStyle: "circle" as const,
        showLine: false,
      })),
    ];

    const minClockRateInData = Math.min(...clockRateData, ...markerPoints.map((mp) => mp.clockRate));
    const yMin = Math.pow(10, Math.floor(Math.log10(minClockRateInData)));

    return { datasets, yMin };
  }, [accelerationG, brachistochrone, coastFraction, distanceLy]);

  const getRapidityComparisonData = useCallback(() => {
    const maxDistanceLy = Math.max(...DESTINATIONS.map((dest) => dest.distanceLy));
    const maxExp = Math.ceil(Math.log10(maxDistanceLy));
    const distances: number[] = [];
    for (let exp = -2; exp <= maxExp; exp += 0.1) {
      distances.push(Math.pow(10, exp));
    }

    const gValues = getComparisonGValues();
    const travelData = gValues.map((g) => {
      const a = g * G_TO_LY_YR2;
      return distances.map((distance) => {
        const result = computeTravel(distance, g, brachistochrone, coastFraction);
        const rapidity = Math.acosh(result.peakGamma);
        return {
          distance,
          shipTimeYears: result.shipTimeYears,
          accelProperTimeYears: rapidity / a,
          rapidity,
        };
      });
    });

    const yMax = Math.ceil(Math.max(...travelData.flatMap((series) => series.map((point) => point.rapidity))));
    return { distances, gValues, travelData, yMax: Math.max(1, yMax) };
  }, [accelerationG, brachistochrone, coastFraction]);

  const buildRapidityShipTimeChart = useCallback(() => {
    if (!rapidityShipTimeChartRef.current || !showAccelChart) return;

    if (rapidityShipTimeChartInstanceRef.current) {
      rapidityShipTimeChartInstanceRef.current.destroy();
    }

    const ctx = rapidityShipTimeChartRef.current.getContext("2d");
    if (!ctx) return;

    const { gValues, travelData, yMax } = getRapidityComparisonData();
    const datasets = gValues.map((g, gi) => {
      const isUserG = Math.abs(g - accelerationG) < 0.01;
      const isCustom = !COMPARISON_GS.some((pg) => Math.abs(pg - g) < 0.01);
      const color = isCustom ? "#7dd3fc" : COMPARISON_COLORS[g];
      return {
        label: `${g}g${isUserG ? " (selected)" : ""}`,
        data: travelData[gi].map((point) => ({ x: point.shipTimeYears, y: point.rapidity })),
        borderColor: color,
        backgroundColor: "transparent",
        borderWidth: isUserG ? 3 : 1.5,
        borderDash: isUserG ? [] : [4, 2],
        pointRadius: 0,
        pointHitRadius: 8,
        fill: false,
        tension: 0.3,
      };
    });

    const config: ChartConfiguration = {
      type: "line",
      data: { datasets },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        animation: { duration: 600, easing: "easeOutQuart" },
        interaction: { mode: "nearest", intersect: false },
        plugins: {
          legend: {
            labels: { color: "#a1a1aa", font: { size: 12 }, usePointStyle: true, pointStyle: "line" },
          },
          tooltip: {
            backgroundColor: "#27272a",
            titleColor: "#fafafa",
            bodyColor: "#d4d4d8",
            borderColor: "#52525b",
            borderWidth: 1,
            callbacks: {
              title: (items) => `Ship time: ${formatTime(items[0].parsed.x as number)}`,
              label: (item) => ` ${item.dataset.label ?? ""}: φ = ${formatRapidity(item.parsed.y as number)}`,
            },
          },
        },
        scales: {
          x: {
            type: "logarithmic",
            title: { display: true, text: "Ship Time (years)", color: "#d4d4d8", font: { size: 13 } },
            afterBuildTicks: filterLogTicks,
            ticks: {
              color: "#a1a1aa",
              font: { size: 11 },
              callback: function (value) {
                return fmtTimeTick(value as number);
              },
            },
            grid: { color: "#3f3f46", lineWidth: 0.5 },
          },
          y: {
            type: "linear",
            min: 0,
            max: yMax,
            title: { display: true, text: "Rapidity (φ)", color: "#d4d4d8", font: { size: 13 } },
            ticks: {
              color: "#a1a1aa",
              font: { size: 11 },
              callback: function (value) {
                return formatRapidity(value as number);
              },
            },
            grid: { color: "#3f3f46", lineWidth: 0.5 },
          },
        },
      },
    };

    rapidityShipTimeChartInstanceRef.current = new Chart(ctx, config);
  }, [accelerationG, getRapidityComparisonData, showAccelChart]);

  const buildRapidityAccelTimeChart = useCallback(() => {
    if (!rapidityAccelTimeChartRef.current || !showAccelChart) return;

    if (rapidityAccelTimeChartInstanceRef.current) {
      rapidityAccelTimeChartInstanceRef.current.destroy();
    }

    const ctx = rapidityAccelTimeChartRef.current.getContext("2d");
    if (!ctx) return;

    const { gValues, travelData, yMax } = getRapidityComparisonData();
    const xMax = Math.max(...travelData.flatMap((series) => series.map((point) => point.accelProperTimeYears)));
    const datasets = gValues.map((g, gi) => {
      const isUserG = Math.abs(g - accelerationG) < 0.01;
      const isCustom = !COMPARISON_GS.some((pg) => Math.abs(pg - g) < 0.01);
      const color = isCustom ? "#7dd3fc" : COMPARISON_COLORS[g];
      return {
        label: `${g}g${isUserG ? " (selected)" : ""}`,
        data: travelData[gi].map((point) => ({ x: point.accelProperTimeYears, y: point.rapidity })),
        borderColor: color,
        backgroundColor: "transparent",
        borderWidth: isUserG ? 3 : 1.5,
        borderDash: isUserG ? [] : [4, 2],
        pointRadius: 0,
        pointHitRadius: 8,
        fill: false,
        tension: 0.3,
      };
    });

    const config: ChartConfiguration = {
      type: "line",
      data: { datasets },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        animation: { duration: 600, easing: "easeOutQuart" },
        interaction: { mode: "nearest", intersect: false },
        plugins: {
          legend: {
            labels: { color: "#a1a1aa", font: { size: 12 }, usePointStyle: true, pointStyle: "line" },
          },
          tooltip: {
            backgroundColor: "#27272a",
            titleColor: "#fafafa",
            bodyColor: "#d4d4d8",
            borderColor: "#52525b",
            borderWidth: 1,
            callbacks: {
              title: (items) => `Powered ship time: ${formatTime(items[0].parsed.x as number)}`,
              label: (item) => ` ${item.dataset.label ?? ""}: φ = ${formatRapidity(item.parsed.y as number)}`,
            },
          },
        },
        scales: {
          x: {
            type: "linear",
            min: 0,
            max: xMax,
            title: { display: true, text: "Powered Ship Time (years)", color: "#d4d4d8", font: { size: 13 } },
            ticks: {
              color: "#a1a1aa",
              font: { size: 11 },
              callback: function (value) {
                return fmtTimeTick(value as number);
              },
            },
            grid: { color: "#3f3f46", lineWidth: 0.5 },
          },
          y: {
            type: "linear",
            min: 0,
            max: yMax,
            title: { display: true, text: "Rapidity (φ)", color: "#d4d4d8", font: { size: 13 } },
            ticks: {
              color: "#a1a1aa",
              font: { size: 11 },
              callback: function (value) {
                return formatRapidity(value as number);
              },
            },
            grid: { color: "#3f3f46", lineWidth: 0.5 },
          },
        },
      },
    };

    rapidityAccelTimeChartInstanceRef.current = new Chart(ctx, config);
  }, [accelerationG, getRapidityComparisonData, showAccelChart]);

  const buildRapidityDistanceChart = useCallback(() => {
    if (!rapidityDistanceChartRef.current || !showAccelChart) return;

    if (rapidityDistanceChartInstanceRef.current) {
      rapidityDistanceChartInstanceRef.current.destroy();
    }

    const ctx = rapidityDistanceChartRef.current.getContext("2d");
    if (!ctx) return;

    const { gValues, travelData, yMax } = getRapidityComparisonData();
    const datasets = gValues.map((g, gi) => {
      const isUserG = Math.abs(g - accelerationG) < 0.01;
      const isCustom = !COMPARISON_GS.some((pg) => Math.abs(pg - g) < 0.01);
      const color = isCustom ? "#7dd3fc" : COMPARISON_COLORS[g];
      return {
        label: `${g}g${isUserG ? " (selected)" : ""}`,
        data: travelData[gi].map((point) => ({ x: point.distance, y: point.rapidity })),
        borderColor: color,
        backgroundColor: "transparent",
        borderWidth: isUserG ? 3 : 1.5,
        borderDash: isUserG ? [] : [4, 2],
        pointRadius: 0,
        pointHitRadius: 8,
        fill: false,
        tension: 0.3,
      };
    });

    const config: ChartConfiguration = {
      type: "line",
      data: { datasets },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        animation: { duration: 600, easing: "easeOutQuart" },
        interaction: { mode: "nearest", intersect: false },
        plugins: {
          legend: {
            labels: { color: "#a1a1aa", font: { size: 12 }, usePointStyle: true, pointStyle: "line" },
          },
          tooltip: {
            backgroundColor: "#27272a",
            titleColor: "#fafafa",
            bodyColor: "#d4d4d8",
            borderColor: "#52525b",
            borderWidth: 1,
            callbacks: {
              title: (items) => `Distance: ${formatDistance(items[0].parsed.x as number)}`,
              label: (item) => ` ${item.dataset.label ?? ""}: φ = ${formatRapidity(item.parsed.y as number)}`,
            },
          },
        },
        scales: {
          x: {
            type: "logarithmic",
            title: { display: true, text: "Distance (light-years)", color: "#d4d4d8", font: { size: 13 } },
            afterBuildTicks: filterLogTicks,
            ticks: {
              color: "#a1a1aa",
              font: { size: 11 },
              callback: function (value) {
                return fmtDistTick(value as number);
              },
            },
            grid: { color: "#3f3f46", lineWidth: 0.5 },
          },
          y: {
            type: "linear",
            min: 0,
            max: yMax,
            title: { display: true, text: "Rapidity (φ)", color: "#d4d4d8", font: { size: 13 } },
            ticks: {
              color: "#a1a1aa",
              font: { size: 11 },
              callback: function (value) {
                return formatRapidity(value as number);
              },
            },
            grid: { color: "#3f3f46", lineWidth: 0.5 },
          },
        },
      },
    };

    rapidityDistanceChartInstanceRef.current = new Chart(ctx, config);
  }, [accelerationG, getRapidityComparisonData, showAccelChart]);

  // --- Time Dilation vs Speed Chart ---
  const buildDilationChart = useCallback(() => {
    if (!dilationChartRef.current || !showAccelChart) return;

    if (dilationChartInstanceRef.current) {
      dilationChartInstanceRef.current.destroy();
    }

    const ctx = dilationChartRef.current.getContext("2d");
    if (!ctx) return;
    const { datasets, yMin } = getSpeedClockRateChartData();
    const xDatasets = dilationChartLogX ? transformSpeedDatasetsForLogX(datasets) : datasets;
    const xMin = dilationChartLogX ? getLogXMin(xDatasets, 1) : undefined;

    const dilConfig: ChartConfiguration = {
      type: "line",
      data: { datasets: xDatasets },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        animation: { duration: 600, easing: "easeOutQuart" },
        interaction: { mode: "nearest", intersect: false },
        plugins: {
          legend: {
            labels: { color: "#a1a1aa", font: { size: 12 }, usePointStyle: true },
          },
          tooltip: {
            backgroundColor: "#27272a",
            titleColor: "#fafafa",
            bodyColor: "#d4d4d8",
            borderColor: "#52525b",
            borderWidth: 1,
            callbacks: {
              title: (items) => {
                const rawX = items[0].parsed.x as number;
                const v = dilationChartLogX ? 1 - rawX : rawX;
                return `Velocity: ${formatVelocity(v)}`;
              },
              label: (item) => {
                const clockRate = item.parsed.y as number;
                const gamma = 1 / clockRate;
                return ` ship clock = ${fmtClockRateTick(clockRate)} of Earth (γ=${formatGamma(gamma)})`;
              },
            },
          },
        },
        scales: {
          x: {
            type: dilationChartLogX ? "logarithmic" : "linear",
            ...(dilationChartLogX ? { min: xMin, max: 1, reverse: true } : { min: 0, max: 1 }),
            title: {
              display: true,
              text: dilationChartLogX ? "Distance from c (1 - v/c), log scale" : "Velocity (v/c)",
              color: "#d4d4d8",
              font: { size: 13 },
            },
            afterBuildTicks: dilationChartLogX ? filterLogTicks : undefined,
            ticks: {
              color: "#a1a1aa",
              font: { size: 11 },
              callback: function (value) {
                const rawX = value as number;
                const v = dilationChartLogX ? 1 - rawX : rawX;
                return fmtVelocityPercentTick(v);
              },
            },
            grid: { color: "#3f3f46", lineWidth: 0.5 },
          },
          y: {
            type: dilationChartLogY ? "logarithmic" : "linear",
            ...(dilationChartLogY ? { min: yMin, max: 1 } : { min: 0, max: 1 }),
            position: "left",
            title: { display: true, text: "Ship Clock Rate (1/γ)", color: "#d4d4d8", font: { size: 13 } },
            afterBuildTicks: dilationChartLogY ? filterLogTicks : undefined,
            ticks: {
              color: "#a1a1aa",
              font: { size: 11 },
              callback: function (value) {
                return fmtClockRateTick(value as number);
              },
            },
            grid: { color: "#3f3f46", lineWidth: 0.5 },
          },
          yRight: {
            type: dilationChartLogY ? "logarithmic" : "linear",
            ...(dilationChartLogY ? { min: yMin, max: 1 } : { min: 0, max: 1 }),
            position: "right",
            title: { display: true, text: "Ship hours per Earth day", color: "#d4d4d8", font: { size: 13 } },
            afterBuildTicks: dilationChartLogY ? filterLogTicks : undefined,
            ticks: {
              color: "#a1a1aa",
              font: { size: 11 },
              callback: function (value) {
                const clockRate = value as number;
                const hoursPerDay = 24 * clockRate;
                if (hoursPerDay >= 1) return `${hoursPerDay.toFixed(hoursPerDay < 10 ? 1 : 0)} h`;
                if (hoursPerDay >= 1 / 60) return `${(hoursPerDay * 60).toFixed(0)} m`;
                return `${(hoursPerDay * 3600).toFixed(0)} s`;
              },
            },
            grid: { drawOnChartArea: false },
          },
        },
      },
    };

    dilationChartInstanceRef.current = new Chart(ctx, dilConfig);
  }, [dilationChartLogX, dilationChartLogY, getSpeedClockRateChartData, showAccelChart]);

  const buildDilationZoomChart = useCallback(() => {
    if (!dilationZoomChartRef.current || !showAccelChart) return;

    if (dilationZoomChartInstanceRef.current) {
      dilationZoomChartInstanceRef.current.destroy();
    }

    const ctx = dilationZoomChartRef.current.getContext("2d");
    if (!ctx) return;

    const { datasets, yMin } = getSpeedClockRateChartData();
    const xDatasets = dilationZoomLogX ? transformSpeedDatasetsForLogX(datasets) : datasets;
    const xMin = dilationZoomLogX ? getLogXMin(xDatasets, 1e-2) : undefined;

    const zoomConfig: ChartConfiguration = {
      type: "line",
      data: { datasets: xDatasets },
      options: {
        responsive: true,
        maintainAspectRatio: false,
        animation: { duration: 600, easing: "easeOutQuart" },
        interaction: { mode: "nearest", intersect: false },
        plugins: {
          legend: {
            labels: { color: "#a1a1aa", font: { size: 12 }, usePointStyle: true },
          },
          tooltip: {
            backgroundColor: "#27272a",
            titleColor: "#fafafa",
            bodyColor: "#d4d4d8",
            borderColor: "#52525b",
            borderWidth: 1,
            callbacks: {
              title: (items) => {
                const rawX = items[0].parsed.x as number;
                const v = dilationZoomLogX ? 1 - rawX : rawX;
                return `Velocity: ${formatVelocity(v)}`;
              },
              label: (item) => {
                const clockRate = item.parsed.y as number;
                const gamma = 1 / clockRate;
                return ` ship clock = ${fmtClockRateTick(clockRate)} of Earth (γ=${formatGamma(gamma)})`;
              },
            },
          },
        },
        scales: {
          x: {
            type: dilationZoomLogX ? "logarithmic" : "linear",
            ...(dilationZoomLogX ? { min: xMin, max: 1e-2, reverse: true } : { min: 0.99, max: 1 }),
            title: {
              display: true,
              text: dilationZoomLogX ? "Distance from c (1 - v/c), log scale" : "Velocity (v/c), zoomed to 99%+",
              color: "#d4d4d8",
              font: { size: 13 },
            },
            afterBuildTicks: dilationZoomLogX ? filterLogTicks : undefined,
            ticks: {
              color: "#a1a1aa",
              font: { size: 11 },
              callback: function (value) {
                const rawX = value as number;
                const v = dilationZoomLogX ? 1 - rawX : rawX;
                return fmtVelocityPercentTick(v);
              },
            },
            grid: { color: "#3f3f46", lineWidth: 0.5 },
          },
          y: {
            type: dilationZoomLogY ? "logarithmic" : "linear",
            ...(dilationZoomLogY ? { min: yMin, max: 1 } : { min: 0, max: 1 }),
            position: "left",
            title: { display: true, text: "Ship Clock Rate (1/γ)", color: "#d4d4d8", font: { size: 13 } },
            afterBuildTicks: dilationZoomLogY ? filterLogTicks : undefined,
            ticks: {
              color: "#a1a1aa",
              font: { size: 11 },
              callback: function (value) {
                return fmtClockRateTick(value as number);
              },
            },
            grid: { color: "#3f3f46", lineWidth: 0.5 },
          },
          yRight: {
            type: dilationZoomLogY ? "logarithmic" : "linear",
            ...(dilationZoomLogY ? { min: yMin, max: 1 } : { min: 0, max: 1 }),
            position: "right",
            title: { display: true, text: "Ship hours per Earth day", color: "#d4d4d8", font: { size: 13 } },
            afterBuildTicks: dilationZoomLogY ? filterLogTicks : undefined,
            ticks: {
              color: "#a1a1aa",
              font: { size: 11 },
              callback: function (value) {
                const clockRate = value as number;
                const hoursPerDay = 24 * clockRate;
                if (hoursPerDay >= 1) return `${hoursPerDay.toFixed(hoursPerDay < 10 ? 1 : 0)} h`;
                if (hoursPerDay >= 1 / 60) return `${(hoursPerDay * 60).toFixed(0)} m`;
                return `${(hoursPerDay * 3600).toFixed(0)} s`;
              },
            },
            grid: { drawOnChartArea: false },
          },
        },
      },
    };

    dilationZoomChartInstanceRef.current = new Chart(ctx, zoomConfig);
  }, [dilationZoomLogX, dilationZoomLogY, getSpeedClockRateChartData, showAccelChart]);

  useEffect(() => {
    buildAccelChart();
    buildClockRateChart();
    buildVelocityChart();
    buildDilationChart();
    buildDilationZoomChart();
    buildRapidityShipTimeChart();
    buildRapidityAccelTimeChart();
    buildRapidityDistanceChart();
    return () => {
      if (accelChartInstanceRef.current) {
        accelChartInstanceRef.current.destroy();
        accelChartInstanceRef.current = null;
      }
      if (clockRateChartInstanceRef.current) {
        clockRateChartInstanceRef.current.destroy();
        clockRateChartInstanceRef.current = null;
      }
      if (velocityChartInstanceRef.current) {
        velocityChartInstanceRef.current.destroy();
        velocityChartInstanceRef.current = null;
      }
      if (dilationChartInstanceRef.current) {
        dilationChartInstanceRef.current.destroy();
        dilationChartInstanceRef.current = null;
      }
      if (dilationZoomChartInstanceRef.current) {
        dilationZoomChartInstanceRef.current.destroy();
        dilationZoomChartInstanceRef.current = null;
      }
      if (rapidityShipTimeChartInstanceRef.current) {
        rapidityShipTimeChartInstanceRef.current.destroy();
        rapidityShipTimeChartInstanceRef.current = null;
      }
      if (rapidityAccelTimeChartInstanceRef.current) {
        rapidityAccelTimeChartInstanceRef.current.destroy();
        rapidityAccelTimeChartInstanceRef.current = null;
      }
      if (rapidityDistanceChartInstanceRef.current) {
        rapidityDistanceChartInstanceRef.current.destroy();
        rapidityDistanceChartInstanceRef.current = null;
      }
    };
  }, [
    buildAccelChart,
    buildClockRateChart,
    buildVelocityChart,
    buildDilationChart,
    buildDilationZoomChart,
    buildRapidityShipTimeChart,
    buildRapidityAccelTimeChart,
    buildRapidityDistanceChart,
  ]);

  // Handle destination dropdown
  const handleDestinationChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
    const name = e.target.value;
    setSelectedDestination(name);
    if (name !== "Custom") {
      const dest = DESTINATIONS.find((d) => d.name === name);
      if (dest) {
        setDistanceInput(dest.distanceLy.toString());
      }
    }
  };

  // Handle manual distance input
  const handleDistanceChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    const val = e.target.value;
    setDistanceInput(val);
    // Check if it matches a preset destination
    const parsed = parseFloat(val);
    const match = DESTINATIONS.find((d) => d.distanceLy === parsed);
    setSelectedDestination(match ? match.name : "Custom");
  };

  // Handle acceleration input
  const handleGChange = (e: React.ChangeEvent<HTMLInputElement>) => {
    setGInput(e.target.value);
  };

  // Group destinations by category for optgroup (exclude tableOnly entries)
  const groupedDestinations = DESTINATIONS.filter((d) => !d.tableOnly).reduce(
    (acc, dest) => {
      if (!acc[dest.category]) acc[dest.category] = [];
      acc[dest.category].push(dest);
      return acc;
    },
    {} as Record<string, Destination[]>,
  );

  return (
    <div className="max-w-none rounded-sm border border-zinc-800 bg-zinc-900 shadow-lg">
      <div className="p-3 md:p-6">
        <div className="grid gap-x-8 gap-y-6 md:grid-cols-2">
          {/* Left Column - Inputs */}
          <div className="space-y-6">
            <h3 className="text-xl font-semibold text-zinc-200">Journey Parameters</h3>

            {/* Destination Dropdown */}
            <div>
              <label className="mb-1 block text-sm font-medium text-zinc-300">Destination</label>
              <select
                value={selectedDestination}
                onChange={handleDestinationChange}
                className="block w-full rounded-md border border-zinc-600 bg-zinc-700 px-3 py-2 text-zinc-200 shadow-sm focus:border-TOElightERblue focus:ring-1 focus:ring-TOElightERblue focus:outline-none"
              >
                <option value="Custom">Custom distance</option>
                {(["stellar", "galactic", "extragalactic", "cosmological"] as const).map((cat) =>
                  groupedDestinations[cat] ? (
                    <optgroup key={cat} label={CATEGORY_LABELS[cat]}>
                      {groupedDestinations[cat].map((dest) => (
                        <option key={dest.name} value={dest.name}>
                          {dest.name} ({formatDistance(dest.distanceLy)})
                        </option>
                      ))}
                    </optgroup>
                  ) : null,
                )}
              </select>
            </div>

            {/* Distance Input */}
            <div>
              <label className="mb-1 block text-sm font-medium text-zinc-300">Distance (light-years)</label>
              <input
                type="text"
                value={distanceInput}
                onChange={handleDistanceChange}
                placeholder="e.g. 4.246"
                className="block w-full rounded-md border border-zinc-600 bg-zinc-700 px-3 py-2 text-zinc-200 shadow-sm focus:border-TOElightERblue focus:ring-1 focus:ring-TOElightERblue focus:outline-none"
              />
            </div>

            {/* Mode Toggle */}
            <div>
              <label className="mb-2 block text-sm font-medium text-zinc-300">Flight Mode</label>
              <div className="flex gap-2">
                <button
                  onClick={() => setBrachistochrone(true)}
                  className={`flex-1 rounded-md px-3 py-2 text-sm font-medium focus:ring-2 focus:ring-TOElightERblue focus:outline-none ${
                    brachistochrone ? "bg-TOElightERblue text-white" : "bg-zinc-700 text-zinc-300 hover:bg-zinc-600"
                  }`}
                >
                  Brachistochrone
                  <span className="block text-xs font-normal opacity-75">Stop at destination</span>
                </button>
                <button
                  onClick={() => setBrachistochrone(false)}
                  className={`flex-1 rounded-md px-3 py-2 text-sm font-medium focus:ring-2 focus:ring-TOElightERblue focus:outline-none ${
                    !brachistochrone ? "bg-TOElightERblue text-white" : "bg-zinc-700 text-zinc-300 hover:bg-zinc-600"
                  }`}
                >
                  Fly-by
                  <span className="block text-xs font-normal opacity-75">No deceleration at destination</span>
                </button>
              </div>
            </div>

            {/* Acceleration Input */}
            <div>
              <label className="mb-1 block text-sm font-medium text-zinc-300">
                Acceleration:{" "}
                {accelerationG > 0 ? `${accelerationG}g (${(accelerationG * G_STANDARD_MS2).toFixed(2)} m/s²)` : "—"}
              </label>

              {/* Preset Buttons */}
              <div className="mb-2 flex flex-wrap gap-2">
                {ACCELERATION_PRESETS.map((preset) => (
                  <button
                    key={preset.label}
                    onClick={() => setGInput(preset.value.toString())}
                    className={`rounded-md px-3 py-1 text-xs font-medium focus:ring-2 focus:ring-TOElightERblue focus:outline-none ${
                      Math.abs(accelerationG - preset.value) < 0.01
                        ? "bg-TOElightERblue text-white"
                        : "bg-zinc-700 text-zinc-300 hover:bg-zinc-600"
                    }`}
                  >
                    {preset.label}
                  </button>
                ))}
              </div>

              <div className="flex items-center gap-2">
                <input
                  type="range"
                  min="0.1"
                  max="10"
                  step="0.1"
                  value={accelerationG || 1}
                  onChange={handleGChange}
                  className="h-1.5 flex-1 cursor-pointer appearance-none rounded-lg bg-zinc-600 accent-TOElightERblue"
                />
                <div className="flex items-center gap-1">
                  <input
                    type="text"
                    value={gInput}
                    onChange={handleGChange}
                    placeholder="1"
                    className="w-16 rounded border border-zinc-600 bg-zinc-800 px-2 py-1 text-right text-sm text-zinc-200 focus:border-TOElightERblue focus:ring-1 focus:ring-TOElightERblue focus:outline-none"
                  />
                  <span className="text-xs text-zinc-500">g</span>
                </div>
              </div>
            </div>

            {/* Coast Phase (brachistochrone only) */}
            <div className={!brachistochrone ? "pointer-events-none opacity-40" : ""}>
              <label className="mb-1 block text-sm font-medium text-zinc-300">
                Coast Phase:{" "}
                {brachistochrone ? `${Math.max(0, Math.min(99, parseFloat(coastInput) || 0))}% of distance` : "N/A"}
              </label>
              <div className="flex items-center gap-2">
                <input
                  type="range"
                  min="0"
                  max="99"
                  step="1"
                  value={brachistochrone ? coastInput : "0"}
                  onChange={(e) => setCoastInput(e.target.value)}
                  disabled={!brachistochrone}
                  className="h-1.5 flex-1 cursor-pointer appearance-none rounded-lg bg-zinc-600 accent-TOElightERblue"
                />
                <div className="flex items-center gap-1">
                  <input
                    type="text"
                    value={brachistochrone ? coastInput : "0"}
                    onChange={(e) => setCoastInput(e.target.value)}
                    onBlur={() => setCoastInput(String(Math.max(0, Math.min(99, parseFloat(coastInput) || 0))))}
                    disabled={!brachistochrone}
                    className="w-14 rounded border border-zinc-600 bg-zinc-800 px-2 py-1 text-right text-sm text-zinc-200 focus:border-TOElightERblue focus:ring-1 focus:ring-TOElightERblue focus:outline-none"
                  />
                  <span className="text-xs text-zinc-500">%</span>
                </div>
              </div>
            </div>

            {/* Exhaust Velocity */}
            <div>
              <label className="mb-1 block text-sm font-medium text-zinc-300">
                Exhaust velocity (v<sub>e</sub>/c)
              </label>
              <p className="mb-1 text-xs text-zinc-500 italic">100% = photon drive (eg. Spin Drive)</p>
              <div className="flex items-center gap-2">
                <input
                  type="range"
                  min="1"
                  max="100"
                  step="1"
                  value={efficiencyInput}
                  onChange={(e) => setEfficiencyInput(e.target.value)}
                  className="h-1.5 flex-1 cursor-pointer appearance-none rounded-lg bg-zinc-600 accent-TOElightERblue"
                />
                <div className="flex items-center gap-1">
                  <input
                    type="text"
                    value={efficiencyInput}
                    onChange={(e) => setEfficiencyInput(e.target.value)}
                    className="w-14 rounded border border-zinc-600 bg-zinc-800 px-2 py-1 text-right text-sm text-zinc-200 focus:border-TOElightERblue focus:ring-1 focus:ring-TOElightERblue focus:outline-none"
                  />
                  <span className="text-xs text-zinc-500">%</span>
                </div>
              </div>
              {efficiency < 1 && (
                <p className="mt-1 text-xs text-zinc-500">
                  Effective exhaust velocity: {(efficiency * 100).toFixed(0)}% of c ({(efficiency * 299792).toFixed(0)}{" "}
                  km/s)
                </p>
              )}
            </div>

            {/* Ship Dry Mass */}
            <div>
              <label className="mb-1 block text-sm font-medium text-zinc-300">Ship Dry Mass</label>
              <div className="flex items-center gap-2">
                <input
                  type="text"
                  value={dryMassInput}
                  onChange={(e) => setDryMassInput(e.target.value)}
                  placeholder="e.g. 100000"
                  className="flex-1 rounded-md border border-zinc-600 bg-zinc-700 px-3 py-2 text-zinc-200 shadow-sm focus:border-TOElightERblue focus:ring-1 focus:ring-TOElightERblue focus:outline-none"
                />
                <span className="text-xs text-zinc-500">kg</span>
              </div>
              {dryMassKg > 0 && <p className="mt-1 text-xs text-zinc-500">{formatMass(dryMassKg)}</p>}
            </div>

            {/* Toggle Buttons */}
            <div className="flex flex-wrap gap-2">
              <button
                onClick={() => setShowAccelChart(!showAccelChart)}
                className={`rounded-md px-4 py-2 text-sm font-medium focus:ring-2 focus:ring-zinc-500 focus:outline-none ${
                  showAccelChart ? "bg-TOElightERblue text-white" : "bg-zinc-700 text-zinc-300 hover:bg-zinc-600"
                }`}
              >
                {showAccelChart ? "Hide Charts" : "Show Charts"}
              </button>
              <button
                onClick={() => setShowAllDestinations(!showAllDestinations)}
                className={`rounded-md px-4 py-2 text-sm font-medium focus:ring-2 focus:ring-zinc-500 focus:outline-none ${
                  showAllDestinations ? "bg-TOElightERblue text-white" : "bg-zinc-700 text-zinc-300 hover:bg-zinc-600"
                }`}
              >
                {showAllDestinations ? "Hide Destinations Table" : "Show All Destinations Table"}
              </button>
            </div>
          </div>

          {/* Right Column - Results */}
          <div className="space-y-6">
            <h3 className="text-xl font-semibold text-zinc-200">Journey Results</h3>

            {!results ? (
              <div className="text-zinc-400">Enter a distance and acceleration to see results.</div>
            ) : (
              <>
                {/* Primary Time Cards */}
                <div className="grid grid-cols-2 gap-4">
                  <div className="group relative rounded-lg bg-zinc-700 p-3">
                    <h4 className="text-sm font-medium text-zinc-400">Ship Time</h4>
                    <p className="text-xl font-bold text-TOElightERblue">{formatTime(results.shipTimeYears)}</p>
                    <div className="pointer-events-none absolute right-0 bottom-full left-0 z-10 mb-2 rounded-lg border border-zinc-600 bg-zinc-800 p-3 text-sm text-zinc-300 opacity-0 shadow-lg transition-opacity group-hover:opacity-100">
                      {results.timeDilationRatio > 1.01
                        ? `You'd age ${formatTime(results.shipTimeYears)} while ${formatTime(results.earthTimeYears)} pass on Earth — a time dilation factor of ${results.timeDilationRatio < 1000 ? results.timeDilationRatio.toFixed(1) : results.timeDilationRatio.toExponential(2)}×.`
                        : `Relativistic effects are negligible at this distance and acceleration.`}
                    </div>
                  </div>
                  <div className="group relative rounded-lg bg-zinc-700 p-3">
                    <h4 className="text-sm font-medium text-zinc-400">Earth Time</h4>
                    <p className="text-xl font-bold text-TOElightERblue">{formatTime(results.earthTimeYears)}</p>
                    <div className="pointer-events-none absolute right-0 bottom-full left-0 z-10 mb-2 rounded-lg border border-zinc-600 bg-zinc-800 p-3 text-sm text-zinc-300 opacity-0 shadow-lg transition-opacity group-hover:opacity-100">
                      {results.timeDilationRatio > 1.01
                        ? `You'd age ${formatTime(results.shipTimeYears)} while ${formatTime(results.earthTimeYears)} pass on Earth — a time dilation factor of ${results.timeDilationRatio < 1000 ? results.timeDilationRatio.toFixed(1) : results.timeDilationRatio.toExponential(2)}×.`
                        : `Relativistic effects are negligible at this distance and acceleration.`}
                    </div>
                  </div>
                </div>

                {/* Journey Details */}
                <div className="rounded-lg bg-zinc-700 p-4">
                  <h4 className="mb-2 font-medium text-zinc-300">Journey Details</h4>
                  <ul className="space-y-2 text-sm">
                    <li className="flex justify-between">
                      <span className="text-zinc-400">Distance:</span>
                      <span className="font-medium text-zinc-200">{formatDistance(distanceLy)}</span>
                    </li>
                    <li className="flex justify-between">
                      <span className="text-zinc-400">Acceleration:</span>
                      <span className="font-medium text-zinc-200">
                        {accelerationG}g ({(accelerationG * G_STANDARD_MS2).toFixed(2)} m/s²)
                      </span>
                    </li>
                    <li className="flex justify-between">
                      <span className="text-zinc-400">Mode:</span>
                      <span className="font-medium text-zinc-200">
                        {brachistochrone ? "Brachistochrone" : "Fly-by"}
                      </span>
                    </li>
                    <li className="flex justify-between">
                      <span className="text-zinc-400">Peak Velocity:</span>
                      <span className="font-medium text-zinc-200">
                        {formatVelocity(results.peakVelocityFractionC, results.peakGamma)}
                      </span>
                    </li>
                    <li className="flex justify-between">
                      <span className="text-zinc-400">Peak Lorentz Factor (γ):</span>
                      <span className="font-medium text-zinc-200">{formatGamma(results.peakGamma)}</span>
                    </li>
                    <li className="flex justify-between">
                      <span className="text-zinc-400">Time Dilation Ratio:</span>
                      <span className="font-medium text-zinc-200">
                        {results.timeDilationRatio < 1000
                          ? `${results.timeDilationRatio.toFixed(2)}×`
                          : `${results.timeDilationRatio.toExponential(2)}×`}
                      </span>
                    </li>
                  </ul>
                </div>

                {/* Coast Phase */}
                {coastFraction > 0 && distanceLy > 0 && (
                  <div className="rounded-lg bg-zinc-700 p-4">
                    <h4 className="mb-2 font-medium text-zinc-300">Coast Phase</h4>
                    <p className="text-sm text-zinc-200">
                      {brachistochrone
                        ? `Accel ${formatDistance((distanceLy * (1 - coastFraction)) / 2)} → Coast ${formatDistance(distanceLy * coastFraction)} → Decel ${formatDistance((distanceLy * (1 - coastFraction)) / 2)}`
                        : `Accel ${formatDistance(distanceLy * (1 - coastFraction))} → Coast ${formatDistance(distanceLy * coastFraction)}`}
                    </p>
                    <p className="mt-1 text-xs text-zinc-500">
                      Coasting at {formatVelocity(results.peakVelocityFractionC, results.peakGamma)} for{" "}
                      {(coastFraction * 100).toFixed(0)}% of the distance
                    </p>
                  </div>
                )}

                {/* Rocket Mass Ratio & Fuel */}
                <div className="rounded-lg bg-zinc-700 p-4">
                  <h4 className="mb-2 font-medium text-zinc-300">Rocket Mass Ratio</h4>

                  <ul className="space-y-2 text-sm">
                    <li className="flex justify-between">
                      <span className="text-zinc-400">Mass ratio:</span>
                      <span className="font-medium text-TOElightERblue">{formatMassRatio(adjustedBrach)}</span>
                    </li>
                  </ul>

                  {dryMassKg > 0 && (
                    <div className="mt-3 border-t border-zinc-600 pt-3">
                      <ul className="space-y-2 text-sm">
                        <li className="flex justify-between">
                          <span className="text-zinc-400">Total launch mass:</span>
                          <span className="font-medium text-zinc-200">{formatMass(dryMassKg * adjustedBrach)}</span>
                        </li>
                        <li className="flex justify-between">
                          <span className="text-zinc-400">Fuel required:</span>
                          <span className="font-medium text-TOElightERblue">
                            {formatMass(dryMassKg * (adjustedBrach - 1))}
                          </span>
                        </li>
                        {(() => {
                          const fuelEnergy = dryMassKg * (adjustedBrach - 1) * C_SQUARED;
                          if (!isFinite(fuelEnergy)) return null;
                          const comparison = getEnergyComparison(fuelEnergy);
                          return (
                            <>
                              <li className="flex justify-between">
                                <span className="text-zinc-400">Energy (mc²):</span>
                                <span className="font-medium text-zinc-200">{formatEnergy(fuelEnergy)}</span>
                              </li>
                              {comparison && <li className="text-xs text-zinc-500 italic">{comparison}</li>}
                            </>
                          );
                        })()}
                      </ul>
                    </div>
                  )}
                  {dryMassKg > 0 &&
                    (() => {
                      const totalMass = dryMassKg * adjustedBrach;

                      // Handle infinite/overflow mass ratios
                      if (!isFinite(totalMass) || !isFinite(adjustedBrach)) {
                        return (
                          <div className="mt-3 rounded-lg border border-TOEred/40 bg-TOEred/5 p-3">
                            <p className="text-xs font-medium text-TOEred">Feasibility</p>
                            <p className="mt-1 text-xs text-zinc-300">
                              The required fuel mass overflows calculable numbers. This trip is physically impossible
                              regardless of available fuel — the mass ratio exceeds the number of atoms in the
                              observable universe many times over.
                            </p>
                          </div>
                        );
                      }

                      if (totalMass < 1e5) return null;

                      const rs = totalMass * SCHWARZSCHILD_FACTOR;
                      const ref = getMassComparison(totalMass);
                      const isBlackHole = totalMass > TOV_LIMIT_KG;

                      return (
                        <div
                          className={`mt-3 rounded-lg border p-3 ${isBlackHole ? "border-TOEred/40 bg-TOEred/5" : "border-zinc-600 bg-zinc-800/50"}`}
                        >
                          <p className={`mb-2 text-xs font-medium ${isBlackHole ? "text-TOEred" : "text-zinc-400"}`}>
                            Feasibility
                          </p>
                          {ref && (
                            <p className="text-xs text-zinc-300">
                              Launch mass ={" "}
                              <span className="font-medium text-TOElightERblue">{formatMultiple(ref.multiple)}×</span>{" "}
                              the mass of {ref.name}
                            </p>
                          )}
                          {DENSITY_COMPARISONS.map(({ name, density }) => {
                            const r = sphereRadius(totalMass, density);
                            if (r <= rs) return null; // inside its own black hole — meaningless
                            return (
                              <p key={name} className="mt-1 text-xs text-zinc-400">
                                <span
                                  className="cursor-help underline decoration-zinc-600 decoration-dotted"
                                  title={`Radius if the total fuel mass were compressed to the density of ${name} (${density.toExponential(2)} kg/m³)`}
                                >
                                  If {name} density
                                </span>
                                : <span className="font-medium text-zinc-200">{formatRadius(r)}</span>
                                <span className="text-zinc-500"> — {getRadiusComparison(r)}</span>
                              </p>
                            );
                          })}
                          <p className="mt-1 text-xs text-zinc-400">
                            <span
                              className="cursor-help underline decoration-zinc-600 decoration-dotted"
                              title="The Schwarzschild radius is the radius at which mass becomes a black hole if compressed within it."
                            >
                              Schwarzschild radius
                            </span>
                            : <span className="font-medium text-zinc-200">{formatRadius(rs)}</span>
                            <span className="text-zinc-500"> — {getRadiusComparison(rs)}</span>
                          </p>
                          {isBlackHole && (
                            <p className="mt-2 text-xs text-TOEred">
                              This fuel mass exceeds the TOV limit (~3 M<sub>&#x2609;</sub>). No known force can prevent
                              gravitational collapse — your rocket is a black hole.
                            </p>
                          )}
                        </div>
                      );
                    })()}

                  {/* Project Hail Mary Discrepancy */}
                  {isPHM &&
                    (() => {
                      const noCoast = computeTravel(distanceLy, accelerationG, true, 0);
                      return (
                        <div className="mt-4 border-t border-zinc-600 pt-3">
                          <button
                            onClick={() => setShowPHM(!showPHM)}
                            className="flex w-full items-center gap-2 rounded-lg border border-TOEorange/40 bg-TOEorange/5 px-3 py-2 text-left text-sm font-medium text-TOEorange transition hover:bg-TOEorange/10"
                          >
                            <span
                              className="text-xs transition-transform"
                              style={{ transform: showPHM ? "rotate(90deg)" : "rotate(0deg)" }}
                            >
                              &#9654;
                            </span>
                            Project Hail Mary Discrepancy
                          </button>
                          {showPHM && (
                            <div className="mt-2 rounded-lg border border-TOEorange/40 bg-TOEorange/5 p-3">
                              <p className="mb-3 text-sm text-zinc-300">
                                The Hail Mary carries ~2 million kg of Astrophage fuel for a 100,000 kg ship (~21:1
                                ratio) on a brachistochrone trip to Tau Ceti at 1.5g.
                              </p>

                              <div className="mb-3 grid grid-cols-2 gap-3 text-sm">
                                <div className="rounded bg-zinc-800 p-2">
                                  <p className="text-xs text-zinc-500">Book&apos;s fuel</p>
                                  <p className="font-medium text-zinc-300">2,000 tonnes</p>
                                  <p className="mt-1 text-xs text-zinc-500 italic">
                                    &ldquo;two million kilograms of Astrophage&rdquo;
                                  </p>
                                </div>
                                <div className="rounded bg-zinc-800 p-2">
                                  <p className="text-xs text-zinc-500">Correct fuel (no coast)</p>
                                  <p className="font-medium text-TOEorange">
                                    {formatMass(100000 * (noCoast.massRatioBrach - 1))}
                                  </p>
                                </div>
                              </div>
                              <p className="mb-3 text-xs text-zinc-500">
                                For brachistochrone, you need fuel to decelerate at the destination (mass ratio R
                                applied to dry mass), <em>and</em> fuel to accelerate that decel-loaded ship (R again).
                                The ratios multiply: R<sub>total</sub> = R².
                              </p>
                              <div className="border-t border-TOEorange/20 pt-3">
                                <p className="mb-2 text-xs font-medium text-TOEorange">The cruise phase fix</p>
                                <p className="text-xs text-zinc-400">
                                  Andy Weir has acknowledged the mass ratio error, noting that the Hail Mary includes a
                                  cruise phase — coasting at peak velocity for part of the trip — which dramatically
                                  reduces the required fuel. Try the coast phase slider above: at ~85% coast, the
                                  brachistochrone mass ratio drops to roughly 21:1, matching the book&apos;s fuel load,
                                  while adding less than a year to the Earth-frame journey.
                                </p>
                                {coastFraction > 0 && (
                                  <p className="mt-2 text-xs text-zinc-300">
                                    Current ({(coastFraction * 100).toFixed(0)}% coast): mass ratio{" "}
                                    <span className="font-medium text-TOElightERblue">
                                      {results.massRatioBrach.toFixed(1)} : 1
                                    </span>
                                    , fuel{" "}
                                    <span className="font-medium text-TOElightERblue">
                                      {formatMass(100000 * (results.massRatioBrach - 1))}
                                    </span>{" "}
                                    (vs. {noCoast.massRatioBrach.toFixed(1)} : 1 without coast)
                                  </p>
                                )}
                              </div>
                              <div className="mt-3 border-t border-TOEorange/20 pt-3">
                                <p className="mb-2 text-xs font-medium text-TOEorange">Thrust vs. consumption</p>
                                <p className="text-xs text-zinc-400">
                                  The book also states 6.045 g/s fuel consumption and 30 MN thrust (3 engines × 10⁷ N).
                                  An ideal engine converting 6 g/s would produce only{" "}
                                  <span className="text-zinc-200">1.81 MN</span> of thrust (F = ṁc). To get 30 MN
                                  you&apos;d need ~100 g/s — or to match 6 g/s at 1.5g on a 2.17M kg ship, the engine
                                  would need <span className="text-zinc-200">&gt;16× the momentum of light</span>, which
                                  is physically impossible. These two numbers are inconsistent.
                                </p>
                              </div>
                            </div>
                          )}
                        </div>
                      );
                    })()}
                </div>
              </>
            )}
          </div>
        </div>

        {/* Acceleration Comparison Chart */}
        {showAccelChart && (
          <div className="mt-8">
            <div className="mb-4 flex items-start justify-between gap-4">
              <div>
                <h3 className="mb-1 text-xl font-semibold text-zinc-200">
                  Acceleration Comparison — {brachistochrone ? "Brachistochrone" : "Fly-by"}
                </h3>
                <p className="text-sm text-zinc-500">
                  {accelChartLinear
                    ? `Ship time vs distance (0–${formatDistance(distanceLy > 0 ? distanceLy * 3 : 100)}) — focused on the selected destination`
                    : "Ship time vs distance at different accelerations — parallel curves on log-log show that higher g mainly shifts travel time down"}
                </p>
              </div>
              <div className="flex shrink-0 gap-1 rounded-md bg-zinc-800 p-0.5">
                <button
                  onClick={() => setAccelChartLinear(false)}
                  className={`rounded px-2 py-1 text-xs font-medium ${!accelChartLinear ? "bg-TOElightERblue text-white" : "text-zinc-400 hover:text-zinc-200"}`}
                >
                  Log
                </button>
                <button
                  onClick={() => setAccelChartLinear(true)}
                  className={`rounded px-2 py-1 text-xs font-medium ${accelChartLinear ? "bg-TOElightERblue text-white" : "text-zinc-400 hover:text-zinc-200"}`}
                >
                  Linear
                </button>
              </div>
            </div>
            <div className="relative h-80 rounded-lg bg-zinc-800 p-3 md:h-[460px] md:p-4">
              <canvas ref={accelChartRef}></canvas>
            </div>

            <div className="mt-6">
              <div className="mb-4 flex items-start justify-between gap-4">
                <div>
                  <h4 className="mb-2 text-lg font-semibold text-zinc-200">Ship Clock Rate vs Distance</h4>
                  <p className="text-sm text-zinc-500">
                    {clockRateChartLinear
                      ? `Ship clock rate vs distance (0–${formatDistance(distanceLy > 0 ? distanceLy * 3 : 100)}) — lower means onboard time is running more slowly`
                      : "Ship clock rate vs distance at different accelerations — higher g drives the onboard clock lower sooner"}
                  </p>
                </div>
                <div className="flex shrink-0 gap-1 rounded-md bg-zinc-800 p-0.5">
                  <button
                    onClick={() => setClockRateChartLinear(false)}
                    className={`rounded px-2 py-1 text-xs font-medium ${!clockRateChartLinear ? "bg-TOElightERblue text-white" : "text-zinc-400 hover:text-zinc-200"}`}
                  >
                    Log
                  </button>
                  <button
                    onClick={() => setClockRateChartLinear(true)}
                    className={`rounded px-2 py-1 text-xs font-medium ${clockRateChartLinear ? "bg-TOElightERblue text-white" : "text-zinc-400 hover:text-zinc-200"}`}
                  >
                    Linear
                  </button>
                </div>
              </div>
              <div className="relative h-80 rounded-lg bg-zinc-800 p-3 md:h-[460px] md:p-4">
                <canvas ref={clockRateChartRef}></canvas>
              </div>
            </div>

            {/* Velocity vs Distance Chart */}
            <div className="mt-6">
              <h4 className="mb-2 text-lg font-semibold text-zinc-200">Velocity vs Distance</h4>
              <p className="mb-3 text-sm text-zinc-500">
                Peak velocity as fraction of c — all curves plateau as they approach the speed of light
              </p>
              <div className="relative h-80 rounded-lg bg-zinc-800 p-3 md:h-[460px] md:p-4">
                <canvas ref={velocityChartRef}></canvas>
              </div>
            </div>

            {/* Time Dilation vs Speed Chart */}
            <div className="mt-6">
              <div className="mb-3 flex items-start justify-between gap-4">
                <div>
                  <h4 className="mb-2 text-lg font-semibold text-zinc-200">Ship Clock Rate vs Speed</h4>
                  <p className="text-sm text-zinc-500">
                    The left axis shows the ship&apos;s clock rate, `1/γ`. Lower values mean time onboard is running
                    more slowly relative to Earth.
                  </p>
                </div>
                <div className="flex shrink-0 gap-1 rounded-md bg-zinc-800 p-0.5">
                  <button
                    onClick={() => setDilationChartLogX(false)}
                    className={`rounded px-2 py-1 text-xs font-medium ${!dilationChartLogX ? "bg-TOElightERblue text-white" : "text-zinc-400 hover:text-zinc-200"}`}
                  >
                    Linear X
                  </button>
                  <button
                    onClick={() => setDilationChartLogX(true)}
                    className={`rounded px-2 py-1 text-xs font-medium ${dilationChartLogX ? "bg-TOElightERblue text-white" : "text-zinc-400 hover:text-zinc-200"}`}
                  >
                    Log X
                  </button>
                </div>
                <div className="flex shrink-0 gap-1 rounded-md bg-zinc-800 p-0.5">
                  <button
                    onClick={() => setDilationChartLogY(false)}
                    className={`rounded px-2 py-1 text-xs font-medium ${!dilationChartLogY ? "bg-TOElightERblue text-white" : "text-zinc-400 hover:text-zinc-200"}`}
                  >
                    Linear Y
                  </button>
                  <button
                    onClick={() => setDilationChartLogY(true)}
                    className={`rounded px-2 py-1 text-xs font-medium ${dilationChartLogY ? "bg-TOElightERblue text-white" : "text-zinc-400 hover:text-zinc-200"}`}
                  >
                    Log Y
                  </button>
                </div>
              </div>
              <p className="mb-3 text-sm text-zinc-500">
                {dilationChartLogX
                  ? "Log X uses distance from light speed, `1 - v/c`, so the near-c compression becomes easier to read across the full curve."
                  : "Linear X shows the familiar velocity scale from rest to c."}{" "}
                {dilationChartLogY
                  ? "Log Y emphasizes the steep slowdown in the ship clock."
                  : "Linear Y shows clock-rate changes in direct percentage space."}
              </p>
              <p className="mb-3 text-sm text-zinc-500">
                {distanceLy > 0 &&
                  ` Each dot marks the slowest onboard clock rate reached traveling to ${selectedDestination !== "Custom" ? selectedDestination : "the selected destination"} at that acceleration.`}
              </p>
              <div className="relative h-80 rounded-lg bg-zinc-800 p-3 md:h-[460px] md:p-4">
                <canvas ref={dilationChartRef}></canvas>
              </div>
            </div>

            <div className="mt-6">
              <div className="mb-3 flex items-start justify-between gap-4">
                <div>
                  <h4 className="mb-2 text-lg font-semibold text-zinc-200">Ship Clock Rate vs Speed (99%+ Inset)</h4>
                  <p className="text-sm text-zinc-500">
                    Zoomed into the last 1% below light speed, where the clock-rate falloff gets compressed in the full
                    chart.
                  </p>
                </div>
                <div className="flex shrink-0 gap-1 rounded-md bg-zinc-800 p-0.5">
                  <button
                    onClick={() => setDilationZoomLogX(false)}
                    className={`rounded px-2 py-1 text-xs font-medium ${!dilationZoomLogX ? "bg-TOElightERblue text-white" : "text-zinc-400 hover:text-zinc-200"}`}
                  >
                    Linear X
                  </button>
                  <button
                    onClick={() => setDilationZoomLogX(true)}
                    className={`rounded px-2 py-1 text-xs font-medium ${dilationZoomLogX ? "bg-TOElightERblue text-white" : "text-zinc-400 hover:text-zinc-200"}`}
                  >
                    Log X
                  </button>
                </div>
                <div className="flex shrink-0 gap-1 rounded-md bg-zinc-800 p-0.5">
                  <button
                    onClick={() => setDilationZoomLogY(false)}
                    className={`rounded px-2 py-1 text-xs font-medium ${!dilationZoomLogY ? "bg-TOElightERblue text-white" : "text-zinc-400 hover:text-zinc-200"}`}
                  >
                    Linear Y
                  </button>
                  <button
                    onClick={() => setDilationZoomLogY(true)}
                    className={`rounded px-2 py-1 text-xs font-medium ${dilationZoomLogY ? "bg-TOElightERblue text-white" : "text-zinc-400 hover:text-zinc-200"}`}
                  >
                    Log Y
                  </button>
                </div>
              </div>
              <p className="mb-3 text-sm text-zinc-500">
                {dilationZoomLogX
                  ? "Log X uses distance from light speed, `1 - v/c`, so the remaining gap to c is easier to read."
                  : "Linear X shows the familiar velocity scale, but only from 99% c up to c."}{" "}
                {dilationZoomLogY
                  ? "Log Y preserves the full drop in clock rate."
                  : "Linear Y expands the upper part of the curve near 100% clock rate."}
              </p>
              <div className="relative h-80 rounded-lg bg-zinc-800 p-3 md:h-[420px] md:p-4">
                <canvas ref={dilationZoomChartRef}></canvas>
              </div>
            </div>

            <div className="mt-6 rounded-lg border border-zinc-700 bg-zinc-800/60 p-4">
              <h4 className="mb-2 text-lg font-semibold text-zinc-200">What Is Rapidity?</h4>
              <p className="text-sm text-zinc-400">
                Rapidity, `φ`, is the velocity coordinate relativity wants you to use. Instead of treating speed itself
                as the fundamental quantity, special relativity rewrites it as:
              </p>
              <div className="mt-3 rounded-md bg-zinc-900 px-4 py-3 font-mono text-sm text-zinc-200">
                v/c = tanh(φ), γ = cosh(φ), γv/c = sinh(φ)
              </div>
              <p className="mt-3 text-sm text-zinc-400">
                For a constantly accelerating rocket, rapidity grows linearly while you are under thrust. That is why it
                is more informative than velocity once the ship gets close to `c`.
              </p>
            </div>

            <div className="mt-6">
              <h4 className="mb-2 text-lg font-semibold text-zinc-200">Rapidity vs Ship Time</h4>
              <p className="mb-3 text-sm text-zinc-500">
                Peak rapidity reached during the trip against total ship time. The x-axis is logarithmic because total
                trip time spans orders of magnitude.
              </p>
              <div className="relative h-80 rounded-lg bg-zinc-800 p-3 md:h-[420px] md:p-4">
                <canvas ref={rapidityShipTimeChartRef}></canvas>
              </div>
            </div>

            <div className="mt-6">
              <h4 className="mb-2 text-lg font-semibold text-zinc-200">Rapidity vs Powered Ship Time</h4>
              <p className="mb-3 text-sm text-zinc-500">
                This uses only the ship time spent under thrust up to peak speed. For constant proper acceleration,
                rapidity grows linearly during powered flight, so these curves are the cleanest statement of the rule.
              </p>
              <div className="relative h-80 rounded-lg bg-zinc-800 p-3 md:h-[420px] md:p-4">
                <canvas ref={rapidityAccelTimeChartRef}></canvas>
              </div>
            </div>

            <div className="mt-6">
              <h4 className="mb-2 text-lg font-semibold text-zinc-200">Rapidity vs Distance</h4>
              <p className="mb-3 text-sm text-zinc-500">
                Peak rapidity against distance on a logarithmic distance axis. This makes it easier to see how
                relativistic travel “spreads out” cosmic distances.
              </p>
              <div className="relative h-80 rounded-lg bg-zinc-800 p-3 md:h-[420px] md:p-4">
                <canvas ref={rapidityDistanceChartRef}></canvas>
              </div>
            </div>
          </div>
        )}

        {/* All Destinations Table */}
        {showAllDestinations && (
          <div className="mt-8">
            <h3 className="mb-4 text-xl font-semibold text-zinc-200">
              All Destinations at {accelerationG}g ({brachistochrone ? "Brachistochrone" : "Fly-by"})
              {efficiency < 1 ? `, v_e = ${(efficiency * 100).toFixed(0)}% c` : ""}
            </h3>
            <div className="overflow-x-auto rounded-lg border border-zinc-700">
              <table className="min-w-full divide-y divide-zinc-700">
                <thead className="bg-zinc-800">
                  <tr>
                    <th className="px-4 py-3 text-left text-xs font-medium tracking-wider text-zinc-400 uppercase">
                      Destination
                    </th>
                    <th className="px-4 py-3 text-left text-xs font-medium tracking-wider text-zinc-400 uppercase">
                      Distance
                    </th>
                    <th className="px-4 py-3 text-left text-xs font-medium tracking-wider text-zinc-400 uppercase">
                      Ship Time
                    </th>
                    <th className="px-4 py-3 text-left text-xs font-medium tracking-wider text-zinc-400 uppercase">
                      Earth Time
                    </th>
                    <th className="px-4 py-3 text-left text-xs font-medium tracking-wider text-zinc-400 uppercase">
                      Peak v
                    </th>
                    <th className="px-4 py-3 text-left text-xs font-medium tracking-wider text-zinc-400 uppercase">
                      Mass Ratio
                    </th>
                  </tr>
                </thead>
                <tbody className="divide-y divide-zinc-800 bg-zinc-900">
                  {allDestinationResults.map((entry) => (
                    <tr key={entry.name} className={selectedDestination === entry.name ? "bg-zinc-800" : ""}>
                      <td className="px-4 py-3 text-sm font-medium whitespace-nowrap text-zinc-200">{entry.name}</td>
                      <td className="px-4 py-3 text-sm whitespace-nowrap text-zinc-300">
                        {formatDistance(entry.distanceLy)}
                      </td>
                      <td className="px-4 py-3 text-sm whitespace-nowrap text-zinc-300">
                        {formatTimeShort(entry.results.shipTimeYears)}
                      </td>
                      <td className="px-4 py-3 text-sm whitespace-nowrap text-zinc-300">
                        {formatTimeShort(entry.results.earthTimeYears)}
                      </td>
                      <td className="px-4 py-3 text-sm whitespace-nowrap text-zinc-300">
                        {formatVelocity(entry.results.peakVelocityFractionC, entry.results.peakGamma)}
                      </td>
                      <td className="px-4 py-3 text-sm whitespace-nowrap text-zinc-300">
                        {(() => {
                          const r = Math.pow(entry.results.massRatioBrach, 1 / efficiency);
                          return r < 1e6 ? `${r.toFixed(r < 100 ? 1 : 0)} : 1` : `${r.toExponential(2)} : 1`;
                        })()}
                      </td>
                    </tr>
                  ))}
                </tbody>
              </table>
            </div>
          </div>
        )}

        {/* Footer */}
        <div className="mt-6 flex items-center justify-between text-xs text-zinc-400">
          <span className="text-TOElightERblue">{VERSION}</span>
          <a
            href="/api/source/CalcRelativisticRocket.tsx"
            className="hover:text-TOElightERblue"
            target="_blank"
            rel="noopener noreferrer"
          >
            Source Code
          </a>
        </div>
      </div>
    </div>
  );
};

export default CalcRelativisticRocket;
