how-far-in-x-hours

Experiment · IPYNB

How Far Can You Get in X Hours?

Select countries on the globe, set your available time, and choose a travel mode to see how far you could travel from each country's capital.

In [1]:
Expand CodeCollapse Code
import plotly.graph_objects as go
import ipywidgets as widgets
import numpy as np
from IPython.display import display, HTML

print("Setup complete.")
Setup complete.

Travel Mode Speeds & Country Infrastructure Adjustments

Speeds vary by country due to infrastructure quality, terrain, and regulations.

In [2]:
Expand CodeCollapse Code
MODE_SPEEDS = {
    "Walking":  5.0,
    "Cycling":  15.0,
    "Driving":  80.0,
    "Public Transit": 40.0,
}

INFRA_MULTIPLIERS = {
    "Walking": {
        "Japan": 1.1, "Netherlands": 1.1, "Singapore": 1.15,
        "Switzerland": 1.05, "Germany": 1.05, "UK": 1.0,
        "USA": 0.9, "India": 0.85, "Brazil": 0.85, "Nigeria": 0.8,
        "Default": 1.0
    },
    "Cycling": {
        "Netherlands": 1.25, "Denmark": 1.2, "Germany": 1.15,
        "Belgium": 1.1, "Sweden": 1.1, "Norway": 1.1,
        "Japan": 1.05, "UK": 1.0, "France": 1.0,
        "USA": 0.85, "India": 0.7, "Brazil": 0.75,
        "Default": 0.9
    },
    "Driving": {
        "Germany": 1.3, "France": 1.15, "USA": 1.1,
        "Japan": 1.05, "UK": 1.0, "South Korea": 1.0,
        "China": 0.9, "Brazil": 0.8, "India": 0.7,
        "Nigeria": 0.65, "Default": 0.9
    },
    "Public Transit": {
        "Japan": 1.5, "Switzerland": 1.4, "South Korea": 1.35,
        "Singapore": 1.3, "Germany": 1.25, "France": 1.2,
        "UK": 1.15, "China": 1.1, "Netherlands": 1.1,
        "USA": 0.75, "India": 0.6, "Brazil": 0.65,
        "Nigeria": 0.55, "Default": 0.85
    }
}

def get_speed(mode, country):
    base = MODE_SPEEDS[mode]
    mult = INFRA_MULTIPLIERS[mode].get(country, INFRA_MULTIPLIERS[mode]["Default"])
    return base * mult

Country Data

In [3]:
Expand CodeCollapse Code
COUNTRIES = [
    {"name": "Afghanistan",       "lat": 33.93, "lon": 67.71,  "iso": "AFG", "region": "Asia"},
    {"name": "Albania",            "lat": 41.15, "lon": 20.17,  "iso": "ALB", "region": "Europe"},
    {"name": "Algeria",            "lat": 28.03, "lon": 1.66,   "iso": "DZA", "region": "Africa"},
    {"name": "Angola",             "lat": -11.2,"lon": 17.87,  "iso": "AGO", "region": "Africa"},
    {"name": "Argentina",          "lat": -38.4,"lon": -63.6,  "iso": "ARG", "region": "South America"},
    {"name": "Armenia",            "lat": 40.07, "lon": 45.04,  "iso": "ARM", "region": "Asia"},
    {"name": "Australia",          "lat": -25.27,"lon": 133.78, "iso": "AUS", "region": "Oceania"},
    {"name": "Austria",            "lat": 47.52, "lon": 14.55,  "iso": "AUT", "region": "Europe"},
    {"name": "Azerbaijan",         "lat": 40.14, "lon": 47.58,  "iso": "AZE", "region": "Asia"},
    {"name": "Bangladesh",         "lat": 23.68, "lon": 90.36,  "iso": "BGD", "region": "Asia"},
    {"name": "Belarus",            "lat": 53.71, "lon": 27.95,  "iso": "BLR", "region": "Europe"},
    {"name": "Belgium",            "lat": 50.5,  "lon": 4.47,   "iso": "BEL", "region": "Europe"},
    {"name": "Bolivia",            "lat": -16.29,"lon": -63.59, "iso": "BOL", "region": "South America"},
    {"name": "Bosnia and Herz.",   "lat": 43.92, "lon": 17.68,  "iso": "BIH", "region": "Europe"},
    {"name": "Botswana",           "lat": -22.33,"lon": 24.68,  "iso": "BWA", "region": "Africa"},
    {"name": "Brazil",             "lat": -14.24,"lon": -51.93, "iso": "BRA", "region": "South America"},
    {"name": "Bulgaria",           "lat": 42.73, "lon": 25.49,  "iso": "BGR", "region": "Europe"},
    {"name": "Burkina Faso",       "lat": 12.37, "lon": -1.52,  "iso": "BFA", "region": "Africa"},
    {"name": "Burundi",            "lat": -3.37, "lon": 29.92,  "iso": "BDI", "region": "Africa"},
    {"name": "Cambodia",           "lat": 12.57, "lon": 104.99, "iso": "KHM", "region": "Asia"},
    {"name": "Cameroon",           "lat": 7.37,  "lon": 12.35,  "iso": "CMR", "region": "Africa"},
    {"name": "Canada",             "lat": 56.13, "lon": -106.35,"iso": "CAN", "region": "North America"},
    {"name": "Central African Rep.","lat": 6.61, "lon": 20.94,  "iso": "CAF", "region": "Africa"},
    {"name": "Chad",               "lat": 15.45, "lon": 18.73,  "iso": "TCD", "region": "Africa"},
    {"name": "Chile",              "lat": -35.68,"lon": -71.54, "iso": "CHL", "region": "South America"},
    {"name": "China",              "lat": 35.86, "lon": 104.2,  "iso": "CHN", "region": "Asia"},
    {"name": "Colombia",           "lat": 4.57,  "lon": -74.3,  "iso": "COL", "region": "South America"},
    {"name": "Congo",              "lat": -0.23, "lon": 15.83,  "iso": "COG", "region": "Africa"},
    {"name": "Costa Rica",         "lat": 9.75,  "lon": -83.75, "iso": "CRI", "region": "North America"},
    {"name": "Croatia",            "lat": 45.1,  "lon": 15.2,   "iso": "HRV", "region": "Europe"},
    {"name": "Cuba",               "lat": 21.52, "lon": -77.78, "iso": "CUB", "region": "North America"},
    {"name": "Czech Rep.",         "lat": 49.82, "lon": 15.47,  "iso": "CZE", "region": "Europe"},
    {"name": "Dem. Rep. Congo",    "lat": -4.04, "lon": 21.76,  "iso": "COD", "region": "Africa"},
    {"name": "Denmark",            "lat": 56.26, "lon": 9.5,    "iso": "DNK", "region": "Europe"},
    {"name": "Dominican Rep.",     "lat": 18.74, "lon": -70.16, "iso": "DOM", "region": "North America"},
    {"name": "Ecuador",            "lat": -1.83, "lon": -78.18, "iso": "ECU", "region": "South America"},
    {"name": "Egypt",              "lat": 26.82, "lon": 30.8,   "iso": "EGY", "region": "Africa"},
    {"name": "El Salvador",        "lat": 13.79, "lon": -88.9,  "iso": "SLV", "region": "North America"},
    {"name": "Ethiopia",           "lat": 9.15,  "lon": 40.49,  "iso": "ETH", "region": "Africa"},
    {"name": "Finland",            "lat": 61.92, "lon": 25.75,  "iso": "FIN", "region": "Europe"},
    {"name": "France",             "lat": 46.23, "lon": 2.21,   "iso": "FRA", "region": "Europe"},
    {"name": "Gabon",              "lat": -0.8,  "lon": 11.61,  "iso": "GAB", "region": "Africa"},
    {"name": "Gambia",             "lat": 13.44, "lon": -15.31, "iso": "GMB", "region": "Africa"},
    {"name": "Germany",            "lat": 51.17, "lon": 10.45,  "iso": "DEU", "region": "Europe"},
    {"name": "Ghana",              "lat": 7.95,  "lon": -1.02,  "iso": "GHA", "region": "Africa"},
    {"name": "Greece",             "lat": 39.07, "lon": 21.82,  "iso": "GRC", "region": "Europe"},
    {"name": "Guatemala",          "lat": 15.78, "lon": -90.23, "iso": "GTM", "region": "North America"},
    {"name": "Guinea",             "lat": 9.95,  "lon": -11.86, "iso": "GIN", "region": "Africa"},
    {"name": "Haiti",              "lat": 18.97, "lon": -72.29, "iso": "HTI", "region": "North America"},
    {"name": "Honduras",           "lat": 15.2,  "lon": -86.24, "iso": "HND", "region": "North America"},
    {"name": "Hungary",            "lat": 47.16, "lon": 19.5,   "iso": "HUN", "region": "Europe"},
    {"name": "India",              "lat": 20.59, "lon": 78.96,  "iso": "IND", "region": "Asia"},
    {"name": "Indonesia",          "lat": -0.79, "lon": 113.92, "iso": "IDN", "region": "Asia"},
    {"name": "Iran",               "lat": 32.43, "lon": 53.69,  "iso": "IRN", "region": "Asia"},
    {"name": "Iraq",               "lat": 33.22, "lon": 43.68,  "iso": "IRQ", "region": "Asia"},
    {"name": "Ireland",            "lat": 53.14, "lon": -7.69,  "iso": "IRL", "region": "Europe"},
    {"name": "Israel",             "lat": 31.05, "lon": 34.85,  "iso": "ISR", "region": "Asia"},
    {"name": "Italy",              "lat": 41.87, "lon": 12.57,  "iso": "ITA", "region": "Europe"},
    {"name": "Ivory Coast",        "lat": 7.54,  "lon": -5.55,  "iso": "CIV", "region": "Africa"},
    {"name": "Jamaica",            "lat": 18.11, "lon": -77.3,  "iso": "JAM", "region": "North America"},
    {"name": "Japan",              "lat": 36.2,  "lon": 138.25, "iso": "JPN", "region": "Asia"},
    {"name": "Jordan",             "lat": 30.59, "lon": 36.24,  "iso": "JOR", "region": "Asia"},
    {"name": "Kazakhstan",         "lat": 48.02, "lon": 66.92,  "iso": "KAZ", "region": "Asia"},
    {"name": "Kenya",              "lat": -0.02, "lon": 37.91,  "iso": "KEN", "region": "Africa"},
    {"name": "Kuwait",             "lat": 29.31, "lon": 47.48,  "iso": "KWT", "region": "Asia"},
    {"name": "Kyrgyzstan",         "lat": 41.2,  "lon": 74.77,  "iso": "KGZ", "region": "Asia"},
    {"name": "Laos",               "lat": 19.86, "lon": 102.5,  "iso": "LAO", "region": "Asia"},
    {"name": "Lebanon",            "lat": 33.85, "lon": 35.86,  "iso": "LBN", "region": "Asia"},
    {"name": "Liberia",            "lat": 6.43,  "lon": -9.43,  "iso": "LBR", "region": "Africa"},
    {"name": "Libya",              "lat": 26.34, "lon": 17.23,  "iso": "LBY", "region": "Africa"},
    {"name": "Madagascar",         "lat": -18.77,"lon": 46.87,  "iso": "MDG", "region": "Africa"},
    {"name": "Malawi",             "lat": -13.25,"lon": 34.3,   "iso": "MWI", "region": "Africa"},
    {"name": "Malaysia",           "lat": 4.21,  "lon": 101.98, "iso": "MYS", "region": "Asia"},
    {"name": "Mali",               "lat": 17.57, "lon": -3.99,  "iso": "MLI", "region": "Africa"},
    {"name": "Mexico",             "lat": 23.63, "lon": -102.55,"iso": "MEX", "region": "North America"},
    {"name": "Mongolia",           "lat": 46.86, "lon": 103.85, "iso": "MNG", "region": "Asia"},
    {"name": "Morocco",            "lat": 31.79, "lon": -7.09,  "iso": "MAR", "region": "Africa"},
    {"name": "Mozambique",         "lat": -18.67,"lon": 35.53,  "iso": "MOZ", "region": "Africa"},
    {"name": "Myanmar",            "lat": 21.91, "lon": 95.96,  "iso": "MMR", "region": "Asia"},
    {"name": "Namibia",            "lat": -22.96,"lon": 18.49,  "iso": "NAM", "region": "Africa"},
    {"name": "Nepal",              "lat": 28.39, "lon": 84.12,  "iso": "NPL", "region": "Asia"},
    {"name": "Netherlands",        "lat": 52.13, "lon": 5.29,   "iso": "NLD", "region": "Europe"},
    {"name": "New Zealand",        "lat": -40.9,"lon": 174.89,  "iso": "NZL", "region": "Oceania"},
    {"name": "Nicaragua",          "lat": 12.87, "lon": -85.21, "iso": "NIC", "region": "North America"},
    {"name": "Niger",              "lat": 17.61, "lon": 8.08,   "iso": "NER", "region": "Africa"},
    {"name": "Nigeria",            "lat": 9.08,  "lon": 8.68,   "iso": "NGA", "region": "Africa"},
    {"name": "North Korea",        "lat": 40.34, "lon": 127.51, "iso": "PRK", "region": "Asia"},
    {"name": "Norway",             "lat": 60.47, "lon": 8.47,   "iso": "NOR", "region": "Europe"},
    {"name": "Oman",               "lat": 21.47, "lon": 55.98,  "iso": "OMN", "region": "Asia"},
    {"name": "Pakistan",           "lat": 30.38, "lon": 69.35,  "iso": "PAK", "region": "Asia"},
    {"name": "Panama",             "lat": 8.54,  "lon": -80.78, "iso": "PAN", "region": "North America"},
    {"name": "Papua New Guinea",   "lat": -6.31, "lon": 143.96, "iso": "PNG", "region": "Oceania"},
    {"name": "Paraguay",           "lat": -23.44,"lon": -58.44, "iso": "PRY", "region": "South America"},
    {"name": "Peru",               "lat": -9.19, "lon": -75.02, "iso": "PER", "region": "South America"},
    {"name": "Philippines",        "lat": 12.88, "lon": 121.77, "iso": "PHL", "region": "Asia"},
    {"name": "Poland",             "lat": 51.92, "lon": 19.15,  "iso": "POL", "region": "Europe"},
    {"name": "Portugal",           "lat": 39.4,  "lon": -8.22,  "iso": "PRT", "region": "Europe"},
    {"name": "Romania",            "lat": 45.94, "lon": 24.97,  "iso": "ROU", "region": "Europe"},
    {"name": "Russia",             "lat": 61.52, "lon": 105.32, "iso": "RUS", "region": "Europe"},
    {"name": "Rwanda",             "lat": -1.94, "lon": 29.87,  "iso": "RWA", "region": "Africa"},
    {"name": "Saudi Arabia",       "lat": 23.89, "lon": 45.08,  "iso": "SAU", "region": "Asia"},
    {"name": "Senegal",            "lat": 14.5,  "lon": -14.45, "iso": "SEN", "region": "Africa"},
    {"name": "Serbia",             "lat": 44.02, "lon": 21.01,  "iso": "SRB", "region": "Europe"},
    {"name": "Sierra Leone",       "lat": 8.46,  "lon": -11.78, "iso": "SLE", "region": "Africa"},
    {"name": "Singapore",          "lat": 1.35,  "lon": 103.82, "iso": "SGP", "region": "Asia"},
    {"name": "Slovakia",           "lat": 48.67, "lon": 19.7,   "iso": "SVK", "region": "Europe"},
    {"name": "Slovenia",           "lat": 46.15, "lon": 14.99,  "iso": "SVN", "region": "Europe"},
    {"name": "Somalia",            "lat": 5.15,  "lon": 46.2,   "iso": "SOM", "region": "Africa"},
    {"name": "South Africa",       "lat": -30.56,"lon": 22.94,  "iso": "ZAF", "region": "Africa"},
    {"name": "South Korea",        "lat": 35.91, "lon": 127.77, "iso": "KOR", "region": "Asia"},
    {"name": "Spain",              "lat": 40.46, "lon": -3.75,  "iso": "ESP", "region": "Europe"},
    {"name": "Sri Lanka",          "lat": 7.87,  "lon": 80.77,  "iso": "LKA", "region": "Asia"},
    {"name": "Sudan",              "lat": 12.86, "lon": 30.22,  "iso": "SDN", "region": "Africa"},
    {"name": "Sweden",             "lat": 60.13, "lon": 18.64,  "iso": "SWE", "region": "Europe"},
    {"name": "Switzerland",        "lat": 46.82, "lon": 8.23,   "iso": "CHE", "region": "Europe"},
    {"name": "Syria",              "lat": 34.8,  "lon": 38.99,  "iso": "SYR", "region": "Asia"},
    {"name": "Taiwan",             "lat": 23.7,  "lon": 120.96, "iso": "TWN", "region": "Asia"},
    {"name": "Tanzania",           "lat": -6.37, "lon": 34.89,  "iso": "TZA", "region": "Africa"},
    {"name": "Thailand",           "lat": 15.87, "lon": 100.99, "iso": "THA", "region": "Asia"},
    {"name": "Tunisia",            "lat": 33.89, "lon": 9.54,   "iso": "TUN", "region": "Africa"},
    {"name": "Turkey",             "lat": 38.96, "lon": 35.24,  "iso": "TUR", "region": "Europe"},
    {"name": "Uganda",             "lat": 1.37,  "lon": 32.29,  "iso": "UGA", "region": "Africa"},
    {"name": "Ukraine",            "lat": 48.38, "lon": 31.17,  "iso": "UKR", "region": "Europe"},
    {"name": "United Arab Emirates","lat": 23.42,"lon": 53.85,  "iso": "ARE", "region": "Asia"},
    {"name": "United Kingdom",     "lat": 55.38, "lon": -3.44,  "iso": "GBR", "region": "Europe"},
    {"name": "United States",      "lat": 37.09, "lon": -95.71, "iso": "USA", "region": "North America"},
    {"name": "Uruguay",            "lat": -32.52,"lon": -55.77, "iso": "URY", "region": "South America"},
    {"name": "Uzbekistan",         "lat": 41.38, "lon": 64.59,  "iso": "UZB", "region": "Asia"},
    {"name": "Venezuela",          "lat": 6.42,  "lon": -66.59, "iso": "VEN", "region": "South America"},
    {"name": "Vietnam",            "lat": 14.06, "lon": 108.28, "iso": "VNM", "region": "Asia"},
    {"name": "Yemen",              "lat": 15.55, "lon": 48.52,  "iso": "YEM", "region": "Asia"},
    {"name": "Zambia",             "lat": -13.13,"lon": 27.85,  "iso": "ZMB", "region": "Africa"},
    {"name": "Zimbabwe",           "lat": -19.02,"lon": 29.15,  "iso": "ZWE", "region": "Africa"},
]

COUNTRY_INDEX = {c["name"]: c for c in COUNTRIES}
print(f"Loaded {len(COUNTRIES)} countries.")
Loaded 133 countries.

Interactive Globe

Select countries from the dropdown, set hours with the slider, and pick a travel mode.

In [4]:
Expand CodeCollapse Code
MODE_COLORS = {
    "Walking":       "rgba(46, 204, 113, 0.25)",
    "Cycling":       "rgba(52, 152, 219, 0.25)",
    "Driving":       "rgba(231, 76, 60, 0.25)",
    "Public Transit": "rgba(155, 89, 182, 0.25)",
}

MODE_LINE_COLORS = {
    "Walking":       "rgba(46, 204, 113, 0.8)",
    "Cycling":       "rgba(52, 152, 219, 0.8)",
    "Driving":       "rgba(231, 76, 60, 0.8)",
    "Public Transit": "rgba(155, 89, 182, 0.8)",
}

def make_circle_points(center_lat, center_lon, radius_km, n=120):
    """Generate polygon points for a circle on the globe."""
    R = 6371.0
    angular_radius = radius_km / R
    center_lat_rad = np.radians(center_lat)
    center_lon_rad = np.radians(center_lon)
    angles = np.linspace(0, 2 * np.pi, n)
    lats = []
    lons = []
    for a in angles:
        lat_rad = np.arcsin(
            np.sin(center_lat_rad) * np.cos(angular_radius)
            + np.cos(center_lat_rad) * np.sin(angular_radius) * np.cos(a)
        )
        lon_rad = center_lon_rad + np.arctan2(
            np.sin(a) * np.sin(angular_radius) * np.cos(center_lat_rad),
            np.cos(angular_radius) - np.sin(center_lat_rad) * np.sin(lat_rad)
        )
        lats.append(np.degrees(lat_rad))
        lons.append(np.degrees(lon_rad))
    lats.append(lats[0])
    lons.append(lons[0])
    return lats, lons


def build_figure(selected_countries, hours, mode):
    fig = go.Figure()

    for name in selected_countries:
        c = COUNTRY_INDEX[name]
        speed = get_speed(mode, name)
        radius_km = speed * hours
        lats, lons = make_circle_points(c["lat"], c["lon"], radius_km)

        fig.add_trace(go.Scattergeo(
            lat=lats, lon=lons,
            mode="lines",
            line=dict(width=1.5, color=MODE_LINE_COLORS[mode]),
            fill="toself",
            fillcolor=MODE_COLORS[mode],
            name=f"{name} ({radius_km:.0f} km)",
            hoverinfo="name",
            showlegend=True,
        ))

        fig.add_trace(go.Scattergeo(
            lat=[c["lat"]], lon=[c["lon"]],
            mode="markers+text",
            marker=dict(size=8, color=MODE_LINE_COLORS[mode], symbol="star"),
            text=[name],
            textposition="top center",
            textfont=dict(size=10, color="white"),
            hoverinfo="text",
            hovertext=f"{name}<br>{speed:.1f} km/h<br>{radius_km:.0f} km in {hours}h",
            showlegend=False,
        ))

    fig.update_geos(
        projection_type="orthographic",
        showcoastlines=True,
        coastlinecolor="rgba(255,255,255,0.3)",
        showland=True,
        landcolor="rgb(40, 40, 55)",
        showocean=True,
        oceancolor="rgb(20, 25, 40)",
        showlakes=True,
        lakecolor="rgb(20, 25, 40)",
        showcountries=True,
        countrycolor="rgba(255,255,255,0.15)",
        bgcolor="rgba(0,0,0,0)",
    )

    fig.update_layout(
        title=dict(
            text=f"How Far Can You Get in {hours} Hour{'s' if hours != 1 else ''} by {mode}?",
            font=dict(size=18, color="white"),
            x=0.5,
        ),
        paper_bgcolor="rgb(18, 18, 28)",
        plot_bgcolor="rgb(18, 18, 28)",
        font=dict(color="white"),
        legend=dict(
            font=dict(size=11),
            bgcolor="rgba(0,0,0,0.3)",
            bordercolor="rgba(255,255,255,0.1)",
            borderwidth=1,
            x=0.01, y=0.99,
        ),
        margin=dict(l=0, r=0, t=50, b=0),
        height=620,
    )
    return fig


country_names = [c["name"] for c in sorted(COUNTRIES, key=lambda x: x["name"])]

selected_dd = widgets.SelectMultiple(
    options=country_names,
    value=["United States", "Japan", "Germany", "Brazil", "Nigeria", "Australia", "India"],
    rows=8,
    description="Countries",
    layout=widgets.Layout(width="240px"),
)

hours_slider = widgets.IntSlider(
    value=4, min=1, max=48, step=1,
    description="Hours",
    style={"description_width": "50px"},
    layout=widgets.Layout(width="400px"),
    continuous_update=False,
)
hours_label = widgets.HTML(value="<b style='font-size:16px'>4 hours</b>")

mode_buttons = widgets.ToggleButtons(
    options=["Walking", "Cycling", "Driving", "Public Transit"],
    value="Driving",
    description="Mode",
    style={"description_width": "40px"},
    button_style="",
    tooltips=[
        "~5 km/h",
        "~15 km/h",
        "~80 km/h",
        "~40 km/h",
    ],
)

info_html = widgets.HTML()


def update_hours_label(change):
    h = change["new"]
    hours_label.value = f"<b style='font-size:16px'>{h} hour{'s' if h != 1 else ''}</b>"

hours_slider.observe(update_hours_label, names="value")


output = widgets.Output()


def on_change(change=None):
    with output:
        output.clear_output(wait=True)
        sel = list(selected_dd.value)
        hours = hours_slider.value
        mode = mode_buttons.value
        fig = build_figure(sel, hours, mode)
        fig.show()
        lines = []
        for name in sel:
            c = COUNTRY_INDEX[name]
            speed = get_speed(mode, name)
            dist = speed * hours
            lines.append(
                f"<div style='padding:4px 8px;margin:2px 0;border-radius:6px;"
                f"background:rgba(255,255,255,0.05);font-size:13px'>"
                f"<b>{name}</b>: {speed:.1f} km/h &rarr; <b>{dist:.0f} km</b> in {hours}h</div>"
            )
        info_html.value = "<div style='max-height:200px;overflow-y:auto;padding:8px;'>" + "".join(lines) + "</div>"

selected_dd.observe(on_change, names="value")
hours_slider.observe(on_change, names="value")
mode_buttons.observe(on_change, names="value")


header = widgets.HTML(
    value="<h2 style='color:white;margin:0'>How Far Can You Get?</h2>"
    "<p style='color:rgba(255,255,255,0.5);margin:4px 0 0 0;font-size:13px'>"
    "Select countries, set time, pick a mode</p>"
)

controls = widgets.VBox([
    header,
    widgets.HBox([
        widgets.VBox([selected_dd], layout=widgets.Layout(align_items="flex-start")),
        widgets.VBox([
            mode_buttons,
            hours_slider,
            hours_label,
            info_html,
        ], layout=widgets.Layout(padding="0 0 0 16px")),
    ]),
], layout=widgets.Layout(
    padding="16px",
    background_color="rgb(28, 28, 42)",
    border_radius="12px",
    margin="0 0 8px 0",
))

display(controls)
display(output)

on_change()
VBox(children=(HTML(value="<h2 style='color:white;margin:0'>How Far Can You Get?</h2><p style='color:rgba(255,…
Output()