      const sample = `$RMDTL,""
$RMCA,1777168430000
$A,"20639","38",20639,"Bowen","Mayhue","N/A",1
$B,1,"Race Outlaw Late Model--Qualifying 1-09:52PM"
$C,1,"Outlaw Late Model--Qualifying 1"
$COMP,"20639","38",1,"Bowen","Mayhue","N/A"," "
$G,1,"20639",2,"00:00:34.466"
$H,1,"20639",2,"00:00:16.989"
$J,"20639","00:00:16.989","00:00:34.466"
$RMS,"race"
$E,"TRACKNAME","Cordele Motor Speedway"
$F,6,"00:00:00","21:53:51","00:00:00"," "`;

      const messages = document.querySelector("#messages");
      const preview = document.querySelector("#preview");
      const meta = document.querySelector("#meta");
      const timingStatus = document.querySelector("#timingStatus");
      const timingSource = document.querySelector("#timingSource");
      const timingUrl = document.querySelector("#timingUrl");
      const programStatus = document.querySelector("#programStatus");
      const heroRaceName = document.querySelector("#heroRaceName");
      const heroRaceDetail = document.querySelector("#heroRaceDetail");
      const heroFlagLabel = document.querySelector("#heroFlagLabel");
      const heroLapsToGo = document.querySelector("#heroLapsToGo");
      const heroLocalTime = document.querySelector("#heroLocalTime");
      const heroLocalZone = document.querySelector("#heroLocalZone");
      const heroRaceReady = document.querySelector("#heroRaceReady");
      const heroReadyState = document.querySelector("#heroReadyState");
      const heroReadyDetail = document.querySelector("#heroReadyDetail");
      const vmixUrl = document.querySelector("#vmixUrl");
      const vmixInput = document.querySelector("#vmixInput");
      const topBannerInput = document.querySelector("#topBannerInput");
      const overlayBaseUrl = document.querySelector("#overlayBaseUrl");
      const vmixProgramUrl = document.querySelector("#vmixProgramUrl");
      const vmixTopBannerUrl = document.querySelector("#vmixTopBannerUrl");
      const programOverlayLink = document.querySelector("#programOverlayLink");
      const topBannerOverlayLink = document.querySelector("#topBannerOverlayLink");
      const footerSource = document.querySelector("#footerSource");
      const manualSession = document.querySelector("#manualSession");
      const missingPlaceholder = document.querySelector("#missingPlaceholder");
      const correctionList = document.querySelector("#correctionList");
      const liveHealthSummary = document.querySelector("#liveHealthSummary");
      const liveHealthStrip = document.querySelector("#liveHealthStrip");
      const raceReadySummary = document.querySelector("#raceReadySummary");
      const raceReadyAction = document.querySelector("#raceReadyAction");
      const raceReadyGrid = document.querySelector("#raceReadyGrid");
      const raceReadyIssues = document.querySelector("#raceReadyIssues");
      const raceReadyDetailsSummary = document.querySelector("#raceReadyDetailsSummary");
      const raceIntelStrip = document.querySelector("#raceIntelStrip");
      const raceStoryPanel = document.querySelector("#storyPanel");
      const raceStoryPreview = document.querySelector("#raceStoryPreview");
      const raceStoryAlert = document.querySelector("#raceStoryAlert");
      const raceStoryAutoUpdate = document.querySelector("#raceStoryAutoUpdate");
      const raceStorySound = document.querySelector("#raceStorySound");
      const raceStoryOutputButton = document.querySelector("[data-preview='story']");
      const sendRaceStoryButton = document.querySelector("#sendRaceStory");
      const hideRaceStoryButton = document.querySelector("#hideRaceStory");
      const raceStoryAutoBestButton = document.querySelector("#raceStoryAutoBest");
      const raceStoryChoiceList = document.querySelector("#raceStoryChoiceList");
      const raceStorySelectionStatus = document.querySelector("#raceStorySelectionStatus");
      const raceStoryTimingStatus = document.querySelector("#raceStoryTimingStatus");
      const raceStoryCueStatus = document.querySelector("#raceStoryCueStatus");
      const raceStoryOutputStatus = document.querySelector("#raceStoryOutputStatus");
      const raceStoryHistoryList = document.querySelector("#raceStoryHistoryList");
      const clearRaceStoryHistoryButton = document.querySelector("#clearRaceStoryHistory");
      const sendReadinessNote = document.querySelector("#sendReadinessNote");
      const bannerLogoStatus = document.querySelector("#bannerLogoStatus");
      const liveControlLock = document.querySelector("#liveControlLock");
      const takeProgramButton = document.querySelector("#takeProgram");
      const clearProgramButton = document.querySelector("#clearProgram");
      const resetLiveStateButton = document.querySelector("#resetLiveState");
      const sendTopBannerButton = document.querySelector("#sendTopBanner");
      const clearTopBannerButton = document.querySelector("#clearTopBanner");
      const toggleBannerData = document.querySelector("#toggleBannerData");
      const raceReadyDetails = document.querySelector(".race-ready-details");
      const bannerTopFiveOpen = document.querySelector("#bannerTopFiveOpen");
      const bannerMetricFields = [...document.querySelectorAll("[name='bannerMetricFields']")];
      const bannerModePresetButtons = [...document.querySelectorAll("[data-banner-mode-preset]")];
      const dataPanel = document.querySelector(".data-panel");
      const stylePanel = document.querySelector(".style-panel");
      const upperLeftStack = document.querySelector(".upper-left-stack");
      const raceDayModeButton = document.querySelector("#raceDayMode");
      const setupModeButton = document.querySelector("#setupMode");
      const outputPreviewGraphic = document.querySelector("#outputPreviewGraphic");
      const outputPreviewDetail = document.querySelector("#outputPreviewDetail");
      const outputLiveGraphic = document.querySelector("#outputLiveGraphic");
      const outputLiveDetail = document.querySelector("#outputLiveDetail");
      const outputBannerStatus = document.querySelector("#outputBannerStatus");
      const outputBannerDetail = document.querySelector("#outputBannerDetail");
      const outputPreviewFrame = document.querySelector("#outputPreviewFrame");
      const outputPreviewFrameLabel = document.querySelector("#outputPreviewFrameLabel");
      const outputPreviewFrameEmpty = document.querySelector("#outputPreviewFrameEmpty");
      const graphicPreviewFrames = [...document.querySelectorAll(".js-graphic-preview-frame")];
      const broadcastPreviewFrames = [...document.querySelectorAll(".graphic-preview-frame.is-broadcast-preview")];
      const TEMPORARY_BANNER_COMPANION_GRAPHICS = new Set(["leaders", "story", "weather"]);
      let temporaryOutputTimer = null;
      const bannerPreviewTemplate = document.querySelector("#bannerPreviewTemplate");
      const bannerPreviewDetail = document.querySelector("#bannerPreviewDetail");
      const bannerPreviewData = document.querySelector("#bannerPreviewData");
      const bannerPreviewMode = document.querySelector("#bannerPreviewMode");
      const weatherPreviewLocation = document.querySelector("#weatherPreviewLocation");
      const weatherPreviewDetail = document.querySelector("#weatherPreviewDetail");
      const tickerPreviewSource = document.querySelector("#tickerPreviewSource");
      const tickerPreviewDetail = document.querySelector("#tickerPreviewDetail");
      const weatherMiniHeaderLocation = document.querySelector("#weatherMiniHeaderLocation");
      const weatherMiniIcon = document.querySelector("#weatherMiniIcon");
      const weatherMiniLocation = document.querySelector("#weatherMiniLocation");
      const weatherMiniTemp = document.querySelector("#weatherMiniTemp");
      const weatherMiniCondition = document.querySelector("#weatherMiniCondition");
      const weatherMiniFeels = document.querySelector("#weatherMiniFeels");
      const weatherMiniWind = document.querySelector("#weatherMiniWind");
      const weatherMiniHumidity = document.querySelector("#weatherMiniHumidity");
      const weatherMiniRain = document.querySelector("#weatherMiniRain");
      const tickerMiniPreviewTrack = document.querySelector("#tickerMiniPreviewTrack");
      const top3PreviewTitle = document.querySelector("#top3PreviewTitle");
      const top3PreviewDetail = document.querySelector("#top3PreviewDetail");
      const stylePresetButtons = [...document.querySelectorAll("[data-style-preset]")];
      let previewGraphic = "blank";
      let programGraphic = "blank";
      let topBannerVisible = true;
      let latestRaceReadyPayload = null;
      let latestTimingState = null;
      let latestRaceReadyStatus = { tone: "warn", blocked: [], warnings: [], primary: null };
      let networkInfo = null;
      let readyPageStatus = { checkedAt: 0, program: null, topBanner: null, control: null };
      let liveControlsLocked = localStorage.getItem("raceGraphics.liveControlsLocked") === "true";
      let pendingLiveControl = null;
      let raceStoryLastReadyKey = "";
      let raceStoryAudioContext = null;
      let raceStoryLastSoundAt = 0;
      let raceStoryHistory = [];
      let activeControlPage = "";
      let latestWeatherPreviewData = null;
      let weatherPreviewSignature = "";
      let weatherPreviewFetchInFlight = false;
      let weatherPreviewLastFetchAt = 0;
      let previewStyleKey = "";
      const programChannel = window.BroadcastChannel ? new BroadcastChannel("raceGraphics.program") : null;
      const sourceUrlKeys = {
        racemonitor: "raceGraphics.lastRaceMonitorUrl",
        myracepass: "raceGraphics.lastMyRacePassUrl"
      };

      const vmixSettings = {
        url: "raceGraphics.vmixUrl",
        input: "raceGraphics.vmixInput",
        topBannerInput: "raceGraphics.topBannerInput",
        overlayBase: "raceGraphics.overlayBaseUrl"
      };

      if (raceStorySound) {
        raceStorySound.checked = localStorage.getItem("raceGraphics.raceStorySound") !== "false";
        raceStorySound.addEventListener("change", () => {
          localStorage.setItem("raceGraphics.raceStorySound", raceStorySound.checked ? "true" : "false");
        });
      }

      loadRaceStoryHistory();
      if (clearRaceStoryHistoryButton) {
        clearRaceStoryHistoryButton.addEventListener("click", () => {
          raceStoryHistory = [];
          saveRaceStoryHistory();
          renderRaceStoryHistory();
        });
      }

      const controlPageIds = [
        "timingPanel",
        "outputPanel",
        "storyPanel",
        "bannerPanel",
        "weatherTickerPanel",
        "podiumPanel",
        "stylePanel"
      ];
      const setupOnlyControlPages = new Set(["podiumPanel", "stylePanel"]);
      const controlPageGroups = {
        stylePanel: ["stylePanel", "dataPanel", "advancedPanel", "setupToolsPanel"]
      };
      const controlPageLinks = [...document.querySelectorAll(".control-quick-nav a")];
      const controlPageTargets = [...document.querySelectorAll("[data-control-page-panel]")];

      function isSetupOnlyPage(pageId) {
        return setupOnlyControlPages.has(pageId);
      }

      function pageIdFromLocation() {
        const pageId = String(location.hash || "").replace(/^#/, "");
        return controlPageIds.includes(pageId) ? pageId : "";
      }

      function pageGroupFor(pageId) {
        return controlPageGroups[pageId] || [pageId];
      }

      function resizeBroadcastPreviewFrames() {
        broadcastPreviewFrames.forEach(wrapper => {
          const baseWidth = Number(wrapper.dataset.previewWidth) || 1920;
          const baseHeight = Number(wrapper.dataset.previewHeight) || 1080;
          const cropHeight = Number(wrapper.dataset.previewCropHeight) || baseHeight;
          const visibleHeight = Math.min(baseHeight, Math.max(1, cropHeight));
          const availableWidth = wrapper.clientWidth || wrapper.getBoundingClientRect().width || wrapper.parentElement?.clientWidth || 820;
          if (!availableWidth || !baseWidth || !baseHeight) return;
          const scale = Math.min(availableWidth / baseWidth, 1);
          const offsetY = wrapper.dataset.previewFocus === "bottom" ? -(baseHeight - visibleHeight) : 0;
          wrapper.style.setProperty("--preview-width", `${baseWidth}px`);
          wrapper.style.setProperty("--preview-height", `${baseHeight}px`);
          wrapper.style.setProperty("--preview-scale", String(scale));
          wrapper.style.setProperty("--preview-offset-y", `${Math.round(offsetY * scale)}px`);
          wrapper.style.height = `${Math.max(80, Math.ceil(visibleHeight * scale))}px`;
        });
      }

      function setActiveControlPage(pageId, options = {}) {
        document.body.classList.add("is-paged-control");
        closeRaceReadyDetails();
        let nextPage = controlPageIds.includes(pageId) ? pageId : "timingPanel";
        if (document.body.classList.contains("is-race-day-mode") && isSetupOnlyPage(nextPage)) {
          nextPage = "timingPanel";
        }
        activeControlPage = nextPage;
        document.body.dataset.activeControlPage = nextPage;
        const activeGroup = new Set(pageGroupFor(nextPage));
        controlPageTargets.forEach(target => {
          const key = target.id || "";
          const isActive = activeGroup.has(key);
          target.classList.toggle("is-current-control-page", isActive);
          target.setAttribute("aria-hidden", isActive ? "false" : "true");
        });
        controlPageLinks.forEach(link => {
          const targetId = (link.getAttribute("href") || "").replace(/^#/, "");
          const isActive = targetId === nextPage;
          link.classList.remove("active", "is-active");
          link.classList.toggle("is-active", isActive);
          link.setAttribute("aria-selected", isActive ? "true" : "false");
          link.setAttribute("role", "tab");
          if (!isActive) link.blur();
        });
        if (document.activeElement?.matches?.(".control-quick-nav a[aria-selected='false']")) {
          document.activeElement.blur();
        }
        const nextHash = `#${nextPage}`;
        if (options.pushHash && location.hash !== nextHash) {
          history.pushState(null, "", nextHash);
        } else if (options.replaceHash && location.hash !== nextHash) {
          history.replaceState(null, "", nextHash);
        }
        loadActiveGraphicPreviews(nextPage);
        requestAnimationFrame(resizeBroadcastPreviewFrames);
        setTimeout(resizeBroadcastPreviewFrames, 150);
        requestAnimationFrame(syncDataPanelHeight);
      }

      function setControlMode(mode) {
        closeRaceReadyDetails();
        const nextMode = mode === "setup" ? "setup" : "race";
        document.body.classList.toggle("is-race-day-mode", nextMode === "race");
        document.body.classList.toggle("is-setup-mode", nextMode === "setup");
        raceDayModeButton.classList.toggle("active", nextMode === "race");
        setupModeButton.classList.toggle("active", nextMode === "setup");
        raceDayModeButton.classList.toggle("secondary", nextMode !== "race");
        setupModeButton.classList.toggle("secondary", nextMode !== "setup");
        try {
          localStorage.setItem("raceGraphics.controlMode", nextMode);
        } catch (_err) {}
        setActiveControlPage(activeControlPage || pageIdFromLocation() || "timingPanel", { replaceHash: true });
        requestAnimationFrame(syncDataPanelHeight);
      }

      function updateControlHeader(state) {
        const metaState = (state && state.meta) || {};
        const raceName = metaState.className || metaState.session || "No race selected";
        const raceDetail = metaState.session && metaState.session !== raceName
          ? metaState.session
          : metaState.trackName || "Waiting for timing";
        const flagText = metaState.flagLabel || metaState.flag || metaState.flagCode || "Flag";
        const lapsText = metaState.lapsToGo || "--";
        if (heroRaceName) heroRaceName.textContent = raceName;
        if (heroRaceDetail) heroRaceDetail.textContent = raceDetail;
        if (heroFlagLabel) heroFlagLabel.textContent = flagText;
        if (heroLapsToGo) heroLapsToGo.textContent = lapsText;
      }

      function updateHeroLocalTime() {
        const now = new Date();
        if (heroLocalTime) heroLocalTime.textContent = now.toLocaleTimeString([], { hour: "numeric", minute: "2-digit", second: "2-digit" });
        if (heroLocalZone) {
          const zone = Intl.DateTimeFormat().resolvedOptions().timeZone || "Local";
          heroLocalZone.textContent = zone.split("/").pop().replace(/_/g, " ");
        }
      }

      function updateHeroRaceReady(tone = "warn", blocked = [], warnings = []) {
        if (!heroRaceReady) return;
        const normalized = tone === "bad" ? "bad" : tone === "good" || tone === "live" ? "good" : "warn";
        heroRaceReady.classList.remove("is-good", "is-warn", "is-bad", "is-live", "is-idle");
        heroRaceReady.classList.add(`is-${normalized}`);
        if (heroReadyState) {
          heroReadyState.textContent = normalized === "good" ? "Ready" : normalized === "bad" ? "Not Ready" : "Review";
        }
        if (heroReadyDetail) {
          const primary = blocked[0] || warnings[0];
          heroReadyDetail.textContent = normalized === "good"
            ? "All systems online"
            : primary
              ? primary.detail || primary.label || "Check details"
              : "Check details";
        }
      }

      function graphicDisplayName(graphic) {
        const names = {
          blank: "Blank",
          topbanner: "Top Banner",
          sidebanner: "Side Tower",
          podium: "Top 3",
          lineup: "Starting Lineup",
          leaders: "Current Leaders",
          top5: "Top 5",
          results: "Race Results",
          story: "Race Story",
          weather: "Weather",
          ticker: "Lower Ticker"
        };
        return names[graphic] || displayText(graphic, "Blank");
      }

      function graphicPreviewUrl(graphic) {
        const urls = {
          topbanner: "/top-banner.html?preview=1",
          sidebanner: "/side-banner.html?preview=1",
          podium: "/top3-podium.html?preview=1",
          lineup: "/board.html?mode=lineup&preview=1",
          leaders: "/current-leaders.html?preview=1",
          top5: "/results.html?preview=1",
          results: "/board.html?mode=results&preview=1",
          story: "/story-spotlight.html?preview=1",
          weather: "/weather.html?preview=1",
          ticker: "/ticker.html?preview=1"
        };
        const baseUrl = urls[graphic] || "";
        if (!baseUrl) return "";
        const separator = baseUrl.includes("?") ? "&" : "?";
        return `${baseUrl}${separator}previewKey=${encodeURIComponent(previewStyleKey || "default")}`;
      }

      function previewUrlWithVersion(url, version = "") {
        const cleanUrl = displayText(url);
        if (!cleanUrl) return "";
        // No timestamp by default: a fresh previewVersion on every render forced
        // the preview iframes to reload ~1x/second. Style changes already reload
        // frames through previewKey, and preview pages stream their own live data.
        if (!version) return cleanUrl;
        const separator = cleanUrl.includes("?") ? "&" : "?";
        return `${cleanUrl}${separator}previewVersion=${encodeURIComponent(version)}`;
      }

      function setGraphicPreviewFrame(frame, url) {
        if (!frame) return;
        const nextUrl = displayText(url);
        const wrapper = frame.closest(".graphic-preview-frame");
        if (!nextUrl) {
          if (frame.getAttribute("src") !== "about:blank") frame.setAttribute("src", "about:blank");
          if (wrapper) wrapper.classList.remove("has-preview");
          return;
        }
        if (frame.getAttribute("src") !== nextUrl) {
          frame.setAttribute("src", nextUrl);
        }
        if (wrapper) wrapper.classList.add("has-preview");
        requestAnimationFrame(resizeBroadcastPreviewFrames);
        setTimeout(resizeBroadcastPreviewFrames, 150);
      }

      function updateOutputVisualPreview() {
        const url = graphicPreviewUrl(previewGraphic);
        setGraphicPreviewFrame(outputPreviewFrame, previewUrlWithVersion(url));
        if (outputPreviewFrameLabel) outputPreviewFrameLabel.textContent = graphicDisplayName(previewGraphic);
        if (outputPreviewFrameEmpty) {
          outputPreviewFrameEmpty.textContent = previewGraphic === "blank"
            ? "Blank selected. Program output will clear."
            : "Loading selected graphic preview.";
        }
      }

      function loadActiveGraphicPreviews(pageId = activeControlPage) {
        const activeGroup = new Set(pageGroupFor(pageId || "timingPanel"));
        graphicPreviewFrames.forEach(frame => {
          const panel = frame.closest("[data-control-page-panel]");
          const panelId = panel?.id || "";
          const isActive = activeGroup.has(panelId);
          if (frame === outputPreviewFrame) {
            if (isActive) updateOutputVisualPreview();
            return;
          }
          if (isActive) {
            setGraphicPreviewFrame(frame, previewUrlWithVersion(frame.dataset.previewSrc));
          }
        });
        requestAnimationFrame(resizeBroadcastPreviewFrames);
        setTimeout(resizeBroadcastPreviewFrames, 150);
      }

      function bannerTemplateLabel(value) {
        const names = {
          classic: "Classic",
          "reference-premium": "Dark Theme",
          "light-theme": "Light Theme"
        };
        return names[value] || displayText(value, "Classic");
      }

      function controlPreviewRows(state) {
        const rows = state?.runningOrder?.length ? state.runningOrder : state?.competitors || [];
        const reviewRows = rows.filter(hasReviewableDriver);
        return reviewRows.length ? reviewRows : rows;
      }

      function top3ManualPreviewRows(settings) {
        const drivers = Array.isArray(settings.top3Drivers) ? settings.top3Drivers : [];
        return drivers.map((driver, index) => ({
          index,
          number: displayText(driver?.number),
          name: displayText(driver?.name)
        })).filter(driver => driver.number || driver.name);
      }

      function top3TimingPreviewRows(state) {
        return controlPreviewRows(state).slice(0, 3).map((driver, index) => ({
          index,
          number: displayText(driverNumber(driver)),
          name: displayText(driverName(driver))
        })).filter(driver => driver.number || driver.name);
      }

      function top3PreviewLine(rows) {
        if (!rows.length) return "Waiting for timing or manual entries.";
        return rows.slice(0, 3).map((driver, index) => {
          const label = [driver.number ? `#${driver.number}` : "", driver.name || "Name missing"].filter(Boolean).join(" ");
          return `P${index + 1} ${label}`;
        }).join(" | ");
      }

      function renderTickerMiniPreview(message) {
        if (!tickerMiniPreviewTrack) return;
        const text = displayText(message, "Ticker preview");
        tickerMiniPreviewTrack.replaceChildren();
        for (let index = 0; index < 4; index += 1) {
          if (index > 0) {
            const separator = document.createElement("em");
            separator.textContent = ".";
            tickerMiniPreviewTrack.append(separator);
          }
          const item = document.createElement("span");
          item.textContent = text;
          tickerMiniPreviewTrack.append(item);
        }
      }

      const weatherStateAbbreviations = {
        alabama: "AL",
        alaska: "AK",
        arizona: "AZ",
        arkansas: "AR",
        california: "CA",
        colorado: "CO",
        connecticut: "CT",
        delaware: "DE",
        florida: "FL",
        georgia: "GA",
        hawaii: "HI",
        idaho: "ID",
        illinois: "IL",
        indiana: "IN",
        iowa: "IA",
        kansas: "KS",
        kentucky: "KY",
        louisiana: "LA",
        maine: "ME",
        maryland: "MD",
        massachusetts: "MA",
        michigan: "MI",
        minnesota: "MN",
        mississippi: "MS",
        missouri: "MO",
        montana: "MT",
        nebraska: "NE",
        nevada: "NV",
        "new hampshire": "NH",
        "new jersey": "NJ",
        "new mexico": "NM",
        "new york": "NY",
        "north carolina": "NC",
        "north dakota": "ND",
        ohio: "OH",
        oklahoma: "OK",
        oregon: "OR",
        pennsylvania: "PA",
        "rhode island": "RI",
        "south carolina": "SC",
        "south dakota": "SD",
        tennessee: "TN",
        texas: "TX",
        utah: "UT",
        vermont: "VT",
        virginia: "VA",
        washington: "WA",
        "west virginia": "WV",
        wisconsin: "WI",
        wyoming: "WY"
      };

      function weatherPreviewRequestLocation(settings, metaState) {
        return displayText(
          settings.weatherLocationOverride || metaState.weatherLocation || metaState.trackName,
          "Timing location"
        );
      }

      function compactWeatherLocation(value, fallback = "Weather") {
        const raw = displayText(value, fallback);
        if (!raw) return fallback;
        const cleaned = raw
          .replace(/\s*,?\s*(United States of America|United States|USA)$/i, "")
          .replace(/\s+/g, " ")
          .trim();
        const parts = cleaned.split(",").map(part => part.trim()).filter(Boolean);
        if (parts.length >= 2) {
          const stateText = parts[1].toLowerCase();
          const state = weatherStateAbbreviations[stateText] || (stateText.length === 2 ? stateText.toUpperCase() : parts[1]);
          return `${parts[0]}, ${state}`;
        }
        return cleaned;
      }

      function weatherRound(value) {
        const number = Number(value);
        return Number.isFinite(number) ? Math.round(number) : null;
      }

      function weatherTemperature(value, unit = "\u00b0F") {
        const rounded = weatherRound(value);
        return rounded == null ? "--" : `${rounded}${unit || "\u00b0F"}`;
      }

      function weatherWind(current = {}) {
        const speed = weatherRound(current.windSpeed);
        const gust = weatherRound(current.windGust);
        const unit = current.units?.wind || "mph";
        if (speed == null) return "--";
        return `${current.windCompass ? `${current.windCompass} ` : ""}${speed} ${unit}${gust ? ` G${gust}` : ""}`;
      }

      function weatherHumidity(value, unit = "%") {
        const rounded = weatherRound(value);
        return rounded == null ? "--" : `${rounded}${unit || "%"}`;
      }

      function weatherRain(value, unit = "in") {
        const number = Number(value);
        if (!Number.isFinite(number)) return "--";
        return `${number.toFixed(number > 0 && number < 1 ? 2 : 1)}${unit || "in"}`;
      }

      function weatherMiniIconClass(condition) {
        const text = String(condition || "").toLowerCase();
        if (text.includes("thunder") || text.includes("storm")) return "is-storm";
        if (text.includes("rain") || text.includes("drizzle") || text.includes("shower")) return "is-rain";
        if (text.includes("snow")) return "is-snow";
        if (text.includes("fog")) return "is-fog";
        if (text.includes("partly") || text.includes("mainly")) return "is-partly";
        if (text.includes("cloud") || text.includes("overcast")) return "is-cloud";
        if (text.includes("clear") || text.includes("sun")) return "is-sun";
        return "is-unknown";
      }

      function renderWeatherMiniData(data, fallbackLocation) {
        const isReady = Boolean(data && data.ok);
        const current = isReady ? (data.current || {}) : {};
        const units = current.units || {};
        const previewLocation = compactWeatherLocation(
          isReady ? (data.resolvedLocation || data.requestedLocation) : (data?.requestedLocation || fallbackLocation),
          "Timing location"
        );
        const condition = isReady ? displayText(current.condition, "Current conditions") : "Weather not loaded";
        if (weatherPreviewLocation) weatherPreviewLocation.textContent = previewLocation;
        if (weatherPreviewDetail) {
          weatherPreviewDetail.textContent = isReady
            ? `${weatherTemperature(current.temperature, units.temperature)} ${condition}`
            : (data?.error || "Apply weather or wait for live weather data.");
        }
        if (weatherMiniHeaderLocation) weatherMiniHeaderLocation.textContent = previewLocation;
        if (weatherMiniLocation) weatherMiniLocation.textContent = isReady ? "Live weather data" : previewLocation;
        if (weatherMiniTemp) weatherMiniTemp.textContent = isReady ? weatherTemperature(current.temperature, units.temperature) : "--";
        if (weatherMiniCondition) weatherMiniCondition.textContent = condition;
        if (weatherMiniFeels) weatherMiniFeels.textContent = isReady ? weatherTemperature(current.feelsLike, units.temperature) : "--";
        if (weatherMiniWind) weatherMiniWind.textContent = isReady ? weatherWind(current) : "--";
        if (weatherMiniHumidity) weatherMiniHumidity.textContent = isReady ? weatherHumidity(current.humidity, units.humidity) : "--";
        if (weatherMiniRain) weatherMiniRain.textContent = isReady ? weatherRain(current.precipitation, units.precipitation) : "--";
        if (weatherMiniIcon) {
          weatherMiniIcon.className = `weather-preview-icon ${weatherMiniIconClass(isReady ? condition : "")}`;
        }
      }

      function updateWeatherTickerMiniPreview(settings, metaState, weatherData = latestWeatherPreviewData) {
        const fallbackLocation = weatherPreviewRequestLocation(settings, metaState);
        renderWeatherMiniData(weatherData, fallbackLocation);
        renderTickerMiniPreview(settings.tickerText);
      }

      function scheduleWeatherPreviewFetch(settings, metaState, options = {}) {
        const location = weatherPreviewRequestLocation(settings, metaState);
        const signature = `${location || ""}|${settings.weatherLocationOverride || ""}`;
        const previewTtl = latestWeatherPreviewData?.ok ? 300000 : 30000;
        const isFresh = latestWeatherPreviewData && Date.now() - weatherPreviewLastFetchAt < previewTtl;
        if (!options.force && signature === weatherPreviewSignature && isFresh) return;
        if (weatherPreviewFetchInFlight) return;
        weatherPreviewSignature = signature;
        weatherPreviewFetchInFlight = true;
        fetch(`/api/weather${options.force ? "?force=1" : ""}`, { cache: "no-store" })
          .then(response => response.json())
          .then(data => {
            latestWeatherPreviewData = data;
            weatherPreviewLastFetchAt = Date.now();
            updateWeatherTickerMiniPreview(settings, metaState, data);
          })
          .catch(() => {
            latestWeatherPreviewData = {
              ok: false,
              requestedLocation: location,
              error: "Weather preview unavailable."
            };
            weatherPreviewLastFetchAt = Date.now();
            updateWeatherTickerMiniPreview(settings, metaState, latestWeatherPreviewData);
          })
          .finally(() => {
            weatherPreviewFetchInFlight = false;
          });
      }

      function updateStylePresetCards(settings = getStyleSettings()) {
        const active = settings.bannerTemplate || defaultStyleSettings.bannerTemplate;
        stylePresetButtons.forEach(button => {
          const selected = button.dataset.stylePreset === active;
          button.classList.toggle("is-active", selected);
          button.setAttribute("aria-pressed", selected ? "true" : "false");
        });
      }

      function renderControlPreviews(state = latestTimingState) {
        const settings = { ...getStyleSettings(), ...((state && state.style) || {}) };
        previewStyleKey = [
          settings.bannerTemplate,
          settings.bannerHeaderLogoVersion,
          settings.bannerSponsorLogoVersion,
          settings.bannerHeaderLogoCount,
          settings.bannerSponsorLogoCount,
          settings.bannerDarkCellColor,
          settings.bannerLightCellColor,
          settings.bannerFlyoutColor,
          settings.bannerAccentLineColor,
          settings.bannerOpacity,
          settings.bannerFlyoutOpacity
        ].join("|");
        const metaState = (state && state.meta) || {};
        if (outputPreviewGraphic) outputPreviewGraphic.textContent = graphicDisplayName(previewGraphic);
        if (outputPreviewDetail) {
          outputPreviewDetail.textContent = previewGraphic === "blank"
            ? "Ready to clear the program overlay."
            : "Selected for next Send / Refresh.";
        }
        if (outputLiveGraphic) outputLiveGraphic.textContent = graphicDisplayName(programGraphic);
        if (outputLiveDetail) {
          outputLiveDetail.textContent = programGraphic === "blank"
            ? "Nothing currently sent."
            : "Currently selected in program output.";
        }
        if (outputBannerStatus) outputBannerStatus.textContent = topBannerVisible ? "Shown" : "Hidden";
        if (outputBannerDetail) outputBannerDetail.textContent = `${topBannerGraphicLabel()} browser input.`;
        if (bannerPreviewTemplate) bannerPreviewTemplate.textContent = bannerTemplateLabel(settings.bannerTemplate);
        if (bannerPreviewDetail) bannerPreviewDetail.textContent = "Top banner and side tower share these controls.";
        if (bannerPreviewData) bannerPreviewData.textContent = settings.bannerDataOpen === false ? "Hidden" : "Shown";
        if (bannerPreviewMode) bannerPreviewMode.textContent = `Mode: ${displayText(settings.bannerRaceMode, "Auto")}`;
        if (tickerPreviewSource) tickerPreviewSource.textContent = settings.tickerSource === "manual" ? "Manual message" : "Ticker";
        if (tickerPreviewDetail) tickerPreviewDetail.textContent = displayText(settings.tickerText, "No ticker message entered.");
        updateWeatherTickerMiniPreview(settings, metaState);
        scheduleWeatherPreviewFetch(settings, metaState);
        const manualRows = top3ManualPreviewRows(settings);
        const timingRows = top3TimingPreviewRows(state);
        const top3Rows = manualRows.length ? manualRows : timingRows;
        if (top3PreviewTitle) {
          top3PreviewTitle.textContent = settings.top3TitleSource === "manual" && displayText(settings.top3Title)
            ? settings.top3Title
            : "Top 3";
        }
        if (top3PreviewDetail) top3PreviewDetail.textContent = top3PreviewLine(top3Rows);
        updateOutputVisualPreview();
        loadActiveGraphicPreviews(activeControlPage);
        updateStylePresetCards(settings);
        requestAnimationFrame(resizeBroadcastPreviewFrames);
      }

      function closeRaceReadyDetails() {
        if (raceReadyDetails) raceReadyDetails.open = false;
      }

      function restorePendingLiveControl() {
        if (!pendingLiveControl) return;
        clearTimeout(pendingLiveControl.timer);
        pendingLiveControl.button.classList.remove("needs-confirm");
        pendingLiveControl.button.textContent = pendingLiveControl.originalText;
        pendingLiveControl = null;
      }

      function setLiveControlsLocked(locked) {
        liveControlsLocked = Boolean(locked);
        liveControlLock.classList.toggle("active", liveControlsLocked);
        liveControlLock.classList.toggle("secondary", !liveControlsLocked);
        liveControlLock.setAttribute("aria-pressed", String(liveControlsLocked));
        liveControlLock.textContent = liveControlsLocked ? "Live Controls Locked" : "Live Controls Unlocked";
        try {
          localStorage.setItem("raceGraphics.liveControlsLocked", String(liveControlsLocked));
        } catch (_err) {}
        if (!liveControlsLocked) restorePendingLiveControl();
      }

      function guardLiveControl(button, label, action) {
        if (!liveControlsLocked) return action();
        const now = Date.now();
        if (pendingLiveControl?.button === button && now - pendingLiveControl.startedAt < 3500) {
          restorePendingLiveControl();
          return action();
        }
        restorePendingLiveControl();
        const originalText = button.textContent;
        button.classList.add("needs-confirm");
        button.textContent = `Click Again: ${label}`;
        programStatus.textContent = `Live controls locked: click ${label} again to confirm.`;
        pendingLiveControl = {
          button,
          originalText,
          startedAt: now,
          timer: setTimeout(restorePendingLiveControl, 3500)
        };
        return undefined;
      }

      function syncDataPanelHeight() {
        if (!dataPanel || !upperLeftStack) return;
        if (getComputedStyle(dataPanel).display === "none" || getComputedStyle(upperLeftStack).display === "none") {
          dataPanel.style.removeProperty("--data-panel-height");
          return;
        }
        const dataTop = dataPanel.getBoundingClientRect().top;
        const stackBottom = upperLeftStack.getBoundingClientRect().bottom;
        const height = Math.max(260, Math.round(stackBottom - dataTop));
        dataPanel.style.setProperty("--data-panel-height", `${height}px`);
      }

      setControlMode(localStorage.getItem("raceGraphics.controlMode") || "race");
      setLiveControlsLocked(liveControlsLocked);
      raceDayModeButton.onclick = () => setControlMode("race");
      setupModeButton.onclick = () => setControlMode("setup");
      liveControlLock.onclick = () => setLiveControlsLocked(!liveControlsLocked);

      document.addEventListener("click", event => {
        if (!raceReadyDetails?.open) return;
        if (raceReadyDetails.contains(event.target)) return;
        closeRaceReadyDetails();
      });

      document.querySelectorAll(".control-quick-nav a").forEach(link => {
        link.addEventListener("click", event => {
          closeRaceReadyDetails();
          const nextPage = (link.getAttribute("href") || "").replace(/^#/, "");
          if (!controlPageIds.includes(nextPage)) return;
          event.preventDefault();
          setActiveControlPage(nextPage, { pushHash: true });
          link.blur();
          window.scrollTo({ top: 0, behavior: "smooth" });
        });
      });
      window.addEventListener("hashchange", () => setActiveControlPage(pageIdFromLocation() || "timingPanel"));
      setInterval(updateHeroLocalTime, 1000);
      updateHeroLocalTime();

      function loadVmixSettings() {
        vmixUrl.value = localStorage.getItem(vmixSettings.url) || "";
        vmixInput.value = localStorage.getItem(vmixSettings.input) || "";
        topBannerInput.value = localStorage.getItem(vmixSettings.topBannerInput) || "";
        overlayBaseUrl.value = localStorage.getItem(vmixSettings.overlayBase) || location.origin;
      }

      function isLocalOverlayBase(value) {
        return /^https?:\/\/(127\.0\.0\.1|localhost)(:\d+)?/i.test(String(value || ""));
      }

      function overlayHost(value) {
        try {
          return new URL(value).hostname;
        } catch (_err) {
          return "";
        }
      }

      function isPrivateLanHost(host) {
        return /^(10|192\.168)\./.test(host) || /^172\.(1[6-9]|2\d|3[0-1])\./.test(host);
      }

      async function preferLanOverlayBase() {
        try {
          const response = await fetch("/api/network", { cache: "no-store" });
          const network = await response.json();
          if (!network.lanBaseUrl) return;
          const savedBase = localStorage.getItem(vmixSettings.overlayBase) || "";
          const visibleBase = overlayBaseUrl.value.trim();
          const visibleProgramUrl = vmixProgramUrl.value.trim();
          const currentHosts = new Set((network.lanBases || []).map(item => item.address));
          const savedHost = overlayHost(savedBase);
          const visibleHost = overlayHost(visibleBase);
          const hasStaleSavedLan = isPrivateLanHost(savedHost) && !currentHosts.has(savedHost);
          const hasStaleVisibleLan = isPrivateLanHost(visibleHost) && !currentHosts.has(visibleHost);
          if (!savedBase || isLocalOverlayBase(savedBase) || isLocalOverlayBase(visibleBase) || isLocalOverlayBase(visibleProgramUrl) || hasStaleSavedLan || hasStaleVisibleLan) {
            overlayBaseUrl.value = network.lanBaseUrl;
            localStorage.setItem(vmixSettings.overlayBase, network.lanBaseUrl);
            updateVmixProgramUrl();
          }
        } catch (_err) {
          updateVmixProgramUrl();
        }
      }

      function saveVmixSettings() {
        localStorage.setItem(vmixSettings.url, vmixUrl.value.trim());
        localStorage.setItem(vmixSettings.input, vmixInput.value.trim());
        localStorage.setItem(vmixSettings.topBannerInput, topBannerInput.value.trim());
        localStorage.setItem(vmixSettings.overlayBase, overlayBaseUrl.value.trim());
        updateVmixProgramUrl();
        rerenderRaceReady();
      }

      function normalizedOverlayBase() {
        return (overlayBaseUrl.value.trim() || location.origin).replace(/\/+$/, "");
      }

      function topBannerGraphicLabel() {
        return "Banner";
      }

      function topBannerGraphicPath() {
        return "/top-banner.html";
      }

      function getIpadControlUrl() {
        return `${normalizedOverlayBase()}/i.html`;
      }

      function makeQrSvg(text) {
        const version = 5;
        const size = 17 + version * 4;
        const dataCodewords = 108;
        const ecCodewords = 26;
        const bytes = Array.from(new TextEncoder().encode(text));
        if (bytes.length > 104) {
          throw new Error("QR link is too long.");
        }

        const bits = [];
        const pushBits = (value, count) => {
          for (let i = count - 1; i >= 0; i -= 1) bits.push((value >>> i) & 1);
        };
        pushBits(0x4, 4);
        pushBits(bytes.length, 8);
        bytes.forEach(byte => pushBits(byte, 8));
        const maxBits = dataCodewords * 8;
        for (let i = 0; i < 4 && bits.length < maxBits; i += 1) bits.push(0);
        while (bits.length % 8) bits.push(0);

        const data = [];
        for (let i = 0; i < bits.length; i += 8) {
          data.push(bits.slice(i, i + 8).reduce((value, bit) => (value << 1) | bit, 0));
        }
        for (let pad = 0xec; data.length < dataCodewords; pad = pad === 0xec ? 0x11 : 0xec) {
          data.push(pad);
        }

        const gfExp = new Array(512);
        const gfLog = new Array(256);
        let x = 1;
        for (let i = 0; i < 255; i += 1) {
          gfExp[i] = x;
          gfLog[x] = i;
          x <<= 1;
          if (x & 0x100) x ^= 0x11d;
        }
        for (let i = 255; i < 512; i += 1) gfExp[i] = gfExp[i - 255];
        const gfMul = (a, b) => (a && b ? gfExp[gfLog[a] + gfLog[b]] : 0);

        const generator = new Array(ecCodewords).fill(0);
        generator[ecCodewords - 1] = 1;
        let root = 1;
        for (let i = 0; i < ecCodewords; i += 1) {
          for (let j = 0; j < ecCodewords; j += 1) {
            generator[j] = gfMul(generator[j], root);
            if (j + 1 < ecCodewords) generator[j] ^= generator[j + 1];
          }
          root = gfMul(root, 0x02);
        }
        const ecc = new Array(ecCodewords).fill(0);
        data.forEach(byte => {
          const factor = byte ^ ecc.shift();
          ecc.push(0);
          generator.forEach((coef, index) => {
            ecc[index] ^= gfMul(coef, factor);
          });
        });
        const codewords = data.concat(ecc);

        const modules = Array.from({ length: size }, () => Array(size).fill(false));
        const reserved = Array.from({ length: size }, () => Array(size).fill(false));
        const setModule = (row, col, dark, isFunction = true) => {
          if (row < 0 || col < 0 || row >= size || col >= size) return;
          modules[row][col] = Boolean(dark);
          if (isFunction) reserved[row][col] = true;
        };

        const drawFinder = (row, col) => {
          for (let r = -1; r <= 7; r += 1) {
            for (let c = -1; c <= 7; c += 1) {
              const rr = row + r;
              const cc = col + c;
              const inside = r >= 0 && r <= 6 && c >= 0 && c <= 6;
              const dark = inside && (r === 0 || r === 6 || c === 0 || c === 6 || (r >= 2 && r <= 4 && c >= 2 && c <= 4));
              setModule(rr, cc, dark);
            }
          }
        };
        drawFinder(0, 0);
        drawFinder(0, size - 7);
        drawFinder(size - 7, 0);

        const drawAlignment = (centerRow, centerCol) => {
          for (let r = -2; r <= 2; r += 1) {
            for (let c = -2; c <= 2; c += 1) {
              const dark = Math.max(Math.abs(r), Math.abs(c)) !== 1;
              setModule(centerRow + r, centerCol + c, dark);
            }
          }
        };
        drawAlignment(30, 30);

        for (let i = 8; i < size - 8; i += 1) {
          setModule(6, i, i % 2 === 0);
          setModule(i, 6, i % 2 === 0);
        }
        for (let i = 0; i < 8; i += 1) {
          reserved[8][i] = true;
          reserved[i][8] = true;
          reserved[8][size - 1 - i] = true;
          reserved[size - 1 - i][8] = true;
        }
        reserved[8][8] = true;

        const dataBits = codewords.flatMap(byte => Array.from({ length: 8 }, (_unused, i) => (byte >>> (7 - i)) & 1));
        let bitIndex = 0;
        let upward = true;
        for (let col = size - 1; col > 0; col -= 2) {
          if (col === 6) col -= 1;
          for (let step = 0; step < size; step += 1) {
            const row = upward ? size - 1 - step : step;
            for (let offset = 0; offset < 2; offset += 1) {
              const cc = col - offset;
              if (reserved[row][cc]) continue;
              const mask = (row + cc) % 2 === 0;
              if (bitIndex >= dataBits.length) {
                modules[row][cc] = mask;
                bitIndex += 1;
                continue;
              }
              modules[row][cc] = Boolean(dataBits[bitIndex] ^ mask);
              bitIndex += 1;
            }
          }
          upward = !upward;
        }

        const formatValue = (() => {
          const ecLevelL = 1;
          const maskPattern = 0;
          const value = (ecLevelL << 3) | maskPattern;
          let work = value << 10;
          for (let i = 14; i >= 10; i -= 1) {
            if ((work >>> i) & 1) work ^= 0x537 << (i - 10);
          }
          return ((value << 10) | work) ^ 0x5412;
        })();
        const setFormat = (index, row, col) => setModule(row, col, ((formatValue >>> index) & 1) === 1);
        for (let i = 0; i <= 5; i += 1) setFormat(i, 8, i);
        setFormat(6, 8, 7);
        setFormat(7, 8, 8);
        setFormat(8, 7, 8);
        for (let i = 9; i < 15; i += 1) setFormat(i, 14 - i, 8);
        for (let i = 0; i < 8; i += 1) setFormat(i, size - 1 - i, 8);
        for (let i = 8; i < 15; i += 1) setFormat(i, 8, size - 15 + i);
        setModule(size - 8, 8, true);

        const quiet = 4;
        const viewSize = size + quiet * 2;
        const path = [];
        for (let row = 0; row < size; row += 1) {
          for (let col = 0; col < size; col += 1) {
            if (modules[row][col]) path.push(`M${col + quiet} ${row + quiet}h1v1h-1z`);
          }
        }
        return `<svg xmlns="http://www.w3.org/2000/svg" width="138" height="138" viewBox="0 0 ${viewSize} ${viewSize}" shape-rendering="crispEdges" role="img"><rect width="${viewSize}" height="${viewSize}" fill="#fff"/><path fill="#000" d="${path.join("")}"/></svg>`;
      }

      function updateIpadQrPanel(renderQr = false) {
        const url = getIpadControlUrl();
        if (ipadControlUrl) ipadControlUrl.value = url;
        if (ipadControlAnchor) {
          ipadControlAnchor.href = url;
          ipadControlAnchor.textContent = url;
        }
        if (!ipadQrPanel || ipadQrPanel.hidden && !renderQr) return;
        try {
          const localQr = makeQrSvg(url);
          const remoteQr = `https://api.qrserver.com/v1/create-qr-code/?size=180x180&margin=12&data=${encodeURIComponent(url)}`;
          const image = document.createElement("img");
          image.alt = "QR code for iPad control link";
          image.src = remoteQr;
          image.onerror = () => {
            image.onerror = null;
            image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(localQr)}`;
          };
          ipadQrCode.replaceChildren(image);
        } catch (_err) {
          ipadQrCode.textContent = "QR unavailable";
        }
      }

      function updateVmixProgramUrl() {
        vmixProgramUrl.value = `${normalizedOverlayBase()}/program.html`;
        vmixTopBannerUrl.value = `${normalizedOverlayBase()}${topBannerGraphicPath()}`;
        if (programOverlayLink) {
          programOverlayLink.href = "/program.html";
        }
        if (topBannerOverlayLink) {
          topBannerOverlayLink.href = "/top-banner.html";
        }
        updateIpadQrPanel();
      }

      loadVmixSettings();
      updateVmixProgramUrl();
      preferLanOverlayBase();
      refreshReadyNetwork();
      refreshReadyPages();
      setInterval(refreshReadyNetwork, 30000);
      setInterval(refreshReadyPages, 15000);
      [vmixUrl, vmixInput, topBannerInput, overlayBaseUrl].forEach(input => {
        input.oninput = saveVmixSettings;
        input.onchange = saveVmixSettings;
      });

      document.querySelector("#copyVmixUrl").onclick = async () => {
        updateVmixProgramUrl();
        vmixProgramUrl.select();
        try {
          await navigator.clipboard.writeText(vmixProgramUrl.value);
          programStatus.textContent = `Copied vMix URL | Output: ${programGraphic}`;
        } catch (_err) {
          document.execCommand("copy");
          programStatus.textContent = `Copied vMix URL | Output: ${programGraphic}`;
        }
      };

      document.querySelector("#copyTopBannerUrl").onclick = async () => {
        updateVmixProgramUrl();
        vmixTopBannerUrl.select();
        try {
          await navigator.clipboard.writeText(vmixTopBannerUrl.value);
          programStatus.textContent = `Copied Banner URL | Banner: ${topBannerVisible ? "shown" : "hidden"}`;
        } catch (_err) {
          document.execCommand("copy");
          programStatus.textContent = `Copied Banner URL | Banner: ${topBannerVisible ? "shown" : "hidden"}`;
        }
      };

      document.querySelector("#showIpadQr").onclick = () => {
        updateVmixProgramUrl();
        updateIpadQrPanel(true);
        ipadQrPanel.hidden = false;
      };

      document.querySelector("#closeIpadQr").onclick = () => {
        ipadQrPanel.hidden = true;
      };

      document.querySelector("#copyIpadUrl").onclick = async () => {
        updateVmixProgramUrl();
        ipadControlUrl.select();
        try {
          await navigator.clipboard.writeText(ipadControlUrl.value);
          programStatus.textContent = "Copied iPad control link.";
        } catch (_err) {
          document.execCommand("copy");
          programStatus.textContent = "Copied iPad control link.";
        }
      };
      const styleInputs = {
        titleSize: document.querySelector("#titleSize"),
        rowNameSize: document.querySelector("#rowNameSize"),
        leaderNameSize: document.querySelector("#leaderNameSize"),
        boardScale: document.querySelector("#boardScale"),
        boardOffsetY: document.querySelector("#boardOffsetY"),
        panelOpacity: document.querySelector("#panelOpacity"),
        competitorNameFormat: document.querySelector("#competitorNameFormat"),
        lineupMetricSource: document.querySelector("#lineupMetricSource"),
        bannerLogoRotateSeconds: document.querySelector("#bannerLogoRotateSeconds"),
        bannerMetricRotateSeconds: document.querySelector("#bannerMetricRotateSeconds"),
        bannerNameSize: document.querySelector("#bannerNameSize"),
        bannerMetricSize: document.querySelector("#bannerMetricSize"),
        bannerInfoSize: document.querySelector("#bannerInfoSize"),
        bannerDurationSize: document.querySelector("#bannerDurationSize"),
        bannerOpacity: document.querySelector("#bannerOpacity"),
        bannerFlyoutOpacity: document.querySelector("#bannerFlyoutOpacity"),
        bannerLogoPanelColor: document.querySelector("#bannerLogoPanelColor"),
        bannerDarkCellColor: document.querySelector("#bannerDarkCellColor"),
        bannerLightCellColor: document.querySelector("#bannerLightCellColor"),
        bannerFlyoutColor: document.querySelector("#bannerFlyoutColor"),
        bannerAccentLineColor: document.querySelector("#bannerAccentLineColor"),
        weatherLocationOverride: document.querySelector("#weatherLocationOverride"),
        tickerSource: document.querySelector("#tickerSource"),
        tickerText: document.querySelector("#tickerText"),
        tickerSpeedSeconds: document.querySelector("#tickerSpeedSeconds"),
        raceStoryPosition: document.querySelector("#raceStoryPosition"),
        raceStoryAutoUpdate: document.querySelector("#raceStoryAutoUpdate")
      };
      const colorInputKeys = [
        "bannerLogoPanelColor",
        "bannerDarkCellColor",
        "bannerLightCellColor",
        "bannerFlyoutColor",
        "bannerAccentLineColor"
      ];
      const colorCodeInputs = {
        bannerLogoPanelColor: document.querySelector("#bannerLogoPanelColorCode"),
        bannerDarkCellColor: document.querySelector("#bannerDarkCellColorCode"),
        bannerLightCellColor: document.querySelector("#bannerLightCellColorCode"),
        bannerFlyoutColor: document.querySelector("#bannerFlyoutColorCode"),
        bannerAccentLineColor: document.querySelector("#bannerAccentLineColorCode")
      };
      const bannerSelects = {
        bannerTemplate: document.querySelector("#bannerTemplate"),
        bannerDurationMode: document.querySelector("#bannerDurationMode"),
        bannerRaceMode: document.querySelector("#bannerRaceMode"),
        bannerInfoSource: document.querySelector("#bannerInfoSource"),
        bannerNameFormat: document.querySelector("#bannerNameFormat")
      };
      const BANNER_MODE_PRESETS = Object.freeze({
        auto: {
          label: "Auto",
          bannerRaceMode: "auto",
          bannerMetricFields: ["lastLap", "gap"],
          bannerDurationMode: "auto"
        },
        qualifying: {
          label: "Qualifying",
          bannerRaceMode: "qualifying",
          bannerMetricFields: ["lastLap", "bestLap"],
          bannerDurationMode: "none"
        },
        heat: {
          label: "Heat",
          bannerRaceMode: "racing",
          bannerMetricFields: ["lastLap", "gap"],
          bannerDurationMode: "lapsToGo"
        },
        feature: {
          label: "Feature",
          bannerRaceMode: "racing",
          bannerMetricFields: ["lastLap", "gap"],
          bannerDurationMode: "lapsToGo"
        }
      });
      const bannerManualInfo = document.querySelector("#bannerManualInfo");
      const top3TitleSource = document.querySelector("#top3TitleSource");
      const top3Title = document.querySelector("#top3Title");
      const top3Subtitle = document.querySelector("#top3Subtitle");
      const top3Source = document.querySelector("#top3Source");
      const top3Layout = document.querySelector("#top3Layout");
      const top3DriverInputs = [1, 2, 3].map(position => ({
        position,
        photo: document.querySelector(`#top3Photo${position}`),
        number: document.querySelector(`#top3Number${position}`),
        name: document.querySelector(`#top3Name${position}`)
      }));
      const bannerStyleInputKeys = [
        "bannerLogoRotateSeconds",
        "bannerMetricRotateSeconds",
        "bannerNameSize",
        "bannerMetricSize",
        "bannerInfoSize",
        "bannerDurationSize",
        "bannerOpacity",
        "bannerFlyoutOpacity",
        "bannerLogoPanelColor",
        "bannerDarkCellColor",
        "bannerLightCellColor",
        "bannerFlyoutColor",
        "bannerAccentLineColor"
      ];
      const bannerResetKeys = [
        ...bannerStyleInputKeys,
        "bannerTopFiveOpen",
        "bannerDataOpen",
        "bannerMetricFields",
        "bannerInfoSource",
        "bannerManualInfo",
        "bannerDurationMode",
        "bannerRaceMode",
        "bannerNameFormat"
      ];
      const generalStyleInputKeys = Object.keys(styleInputs).filter(key => !bannerStyleInputKeys.includes(key));
      const weatherInputKeys = ["weatherLocationOverride"];
      const tickerInputKeys = ["tickerSource", "tickerText", "tickerSpeedSeconds"];
      const raceStoryInputKeys = ["raceStoryPosition", "raceStoryAutoUpdate"];

      function normalizeHexColor(value) {
        const text = String(value || "").trim().replace(/^#/, "");
        if (/^[0-9a-f]{3}$/i.test(text)) {
          return `#${text.split("").map(char => char + char).join("")}`.toLowerCase();
        }
        if (/^[0-9a-f]{6}$/i.test(text)) return `#${text.toLowerCase()}`;
        return "";
      }

      function setColorControl(key, value) {
        const color = normalizeHexColor(value) || defaultStyleSettings[key] || "#ffffff";
        if (styleInputs[key]) styleInputs[key].value = color;
        if (colorCodeInputs[key]) {
          colorCodeInputs[key].value = color.toUpperCase();
          colorCodeInputs[key].classList.remove("invalid");
        }
      }

      function syncColorCodeInputs(settings = getStyleSettings()) {
        colorInputKeys.forEach(key => {
          setColorControl(key, settings[key]);
        });
      }

      function readControlValue(key, input) {
        if (input.type === "checkbox") return input.checked;
        if (input.type === "range" || input.type === "number") {
          const value = Number(input.value);
          if (!Number.isFinite(value)) return defaultStyleSettings[key];
          const min = Number(input.min);
          const max = Number(input.max);
          return Math.min(Number.isFinite(max) ? max : value, Math.max(Number.isFinite(min) ? min : value, value));
        }
        if (input.type === "color") return normalizeHexColor(colorCodeInputs[key]?.value) || normalizeHexColor(input.value) || defaultStyleSettings[key];
        return input.value;
      }

      function normalizeFooterSource(value) {
        if (value === "track" || value === "manual") return value;
        return "class";
      }

      // Package-wide look toggles: one click flips and saves immediately,
      // matching the preset-card behavior (no separate Apply needed).
      const packageToggles = [
        { button: document.querySelector("#toggleGlass"), key: "bannerGlassOpen", label: "Glass Effect" },
        { button: document.querySelector("#toggleMoments"), key: "bannerMomentsOpen", label: "Race Moments" },
        { button: document.querySelector("#toggleMotion"), key: "bannerMotionOpen", label: "Live Motion" }
      ];

      function renderPackageToggles(settings = getStyleSettings()) {
        packageToggles.forEach(({ button, key, label }) => {
          if (!button) return;
          const on = settings[key] !== false;
          button.textContent = `${label}: ${on ? "On" : "Off"}`;
          button.classList.toggle("is-active", on);
        });
      }

      packageToggles.forEach(({ button, key, label }) => {
        if (!button) return;
        button.onclick = async () => {
          const settings = getStyleSettings();
          settings[key] = settings[key] === false;
          renderPackageToggles(settings);
          const result = await saveServerStyle(settings);
          programStatus.textContent = result.ok
            ? `${label} ${settings[key] !== false ? "on" : "off"}`
            : result.error || `${label} toggle failed`;
          loadStyleControls();
        };
      });

      function loadStyleControls() {
        const settings = getStyleSettings();
        renderPackageToggles(settings);
        Object.entries(styleInputs).forEach(([key, input]) => {
          if (input.type === "checkbox") {
            input.checked = settings[key] !== false;
            return;
          }
          input.value = settings[key];
        });
        syncColorCodeInputs(settings);
        footerSource.value = normalizeFooterSource(settings.footerSource);
        manualSession.value = settings.manualSession || settings.manualTrackName || "";
        missingPlaceholder.value = settings.missingPlaceholder || defaultStyleSettings.missingPlaceholder;
        bannerTopFiveOpen.checked = settings.bannerTopFiveOpen !== false;
        updateBannerDataButton(settings);
        bannerManualInfo.value = settings.bannerManualInfo || "";
        updateBannerLogoStatus(settings);
        Object.entries(bannerSelects).forEach(([key, input]) => {
          input.value = settings[key] || defaultStyleSettings[key];
        });
        const selectedMetrics = (Array.isArray(settings.bannerMetricFields) && settings.bannerMetricFields.length
          ? settings.bannerMetricFields
          : defaultStyleSettings.bannerMetricFields);
        bannerMetricFields.forEach(input => {
          input.checked = selectedMetrics.includes(input.value);
        });
        top3TitleSource.value = settings.top3TitleSource || defaultStyleSettings.top3TitleSource;
        top3Title.value = settings.top3Title || defaultStyleSettings.top3Title;
        top3Subtitle.value = settings.top3Subtitle || defaultStyleSettings.top3Subtitle;
        top3Source.value = settings.top3Source || defaultStyleSettings.top3Source;
        top3Layout.value = "photo";
        const top3Drivers = Array.isArray(settings.top3Drivers) ? settings.top3Drivers : defaultStyleSettings.top3Drivers;
        top3DriverInputs.forEach(({ position, number, name }) => {
          const driver = top3Drivers[position - 1] || {};
          number.value = driver.number || "";
          name.value = driver.name || "";
        });
        updateStylePresetCards(settings);
        renderControlPreviews(latestTimingState);
      }

      function readStyleControls() {
        const settings = getStyleSettings();
        Object.entries(styleInputs).forEach(([key, input]) => {
          settings[key] = readControlValue(key, input);
        });
        settings.footerSource = normalizeFooterSource(footerSource.value);
        settings.manualTrackName = "";
        settings.manualSession = manualSession.value.trim();
        settings.missingPlaceholder = missingPlaceholder.value;
        settings.bannerTopFiveOpen = bannerTopFiveOpen.checked;
        settings.bannerAccentLinesOpen = true;
        settings.bannerTextureOpen = true;
        settings.bannerReflectionOpen = true;
        settings.bannerBevelOpen = true;
        settings.bannerManualInfo = bannerManualInfo.value.trim();
        Object.entries(bannerSelects).forEach(([key, input]) => {
          settings[key] = input.value;
        });
        settings.bannerMetricFields = bannerMetricFields.filter(input => input.checked).map(input => input.value);
        return settings;
      }

      stylePresetButtons.forEach(button => {
        button.addEventListener("click", async () => {
          const template = button.dataset.stylePreset;
          if (!template) return;
          const settings = readStyleControls();
          settings.bannerTemplate = template;
          if (bannerSelects.bannerTemplate) bannerSelects.bannerTemplate.value = template;
          applyStyleSettings(settings);
          updateStylePresetCards(settings);
          renderControlPreviews(latestTimingState);
          const result = await saveServerStyle(settings);
          if (result.ok) {
            if (latestTimingState) latestTimingState = { ...latestTimingState, style: result.style || settings };
            programStatus.textContent = `${bannerTemplateLabel(template)} style selected`;
            loadStyleControls();
          } else {
            programStatus.textContent = result.error || "Style preset failed";
          }
        });
      });

      // Some editions only include a subset of the built-in themes; hide the rest.
      (async () => {
        try {
          const response = await fetch("/api/product");
          const product = await response.json();
          const allowed = Array.isArray(product.allowedBannerTemplates) ? product.allowedBannerTemplates : [];
          if (!allowed.length) return;
          stylePresetButtons.forEach(button => {
            if (!allowed.includes(button.dataset.stylePreset)) button.remove();
          });
          const templateSelect = document.querySelector("#bannerTemplate");
          if (templateSelect) {
            [...templateSelect.options].forEach(option => {
              if (!allowed.includes(option.value)) option.remove();
            });
          }
        } catch (_err) {
          // Keep the full template list when product info is unavailable.
        }
      })();

      const savedLookName = document.querySelector("#savedLookName");
      const saveLookButton = document.querySelector("#saveLookButton");
      const savedLooksList = document.querySelector("#savedLooksList");

      async function savedLooksRequest(path, payload) {
        try {
          const response = await fetch(path, payload ? {
            method: "POST",
            headers: { "Content-Type": "application/json" },
            body: JSON.stringify(payload)
          } : undefined);
          return await response.json();
        } catch (_err) {
          return { ok: false, error: "Could not reach the program server." };
        }
      }

      function renderSavedLooks(presets) {
        if (!savedLooksList) return;
        savedLooksList.innerHTML = "";
        (presets || []).forEach(preset => {
          const row = document.createElement("div");
          row.className = "saved-look-row";

          const apply = document.createElement("button");
          apply.type = "button";
          apply.className = "saved-look-apply";
          apply.title = `Apply "${preset.name}"`;
          const swatches = document.createElement("span");
          swatches.className = "saved-look-swatches";
          ["accentColor", "logoPanelColor", "driverBoxColor", "bottomRowColor"].forEach(key => {
            const color = preset.summary && preset.summary[key];
            if (!color) return;
            const chip = document.createElement("i");
            chip.style.background = color;
            swatches.appendChild(chip);
          });
          const label = document.createElement("strong");
          label.textContent = preset.name;
          apply.append(swatches, label);
          apply.addEventListener("click", async () => {
            const result = await savedLooksRequest("/api/style-presets/apply", { id: preset.id });
            if (result.ok) {
              if (latestTimingState) latestTimingState = { ...latestTimingState, style: result.style };
              programStatus.textContent = `"${preset.name}" look applied`;
              loadStyleControls();
              renderControlPreviews(latestTimingState);
              renderSavedLooks(result.presets);
            } else {
              programStatus.textContent = result.error || "Could not apply that look";
            }
          });

          const remove = document.createElement("button");
          remove.type = "button";
          remove.className = "saved-look-delete";
          remove.title = `Delete "${preset.name}"`;
          remove.textContent = "×";
          remove.addEventListener("click", async () => {
            const result = await savedLooksRequest("/api/style-presets/delete", { id: preset.id });
            if (result.ok) {
              programStatus.textContent = `"${preset.name}" look deleted`;
              renderSavedLooks(result.presets);
            } else {
              programStatus.textContent = result.error || "Could not delete that look";
            }
          });

          row.append(apply, remove);
          savedLooksList.appendChild(row);
        });
      }

      if (saveLookButton) {
        saveLookButton.addEventListener("click", async () => {
          const name = savedLookName ? savedLookName.value.trim() : "";
          if (!name) {
            programStatus.textContent = "Name the look before saving";
            if (savedLookName) savedLookName.focus();
            return;
          }
          const result = await savedLooksRequest("/api/style-presets", { name });
          if (result.ok) {
            programStatus.textContent = `"${name}" look saved`;
            if (savedLookName) savedLookName.value = "";
            renderSavedLooks(result.presets);
          } else {
            programStatus.textContent = result.error || "Could not save the look";
          }
        });
      }

      savedLooksRequest("/api/style-presets").then(result => {
        if (result && result.ok) renderSavedLooks(result.presets);
      });

      function readGeneralStyleControls() {
        const settings = getStyleSettings();
        generalStyleInputKeys.forEach(key => {
          const input = styleInputs[key];
          settings[key] = readControlValue(key, input);
        });
        return settings;
      }

      function readFooterControls() {
        const settings = getStyleSettings();
        settings.footerSource = normalizeFooterSource(footerSource.value);
        settings.manualTrackName = "";
        settings.manualSession = manualSession.value.trim();
        return settings;
      }

      function readMissingDataControls() {
        const settings = getStyleSettings();
        settings.missingPlaceholder = missingPlaceholder.value;
        return settings;
      }

      function readWeatherControls() {
        const settings = getStyleSettings();
        weatherInputKeys.forEach(key => {
          const input = styleInputs[key];
          if (input) settings[key] = readControlValue(key, input);
        });
        return settings;
      }

      function readTickerControls() {
        const settings = getStyleSettings();
        tickerInputKeys.forEach(key => {
          const input = styleInputs[key];
          if (input) settings[key] = readControlValue(key, input);
        });
        return settings;
      }

      function raceStoryLiveState(state) {
        const settings = {
          ...(state?.style || getStyleSettings()),
          raceStoryAutoUpdate: true
        };
        return { ...(state || {}), style: settings };
      }

      function clearRaceStoryHold(settings) {
        settings.raceStoryHeldTitle = "";
        settings.raceStoryHeldDetail = "";
        settings.raceStoryHeldTone = "";
        settings.raceStoryHeldType = "";
        settings.raceStoryHeldClass = "";
        settings.raceStoryHeldMode = "";
        settings.raceStoryHeldKey = "";
      }

      function holdRaceStoryFromState(settings, state) {
        if (!state) return;
        const liveState = raceStoryLiveState(state);
        const spotlight = raceStorySpotlightItem(liveState);
        const message = raceStoryTickerMessage(liveState);
        if (!spotlight && !displayText(message)) return;
        const meta = state.meta || {};
        const raceState = state.raceState || {};
        settings.raceStoryHeldTitle = displayText(spotlight?.title, "Race Story");
        settings.raceStoryHeldDetail = displayText(spotlight?.detail || message, "Story ready");
        settings.raceStoryHeldTone = displayText(spotlight?.tone, "live");
        settings.raceStoryHeldType = displayText(spotlight?.type, "story");
        settings.raceStoryHeldClass = displayText(meta.className || meta.session || meta.trackName, "");
        settings.raceStoryHeldMode = displayText(raceState.label || raceState.mode || meta.sessionName || meta.mode, "");
        settings.raceStoryHeldKey = raceStoryReadyKey(spotlight, message) || `${Date.now()}`;
      }

      function storyChoiceKey(item) {
        return raceStoryReadyKey(item, item?.detail || "");
      }

      function raceStoryTypeLabel(type) {
        const text = displayText(type, "Story");
        return text
          .split(/[-_\s]+/)
          .filter(Boolean)
          .map(part => part.charAt(0).toUpperCase() + part.slice(1))
          .join(" ");
      }

      function raceStoryTypeGroup(type) {
        const key = displayText(type, "story").toLowerCase();
        if (["winner", "lead-change", "leader"].includes(key)) return "leader";
        if (["fastest-lap", "top-qualifier"].includes(key)) return "fast";
        if (key === "biggest-mover") return "mover";
        if (["closest-battle", "battle"].includes(key)) return "battle";
        return "other";
      }

      function renderRaceStoryHistory() {
        if (!raceStoryHistoryList) return;
        raceStoryHistoryList.innerHTML = "";
        if (!raceStoryHistory.length) {
          const empty = document.createElement("span");
          empty.textContent = "No Race Story sent yet.";
          raceStoryHistoryList.appendChild(empty);
          return;
        }
        raceStoryHistory.slice(0, 5).forEach(item => {
          const row = document.createElement("div");
          row.className = "race-story-history-item";
          const title = document.createElement("strong");
          const meta = document.createElement("em");
          title.textContent = displayText(item.title, "Race Story");
          const sentAt = item.sentAt ? new Date(item.sentAt).toLocaleTimeString([], { hour: "numeric", minute: "2-digit" }) : "";
          meta.textContent = [sentAt, displayText(item.type)].filter(Boolean).join(" | ");
          row.append(title, meta);
          raceStoryHistoryList.appendChild(row);
        });
      }

      function loadRaceStoryHistory() {
        try {
          const parsed = JSON.parse(localStorage.getItem("raceGraphics.raceStoryHistory") || "[]");
          raceStoryHistory = Array.isArray(parsed) ? parsed.slice(0, 5) : [];
        } catch (_err) {
          raceStoryHistory = [];
        }
        renderRaceStoryHistory();
      }

      function saveRaceStoryHistory() {
        try {
          localStorage.setItem("raceGraphics.raceStoryHistory", JSON.stringify(raceStoryHistory.slice(0, 5)));
        } catch (_err) {}
      }

      function recordRaceStorySent(spotlight, message) {
        const title = displayText(spotlight?.title, "Race Story");
        const detail = displayText(spotlight?.detail || message, "");
        const type = spotlight ? raceStoryTypeLabel(spotlight.type) : "Manual";
        const key = raceStoryReadyKey(spotlight, message) || `${title}|${detail}`;
        if (raceStoryHistory[0]?.key === key && Date.now() - Number(raceStoryHistory[0]?.at || 0) < 5000) return;
        raceStoryHistory = [{
          key,
          title,
          detail,
          type,
          at: Date.now(),
          sentAt: new Date().toISOString()
        }, ...raceStoryHistory].slice(0, 5);
        saveRaceStoryHistory();
        renderRaceStoryHistory();
      }

      function raceStoryChoiceItems(state) {
        const story = state?.raceStory || {};
        const sourceItems = [];
        if (story.primary) sourceItems.push({ item: story.primary, recommended: true });
        if (Array.isArray(story.items)) {
          story.items.forEach(item => sourceItems.push({ item, recommended: false }));
        }
        const seen = new Set();
        return sourceItems.reduce((items, source) => {
          const rawItem = source.item;
          const title = displayText(rawItem?.title);
          const detail = displayText(rawItem?.detail);
          if (!title && !detail) return items;
          const item = {
            title: title || "Race Story",
            detail,
            tone: displayText(rawItem?.tone, "live"),
            type: displayText(rawItem?.type, "story"),
            recommended: source.recommended
          };
          item.key = storyChoiceKey(item);
          if (!item.key || seen.has(item.key)) return items;
          seen.add(item.key);
          items.push(item);
          return items;
        }, []).slice(0, 6);
      }

      function isRaceStoryChoiceSelected(settings, item) {
        if (settings.raceStoryAutoUpdate !== false) return false;
        const heldKey = displayText(settings.raceStoryHeldKey);
        if (heldKey && heldKey === item.key) return true;
        return displayText(settings.raceStoryHeldTitle) === item.title
          && displayText(settings.raceStoryHeldDetail) === item.detail;
      }

      function applyHeldRaceStory(settings, item, state) {
        const meta = state?.meta || {};
        const raceState = state?.raceState || {};
        settings.raceStoryAutoUpdate = false;
        settings.raceStoryHeldTitle = item.title;
        settings.raceStoryHeldDetail = item.detail;
        settings.raceStoryHeldTone = item.tone || "live";
        settings.raceStoryHeldType = item.type || "story";
        settings.raceStoryHeldClass = displayText(meta.className || meta.session || meta.trackName, "");
        settings.raceStoryHeldMode = displayText(raceState.label || raceState.mode || meta.sessionName || meta.mode, "");
        settings.raceStoryHeldKey = item.key || storyChoiceKey(item);
      }

      function readRaceStoryControls(options = {}) {
        const settings = getStyleSettings();
        raceStoryInputKeys.forEach(key => {
          const input = styleInputs[key];
          if (input) settings[key] = readControlValue(key, input);
        });
        if (settings.raceStoryAutoUpdate === false) {
          const hasHeldStory = Boolean(displayText(settings.raceStoryHeldTitle) || displayText(settings.raceStoryHeldDetail));
          if (options.freeze && !hasHeldStory) holdRaceStoryFromState(settings, latestTimingState);
        } else {
          clearRaceStoryHold(settings);
        }
        return settings;
      }

      async function selectRaceStoryChoice(item) {
        const settings = readRaceStoryControls();
        applyHeldRaceStory(settings, item, latestTimingState || {});
        if (raceStoryAutoUpdate) raceStoryAutoUpdate.checked = false;
        const result = await saveServerStyle(settings);
        if (result.ok) {
          latestTimingState = { ...(latestTimingState || {}), style: result.style || settings };
          programStatus.textContent = `Race Story selected: ${item.title}`;
          renderRaceStoryPreview(latestTimingState);
        } else {
          programStatus.textContent = result.error || "Race Story selection failed";
        }
      }

      async function selectAutoRaceStory() {
        const settings = readRaceStoryControls();
        settings.raceStoryAutoUpdate = true;
        clearRaceStoryHold(settings);
        if (raceStoryAutoUpdate) raceStoryAutoUpdate.checked = true;
        const result = await saveServerStyle(settings);
        if (result.ok) {
          latestTimingState = { ...(latestTimingState || {}), style: result.style || settings };
          programStatus.textContent = "Race Story set to Auto Best";
          renderRaceStoryPreview(latestTimingState);
        } else {
          programStatus.textContent = result.error || "Race Story auto selection failed";
        }
      }

      function readBannerControls() {
        const settings = getStyleSettings();
        bannerStyleInputKeys.forEach(key => {
          const input = styleInputs[key];
          settings[key] = readControlValue(key, input);
        });
        settings.bannerTopFiveOpen = bannerTopFiveOpen.checked;
        settings.bannerAccentLinesOpen = true;
        settings.bannerTextureOpen = true;
        settings.bannerReflectionOpen = true;
        settings.bannerBevelOpen = true;
        settings.bannerManualInfo = bannerManualInfo.value.trim();
        Object.entries(bannerSelects).forEach(([key, input]) => {
          settings[key] = input.value;
        });
        settings.bannerMetricFields = bannerMetricFields.filter(input => input.checked).map(input => input.value);
        return settings;
      }

      function setBannerMetricFields(values = []) {
        const selected = new Set(values);
        bannerMetricFields.forEach(input => {
          input.checked = selected.has(input.value);
        });
      }

      function updateBannerModePresetButtons(activeKey = "") {
        bannerModePresetButtons.forEach(button => {
          const isActive = button.dataset.bannerModePreset === activeKey;
          button.classList.toggle("is-active", isActive);
          button.setAttribute("aria-pressed", isActive ? "true" : "false");
        });
      }

      async function applyBannerModePreset(key) {
        const preset = BANNER_MODE_PRESETS[key];
        if (!preset) return;
        const settings = readBannerControls();
        settings.bannerRaceMode = preset.bannerRaceMode;
        settings.bannerMetricFields = [...preset.bannerMetricFields];
        settings.bannerDurationMode = preset.bannerDurationMode;
        if (bannerSelects.bannerRaceMode) bannerSelects.bannerRaceMode.value = settings.bannerRaceMode;
        if (bannerSelects.bannerDurationMode) bannerSelects.bannerDurationMode.value = settings.bannerDurationMode;
        setBannerMetricFields(settings.bannerMetricFields);
        applyStyleSettings(settings);
        updateBannerModePresetButtons(key);
        renderControlPreviews(latestTimingState);
        const result = await saveServerStyle(settings);
        if (result.ok) {
          if (latestTimingState) latestTimingState = { ...latestTimingState, style: result.style || settings };
          programStatus.textContent = `${preset.label} banner preset applied`;
          if (topBannerVisible) await setTopBannerOutput(true);
        } else {
          programStatus.textContent = result.error || `${preset.label} banner preset failed`;
        }
      }

      function updateBannerDataButton(settings = getStyleSettings()) {
        const isOpen = settings.bannerDataOpen !== false;
        toggleBannerData.textContent = isOpen ? "Hide Race Data" : "Show Race Data";
        toggleBannerData.classList.toggle("active", !isOpen);
        toggleBannerData.setAttribute("aria-pressed", String(!isOpen));
        toggleBannerData.title = isOpen
          ? "Hide driver names, numbers, and timing data while keeping the banner structure."
          : "Show driver names, numbers, and timing data again.";
      }

      function updateBannerLogoStatus(settings = getStyleSettings()) {
        if (!bannerLogoStatus) return;
        const headerCount = Number(settings.bannerHeaderLogoCount || 0);
        const sponsorCount = Number(settings.bannerSponsorLogoCount || 0);
        const rotateSeconds = Number(settings.bannerLogoRotateSeconds || defaultStyleSettings.bannerLogoRotateSeconds);
        bannerLogoStatus.innerHTML = "";
        [
          ["Header logo", headerCount ? `${headerCount} loaded` : "Default"],
          ["Sponsor/series", sponsorCount ? `${sponsorCount} loaded` : "Default"],
          ["Logo rotation", `${rotateSeconds}s`]
        ].forEach(([label, value]) => {
          const item = document.createElement("div");
          const labelNode = document.createElement("span");
          const valueNode = document.createElement("strong");
          labelNode.textContent = label;
          valueNode.textContent = value;
          item.append(labelNode, valueNode);
          bannerLogoStatus.appendChild(item);
        });
      }

      function readTop3Controls() {
        const settings = getStyleSettings();
        settings.top3TitleSource = top3TitleSource.value;
        settings.top3Title = top3Title.value.trim();
        settings.top3Subtitle = top3Subtitle.value.trim();
        settings.top3Source = top3Source.value;
        settings.top3Layout = "photo";
        settings.top3Drivers = top3DriverInputs.map(({ number, name }) => ({
          number: number.value.trim(),
          name: name.value.trim()
        }));
        return settings;
      }

      loadServerStyle().then(() => {
        loadStyleControls();
        requestAnimationFrame(resizeBroadcastPreviewFrames);
        requestAnimationFrame(syncDataPanelHeight);
      });
      window.addEventListener("resize", () => {
        resizeBroadcastPreviewFrames();
        syncDataPanelHeight();
      });
      graphicPreviewFrames.forEach(frame => {
        frame.addEventListener("load", resizeBroadcastPreviewFrames);
      });
      if ("ResizeObserver" in window) {
        const dataPanelObserver = new ResizeObserver(syncDataPanelHeight);
        dataPanelObserver.observe(stylePanel);
        dataPanelObserver.observe(upperLeftStack);
      }
      Object.entries(styleInputs).forEach(([key, input]) => {
        if (bannerStyleInputKeys.includes(key)) return;
        const previewGeneralStyle = () => {
          applyStyleSettings(readStyleControls());
          renderControlPreviews(latestTimingState);
        };
        input.oninput = previewGeneralStyle;
        input.onchange = previewGeneralStyle;
      });
      colorInputKeys.forEach(key => {
        const colorInput = styleInputs[key];
        const codeInput = colorCodeInputs[key];
        colorInput.addEventListener("input", () => {
          setColorControl(key, colorInput.value);
        });
        codeInput.addEventListener("input", () => {
          const color = normalizeHexColor(codeInput.value);
          codeInput.classList.toggle("invalid", Boolean(codeInput.value.trim()) && !color);
          if (!color) return;
          setColorControl(key, color);
          if (bannerStyleInputKeys.includes(key)) {
            programStatus.textContent = "Top banner changes pending";
          } else {
            applyStyleSettings(readStyleControls());
          }
        });
      });

      document.querySelectorAll("[data-step-for]").forEach(button => {
        button.addEventListener("click", () => {
          const input = document.querySelector(`#${button.dataset.stepFor}`);
          if (!input) return;
          if (button.dataset.stepDir === "down") input.stepDown();
          else input.stepUp();
          input.dispatchEvent(new Event("input", { bubbles: true }));
          input.dispatchEvent(new Event("change", { bubbles: true }));
        });
      });

      document.querySelector("#applyStyle").onclick = async () => {
        const result = await saveServerStyle(readGeneralStyleControls());
        if (!result.ok) programStatus.textContent = result.error || "Style apply failed";
      };

      async function applyBannerColorsToAllGraphics() {
        const settings = readStyleControls();
        settings.accentColor = settings.bannerAccentLineColor || settings.accentColor;
        settings.numberColor = settings.bannerAccentLineColor || settings.numberColor;
        settings.metricColor = settings.bannerLightCellColor || settings.metricColor;
        colorInputKeys.forEach(key => {
          if (settings[key]) setColorControl(key, settings[key]);
        });
        applyStyleSettings(settings);
        const result = await saveServerStyle(settings);
        programStatus.textContent = result.ok ? "Top banner colors applied to all graphics" : result.error || "Color apply failed";
        if (result.ok) loadStyleControls();
      }

      document.querySelector("#applyBannerColorsAll").onclick = applyBannerColorsToAllGraphics;

      const runDemoRaceButton = document.querySelector("#runDemoRace");
      if (runDemoRaceButton) {
        runDemoRaceButton.onclick = () => guardLiveControl(runDemoRaceButton, "Play Demo Race", async () => {
          const result = await postJson("/api/demo-race", {});
          programStatus.textContent = result.ok
            ? (result.alreadyRunning ? "Demo race already running" : "Demo race running (about 60 seconds) - watch the banner and tower")
            : result.error || "Demo race failed to start";
        });
      }

      async function resetBannerColorsToDefault() {
        const settings = getStyleSettings();
        [...colorInputKeys, "accentColor", "numberColor", "metricColor"].forEach(key => {
          settings[key] = defaultStyleSettings[key];
        });
        colorInputKeys.forEach(key => setColorControl(key, settings[key]));
        applyStyleSettings(settings);
        const result = await saveServerStyle(settings);
        programStatus.textContent = result.ok ? "Colors reset to defaults on all graphics" : result.error || "Color reset failed";
        if (result.ok) loadStyleControls();
      }

      document.querySelector("#resetBannerColors").onclick = resetBannerColorsToDefault;

      document.querySelector("#applyFooter").onclick = async () => {
        const result = await saveServerStyle(readFooterControls());
        if (!result.ok) programStatus.textContent = result.error || "Bottom bar apply failed";
      };

      document.querySelector("#applyMissingData").onclick = async () => {
        const result = await saveServerStyle(readMissingDataControls());
        programStatus.textContent = result.ok ? "Missing data display applied" : result.error || "Missing data apply failed";
      };

      document.querySelector("#applyWeatherGraphic").onclick = async () => {
        const weatherSettings = readWeatherControls();
        const result = await saveServerStyle(weatherSettings);
        if (!result.ok) {
          programStatus.textContent = result.error || "Weather apply failed";
          return;
        }
        programStatus.textContent = "Weather settings applied, loading weather data";
        try {
          const weather = await fetch("/api/weather?force=1", { cache: "no-store" }).then(response => response.json());
          latestWeatherPreviewData = weather;
          weatherPreviewLastFetchAt = Date.now();
          weatherPreviewSignature = "";
          updateWeatherTickerMiniPreview(weatherSettings, latestTimingState?.meta || {}, weather);
          programStatus.textContent = weather.ok ? "Weather data ready" : weather.error || "Weather data unavailable";
        } catch (_err) {
          latestWeatherPreviewData = {
            ok: false,
            requestedLocation: weatherPreviewRequestLocation(weatherSettings, latestTimingState?.meta || {}),
            error: "Weather settings applied, weather data unavailable"
          };
          weatherPreviewLastFetchAt = Date.now();
          updateWeatherTickerMiniPreview(weatherSettings, latestTimingState?.meta || {}, latestWeatherPreviewData);
          programStatus.textContent = "Weather settings applied, weather data unavailable";
        }
      };

      document.querySelector("#applyTickerGraphic").onclick = async () => {
        const result = await saveServerStyle(readTickerControls());
        programStatus.textContent = result.ok ? "Ticker settings applied" : result.error || "Ticker apply failed";
      };

      document.querySelector("#applyRaceStorySettings").onclick = async () => {
        const result = await saveServerStyle(readRaceStoryControls({ freeze: true }));
        programStatus.textContent = result.ok ? "Race Story settings applied" : result.error || "Race Story apply failed";
      };

      document.querySelector("#applyBanner").onclick = async () => {
        const settings = readBannerControls();
        const result = await saveServerStyle(settings);
        if (result.ok) {
          updateBannerLogoStatus(result.style || settings);
          const refreshed = await sendTopBannerOnly();
          programStatus.textContent = refreshed ? "Banner settings applied and refreshed" : "Banner settings applied";
        } else {
          programStatus.textContent = result.error || "Banner apply failed";
        }
      };

      async function toggleTopBannerRaceData() {
        const settings = getStyleSettings();
        settings.bannerDataOpen = settings.bannerDataOpen === false;
        const result = await saveServerStyle(settings);
        if (result.ok) {
          const nextSettings = result.style || settings;
          updateBannerDataButton(nextSettings);
          if (topBannerVisible) await setTopBannerOutput(true);
          programStatus.textContent = nextSettings.bannerDataOpen ? "Banner race data shown" : "Banner race data hidden";
          rerenderRaceReady();
        } else {
          programStatus.textContent = result.error || "Race data toggle failed";
        }
      }

      toggleBannerData.onclick = () => {
        const label = toggleBannerData.textContent.trim() || "Toggle Race Data";
        return guardLiveControl(toggleBannerData, label, toggleTopBannerRaceData);
      };

      document.querySelector("#resetBanner").onclick = async () => {
        const settings = getStyleSettings();
        bannerResetKeys.forEach(key => {
          settings[key] = Array.isArray(defaultStyleSettings[key])
            ? [...defaultStyleSettings[key]]
            : defaultStyleSettings[key];
        });
        const result = await saveServerStyle(settings);
        if (result.ok) {
          loadStyleControls();
          programStatus.textContent = "Banner reset; template kept";
        } else {
          programStatus.textContent = result.error || "Banner reset failed";
        }
      };

      function fileToDataUrl(file) {
        return new Promise((resolve, reject) => {
          const reader = new FileReader();
          reader.onload = () => resolve(reader.result);
          reader.onerror = reject;
          reader.readAsDataURL(file);
        });
      }

      function uploadBannerLogos(input, side) {
        input.onchange = () => {
          const files = [...(input.files || [])];
          if (!files.length) return;
          Promise.all(files.map(fileToDataUrl)).then(async dataUrls => {
            const result = await postJson("/api/banner-logo", { side, dataUrls });
            if (result.ok && result.style) {
              saveStyleSettings(result.style);
              updateBannerLogoStatus(result.style);
              programStatus.textContent = `${side === "sponsor" ? "Sponsor/series" : "Header"} logos updated`;
            } else {
              programStatus.textContent = result.error || "Banner logo upload failed";
            }
          }).catch(() => {
            programStatus.textContent = "Banner logo upload failed";
          });
        };
      }

      uploadBannerLogos(document.querySelector("#bannerHeaderLogos"), "header");
      uploadBannerLogos(document.querySelector("#bannerSponsorLogos"), "sponsor");

      function uploadTop3Photo(input, position) {
        input.onchange = () => {
          const file = input.files && input.files[0];
          if (!file) return;
          fileToDataUrl(file).then(async dataUrl => {
            const result = await postJson("/api/top3-photo", { position, dataUrl });
            if (result.ok && result.style) {
              saveStyleSettings(result.style);
              programStatus.textContent = `P${position} photo updated`;
            } else {
              programStatus.textContent = result.error || "Driver photo upload failed";
            }
          }).catch(() => {
            programStatus.textContent = "Driver photo upload failed";
          });
        };
      }

      top3DriverInputs.forEach(({ photo, position }) => uploadTop3Photo(photo, position));

      document.querySelectorAll("[data-clear-top3-photo]").forEach(button => {
        button.onclick = async () => {
          const position = Number(button.dataset.clearTop3Photo);
          const result = await postJson("/api/top3-photo", { position, clear: true });
          if (result.ok && result.style) {
            const input = document.querySelector(`#top3Photo${position}`);
            if (input) input.value = "";
            saveStyleSettings(result.style);
            programStatus.textContent = `P${position} photo cleared`;
          } else {
            programStatus.textContent = result.error || "Driver photo clear failed";
          }
        };
      });

      document.querySelector("#applyTop3").onclick = async () => {
        const result = await saveServerStyle(readTop3Controls());
        programStatus.textContent = result.ok ? "Top 3 feature board applied" : result.error || "Top 3 apply failed";
      };

      document.querySelector("#clearTop3Manual").onclick = async () => {
        top3DriverInputs.forEach(({ number, name }) => {
          number.value = "";
          name.value = "";
        });
        const settings = readTop3Controls();
        settings.top3Source = top3Source.value === "manual" ? "qualifying" : top3Source.value;
        const result = await saveServerStyle(settings);
        if (result.ok) {
          top3Source.value = settings.top3Source;
          programStatus.textContent = "Manual Top 3 fields cleared";
        } else {
          programStatus.textContent = result.error || "Manual Top 3 clear failed";
        }
      };

      [
        bannerTopFiveOpen,
        bannerManualInfo,
        ...Object.values(bannerSelects),
        ...bannerMetricFields,
        styleInputs.bannerLogoRotateSeconds,
        styleInputs.bannerMetricRotateSeconds,
        styleInputs.bannerNameSize,
        styleInputs.bannerMetricSize,
        styleInputs.bannerInfoSize,
        styleInputs.bannerDurationSize,
        styleInputs.bannerOpacity,
        styleInputs.bannerFlyoutOpacity,
        styleInputs.bannerLogoPanelColor,
        styleInputs.bannerDarkCellColor,
        styleInputs.bannerLightCellColor,
        styleInputs.bannerFlyoutColor,
        styleInputs.bannerAccentLineColor
      ].filter(Boolean).forEach(input => {
        input.oninput = () => {
          if (input === styleInputs.bannerLogoRotateSeconds) updateBannerLogoStatus(readBannerControls());
          programStatus.textContent = "Top banner changes pending";
        };
        input.onchange = () => {
          if (input === styleInputs.bannerLogoRotateSeconds) updateBannerLogoStatus(readBannerControls());
          programStatus.textContent = "Top banner changes pending";
        };
      });

      const liveBannerControlInputs = new Set([
        bannerTopFiveOpen,
        bannerManualInfo,
        ...Object.values(bannerSelects),
        ...bannerMetricFields
      ].filter(Boolean));
      async function autoApplyBannerControls(input) {
        if (!liveBannerControlInputs.has(input)) return;
        const settings = readBannerControls();
        const result = await saveServerStyle(settings);
        if (result.ok) {
          updateBannerLogoStatus(result.style || settings);
          programStatus.textContent = "Banner settings applied";
          if (topBannerVisible) await setTopBannerOutput(true);
        } else {
          programStatus.textContent = result.error || "Banner apply failed";
        }
      }
      liveBannerControlInputs.forEach(input => {
        input.addEventListener("change", () => autoApplyBannerControls(input));
      });

      bannerModePresetButtons.forEach(button => {
        button.onclick = () => applyBannerModePreset(button.dataset.bannerModePreset);
      });

      [top3TitleSource, top3Title, top3Subtitle, top3Source, top3Layout, ...top3DriverInputs.flatMap(driver => [driver.number, driver.name])].forEach(input => {
        input.oninput = () => {
          programStatus.textContent = "Top 3 changes pending";
        };
        input.onchange = () => {
          programStatus.textContent = "Top 3 changes pending";
        };
      });

      document.querySelector("#resetStyle").onclick = async () => {
        await saveServerStyle({ ...defaultStyleSettings });
        loadStyleControls();
      };

      function renderCorrections(state) {
        const rows = state.runningOrder.length ? state.runningOrder : state.competitors;
        const missingRows = rows.filter(driver => hasReviewableDriver(driver) && driver.missingData);
        correctionList.innerHTML = "";

        if (!missingRows.length) {
          correctionList.textContent = "No missing driver rows right now.";
          return;
        }

        const missingTitle = document.createElement("strong");
        missingTitle.textContent = "Missing positions";
        correctionList.appendChild(missingTitle);
        missingRows.slice(0, 12).forEach(driver => {
          const item = document.createElement("span");
          item.className = "correction-chip";
          item.textContent = `P${driver.position}`;
          correctionList.appendChild(item);
        });
      }

      function setTimingPlaceholder() {
        timingUrl.placeholder = timingSource.value === "racemonitor"
          ? "https://www.race-monitor.com/Live/Race/#####"
          : "https://srv.myracepass.com/v3/broadcasts/ticker/...";
      }

      function loadStoredTimingUrl() {
        timingUrl.value = localStorage.getItem(sourceUrlKeys[timingSource.value]) || "";
      }

      const storedSource = localStorage.getItem("raceGraphics.lastTimingSource");
      if (storedSource === "racemonitor" || storedSource === "myracepass") {
        timingSource.value = storedSource;
      }
      setTimingPlaceholder();
      loadStoredTimingUrl();

      timingSource.onchange = () => {
        localStorage.setItem("raceGraphics.lastTimingSource", timingSource.value);
        setTimingPlaceholder();
        loadStoredTimingUrl();
      };

      timingUrl.oninput = () => {
        localStorage.setItem(sourceUrlKeys[timingSource.value], timingUrl.value.trim());
      };

      function sourceLabel(value) {
        if (value === "racemonitor") return "Race Monitor";
        if (value === "myracepass") return "MyRacePass";
        return value || "None";
      }

      function formatAge(value) {
        if (!value) return "No updates";
        const time = new Date(value).getTime();
        if (!Number.isFinite(time)) return "No updates";
        const seconds = Math.max(0, Math.round((Date.now() - time) / 1000));
        if (seconds < 2) return "Now";
        if (seconds < 60) return `${seconds}s ago`;
        const minutes = Math.floor(seconds / 60);
        if (minutes < 60) return `${minutes}m ago`;
        const hours = Math.floor(minutes / 60);
        return `${hours}h ago`;
      }

      function detectedMode(meta) {
        const text = `${meta.mode || ""} ${meta.session || ""}`.toLowerCase();
        if (/\b(qual|practice|hot\s*laps?|hotlaps|time trials?|best lap|competitors)\b/.test(text)) return "Qualifying";
        if (/\b(race|feature|heat|main|dash)\b/.test(text)) return "Race";
        return meta.mode || "Auto";
      }

      function connectionHealth(meta) {
        const live = meta.live || {};
        if (live.error) return { status: "Issue", tone: "bad", detail: live.error };
        if (live.connected) return { status: "Connected", tone: "good", detail: sourceLabel(live.provider || meta.source) };
        if (live.connecting) return { status: "Connecting", tone: "warn", detail: sourceLabel(live.provider || meta.source) };
        if (meta.source && meta.source !== "none") return { status: "Data loaded", tone: "warn", detail: sourceLabel(meta.source) };
        return { status: "Timing idle", tone: "idle", detail: "No source" };
      }

      function renderLiveHealth(state, rows, missingCount) {
        const metaData = state.meta || {};
        const live = metaData.live || {};
        const health = connectionHealth(metaData);
        const lastSignal = metaData.lastUpdated || live.lastPoll || "";
        liveHealthSummary.innerHTML = "";
        const dot = document.createElement("span");
        dot.className = `health-dot is-${health.tone}`;
        const summaryText = document.createElement("strong");
        summaryText.textContent = health.status;
        const summaryDetail = document.createElement("em");
        summaryDetail.textContent = health.detail;
        liveHealthSummary.append(dot, summaryText, summaryDetail);

        liveHealthStrip.innerHTML = "";
        [
          ["Source", sourceLabel(metaData.source || live.provider)],
          ["Updated", formatAge(lastSignal)],
          ["Drivers", rows.length],
          ["Missing", missingCount],
          ["Flag", metaData.flagLabel || metaData.flag || "-"],
          ["Mode", detectedMode(metaData)]
        ].forEach(([label, value]) => {
          const item = document.createElement("div");
          const labelNode = document.createElement("span");
          const valueNode = document.createElement("strong");
          labelNode.textContent = label;
          valueNode.textContent = value;
          item.append(labelNode, valueNode);
          liveHealthStrip.appendChild(item);
        });
      }

      function renderRaceIntelligence(state) {
        if (!raceIntelStrip) return;
        const raceState = state.raceState || {};
        const story = state.raceStory || {};
        const database = state.driverDatabase || {};
        const primary = story.primary || {};
        const clean = value => String(value || "").trim();
        const profileCount = Number(database.profileCount || 0);
        const recoveredCount = Number(database.fallbacksApplied || 0);
        const cardData = [
          ["Race State", clean(raceState.label) || "Waiting", clean(raceState.mode) || "Auto"],
          ["Lead Story", clean(primary.title) || "Collecting", clean(primary.detail) || "No standout story yet"],
          ["Fastest Lap", clean(story.fastestLap?.name) || "-", clean(story.fastestLap?.time) || "No lap time"],
          ["Biggest Mover", clean(story.biggestMover?.name) || "-", story.biggestMover ? `+${story.biggestMover.gained}` : "No movement yet"],
          [
            "Name Backup",
            `${profileCount} saved`,
            recoveredCount ? `${recoveredCount} timing names recovered` : "Ready to fix broken names"
          ]
        ];
        raceIntelStrip.innerHTML = "";
        cardData.forEach(([label, value, detail]) => {
          const item = document.createElement("div");
          item.className = "race-intel-card";
          const labelNode = document.createElement("span");
          const valueNode = document.createElement("strong");
          const detailNode = document.createElement("em");
          labelNode.textContent = label;
          valueNode.textContent = value;
          detailNode.textContent = detail;
          item.append(labelNode, valueNode, detailNode);
          raceIntelStrip.appendChild(item);
        });
      }

      function raceStoryReadyKey(spotlight, message) {
        return [
          displayText(spotlight?.type),
          displayText(spotlight?.title),
          displayText(spotlight?.detail),
          displayText(message)
        ].join("|").toLowerCase();
      }

      function playRaceStorySoundAlert() {
        if (!raceStorySound?.checked) return;
        const nowMs = Date.now();
        if (nowMs - raceStoryLastSoundAt < 4500) return;
        const AudioContext = window.AudioContext || window.webkitAudioContext;
        if (!AudioContext) return;
        try {
          raceStoryAudioContext = raceStoryAudioContext || new AudioContext();
          const context = raceStoryAudioContext;
          const makeTone = (startOffset, frequency, duration) => {
            const start = context.currentTime + startOffset;
            const oscillator = context.createOscillator();
            const gain = context.createGain();
            oscillator.type = "sine";
            oscillator.frequency.setValueAtTime(frequency, start);
            gain.gain.setValueAtTime(0.0001, start);
            gain.gain.exponentialRampToValueAtTime(0.075, start + 0.025);
            gain.gain.exponentialRampToValueAtTime(0.0001, start + duration);
            oscillator.connect(gain);
            gain.connect(context.destination);
            oscillator.start(start);
            oscillator.stop(start + duration + 0.02);
          };
          const play = () => {
            makeTone(0, 740, 0.16);
            makeTone(0.18, 980, 0.18);
            raceStoryLastSoundAt = nowMs;
          };
          if (context.state === "suspended") {
            context.resume().then(play).catch(() => {});
          } else {
            play();
          }
        } catch (_err) {}
      }

      function raceStoryAirCheck(state, message) {
        const raceState = state.raceState || {};
        const story = state.raceStory || {};
        const primaryType = String(story.primary?.type || "");
        const hasMessage = Boolean(displayText(message));
        if (!hasMessage) {
          return {
            tone: "idle",
            label: "Wait",
            guidance: "No automatic race story is available yet."
          };
        }
        if (raceState.isStale || raceState.phase === "stale") {
          return {
            tone: "bad",
            label: "Manual safer",
            guidance: "Timing may be stale. Wait until the timing feed catches up."
          };
        }
        if (!raceState.hasDrivers) {
          return {
            tone: "bad",
            label: "Do not use yet",
            guidance: "No driver list is loaded yet. Leave Race Story off."
          };
        }
        if (["lead-change", "top-qualifier", "winner", "fastest-lap", "closest-battle", "biggest-mover"].includes(primaryType)) {
          return {
            tone: "good",
            label: "Good to air",
            guidance: "This has a real race story."
          };
        }
        if (raceState.phase === "pre-race") {
          return {
            tone: "warn",
            label: "Use between races",
            guidance: "There is not a standout timing story yet."
          };
        }
        return {
          tone: "warn",
          label: "Use if helpful",
          guidance: "This is usable, but a manual ticker may be stronger if you have a specific announcement."
        };
      }

      function raceStoryReadiness(state) {
        const baseState = state || {};
        const settings = baseState.style || getStyleSettings();
        const liveState = raceStoryLiveState(baseState);
        const usesHeldStory = settings.raceStoryAutoUpdate === false;
        const spotlight = raceStorySpotlightItem(baseState);
        const autoMessage = usesHeldStory && spotlight
          ? `${spotlight.title}${spotlight.detail ? `: ${spotlight.detail}` : ""}`
          : raceStoryTickerMessage(liveState);
        const checkState = usesHeldStory && spotlight
          ? { ...baseState, raceStory: { ...(baseState.raceStory || {}), primary: spotlight } }
          : liveState;
        const airCheck = raceStoryAirCheck(checkState, autoMessage);
        const canSendStory = Boolean(
          spotlight
          && displayText(autoMessage)
          && !["idle", "bad"].includes(airCheck.tone)
        );
        return { liveState, autoMessage, spotlight, airCheck, canSendStory };
      }

      function syncRaceStoryActionButtons(canSendStory = false, airCheck = { label: "Wait" }) {
        if (sendRaceStoryButton) {
          sendRaceStoryButton.disabled = !canSendStory;
          sendRaceStoryButton.textContent = canSendStory ? "Send Race Story" : "Wait";
          sendRaceStoryButton.classList.toggle("is-story-ready", canSendStory);
          sendRaceStoryButton.title = canSendStory
            ? "Send the current Race Story pop-up to Program Output"
            : "No Race Story is ready to send yet";
        }
        if (hideRaceStoryButton) {
          hideRaceStoryButton.disabled = programGraphic !== "story";
          hideRaceStoryButton.textContent = programGraphic === "story" ? "Hide Race Story" : "Race Story Hidden";
        }
        if (raceStoryAlert && canSendStory) {
          raceStoryAlert.title = "Race Story is ready to send.";
        } else if (raceStoryAlert) {
          raceStoryAlert.title = airCheck?.label || "Waiting for a stronger race cue.";
        }
      }

      function setRaceStoryStatusCard(card, tone, label, value, detail) {
        if (!card) return;
        card.className = `race-story-status-card ${itemToneClass(tone)}`;
        const labelEl = card.querySelector("span");
        const valueEl = card.querySelector("strong");
        const detailEl = card.querySelector("em");
        if (labelEl) labelEl.textContent = label;
        if (valueEl) valueEl.textContent = value;
        if (detailEl) detailEl.textContent = detail;
      }

      function updateRaceStoryStatusStrip(state, canSendStory, airCheck) {
        const meta = state?.meta || {};
        const live = meta.live || {};
        const provider = live.provider === "myracepass"
          ? "MyRacePass"
          : live.provider === "racemonitor"
            ? "Race Monitor"
            : displayText(meta.source, "Timing");
        if (live.connected) {
          setRaceStoryStatusCard(raceStoryTimingStatus, "good", "Timing", "Connected", provider);
        } else if (live.connecting) {
          setRaceStoryStatusCard(raceStoryTimingStatus, "warn", "Timing", "Connecting", provider);
        } else if (live.error) {
          setRaceStoryStatusCard(raceStoryTimingStatus, "bad", "Timing", "Issue", live.error);
        } else {
          setRaceStoryStatusCard(raceStoryTimingStatus, "idle", "Timing", "Waiting", "Connect timing first");
        }

        if (canSendStory) {
          setRaceStoryStatusCard(raceStoryCueStatus, "good", "Story Cue", "Ready", "Send when it fits the show");
        } else {
          setRaceStoryStatusCard(
            raceStoryCueStatus,
            airCheck?.tone || "idle",
            "Story Cue",
            airCheck?.label || "Waiting",
            airCheck?.guidance || "No strong cue yet"
          );
        }

        if (programGraphic === "story") {
          setRaceStoryStatusCard(raceStoryOutputStatus, "live", "Output", "On Program", "Race Story is on screen");
        } else if (previewGraphic === "story") {
          setRaceStoryStatusCard(raceStoryOutputStatus, "warn", "Output", "Previewed", "Use Send Race Story");
        } else {
          setRaceStoryStatusCard(
            raceStoryOutputStatus,
            canSendStory ? "warn" : "idle",
            "Output",
            "Hidden",
            canSendStory ? "Ready, not on screen" : "Not on screen"
          );
        }
      }

      function renderRaceStoryChoices(state) {
        if (!raceStoryChoiceList) return;
        const style = state.style || getStyleSettings();
        const choices = raceStoryChoiceItems(state);
        const visibleChoices = choices;
        const autoMode = style.raceStoryAutoUpdate !== false;
        if (raceStoryAutoBestButton) {
          raceStoryAutoBestButton.classList.toggle("is-active", autoMode);
          raceStoryAutoBestButton.setAttribute("aria-pressed", autoMode ? "true" : "false");
        }
        if (raceStorySelectionStatus) {
          const selectedChoice = choices.find(item => isRaceStoryChoiceSelected(style, item));
          raceStorySelectionStatus.textContent = autoMode
            ? "Auto best story selected"
            : selectedChoice
              ? `Locked on ${selectedChoice.title}`
              : "Manual story selected";
        }
        raceStoryChoiceList.innerHTML = "";
        if (!visibleChoices.length) {
          const empty = document.createElement("button");
          empty.className = "race-story-choice is-empty";
          empty.type = "button";
          empty.disabled = true;
          const label = document.createElement("span");
          const title = document.createElement("strong");
          const detail = document.createElement("em");
          label.textContent = "Waiting";
          title.textContent = "No story options yet";
          detail.textContent = "Suggestions appear when timing creates a real race cue.";
          empty.append(label, title, detail);
          raceStoryChoiceList.appendChild(empty);
          return;
        }
        visibleChoices.forEach((item, index) => {
          const button = document.createElement("button");
          button.className = `race-story-choice ${itemToneClass(item.tone)}`;
          button.type = "button";
          button.setAttribute("role", "listitem");
          const selected = isRaceStoryChoiceSelected(style, item);
          button.classList.toggle("is-selected", selected);
          button.classList.toggle("is-recommended", Boolean(item.recommended));
          button.setAttribute("aria-pressed", selected ? "true" : "false");
          const metaRow = document.createElement("span");
          const tag = document.createElement("span");
          const badge = document.createElement("b");
          const title = document.createElement("strong");
          const detail = document.createElement("em");
          metaRow.className = "race-story-choice-meta";
          tag.className = "race-story-choice-type";
          tag.textContent = `${index + 1}. ${raceStoryTypeLabel(item.type)}`;
          badge.textContent = item.recommended ? "Best" : "";
          if (item.recommended) metaRow.append(tag, badge);
          else metaRow.append(tag);
          title.textContent = item.title;
          detail.textContent = item.detail || "Ready to show";
          button.append(metaRow, title, detail);
          button.onclick = () => selectRaceStoryChoice(item);
          raceStoryChoiceList.appendChild(button);
        });
      }

      function renderRaceStoryPreview(state) {
        if (!raceStoryPreview) return;
        const style = state.style || getStyleSettings();
        const { autoMessage, spotlight, airCheck, canSendStory } = raceStoryReadiness(state);
        const positionLabel = style.raceStoryPosition === "bottom-left" ? "Bottom left" : "Bottom center";
        const readyKey = canSendStory ? raceStoryReadyKey(spotlight, autoMessage) : "";
        const statusLabel = canSendStory ? "Send Race Story" : airCheck.label;
        const updateLabel = style.raceStoryAutoUpdate === false ? "Auto update off" : "Auto update on";
        const guidance = autoMessage
          ? `${airCheck.guidance} Use Send Race Story when you want the pop-up on screen.`
          : "No strong automatic story yet. Leave Race Story off until a real timing cue is available.";
        raceStoryPanel?.classList.toggle("is-story-ready", canSendStory);
        raceStoryOutputButton?.classList.toggle("is-story-ready", canSendStory);
        syncRaceStoryActionButtons(canSendStory, airCheck);
        updateRaceStoryStatusStrip(state, canSendStory, airCheck);
        if (raceStoryOutputButton) {
          raceStoryOutputButton.title = canSendStory
            ? "Race Story is ready to send"
            : "Race Story is waiting for a real timing cue";
        }
        if (raceStoryAlert) {
          raceStoryAlert.textContent = canSendStory ? "Story Ready" : "Waiting";
          raceStoryAlert.className = `race-story-alert ${canSendStory ? "is-ready" : `is-${airCheck.tone}`}`;
        }
        renderRaceStoryChoices(state);
        if (canSendStory && readyKey && readyKey !== raceStoryLastReadyKey) {
          playRaceStorySoundAlert();
          raceStoryLastReadyKey = readyKey;
        } else if (!canSendStory) {
          raceStoryLastReadyKey = "";
        }
        raceStoryPreview.className = `race-story-preview is-${airCheck.tone}`;
        raceStoryPreview.innerHTML = "";
        const head = document.createElement("div");
        head.className = "race-story-preview-head";
        const title = document.createElement("strong");
        const source = document.createElement("span");
        title.textContent = "Selected Story";
        source.textContent = `${updateLabel} | Pop-up position: ${positionLabel}`;
        head.append(title, source);

        const body = document.createElement("div");
        body.className = "race-story-preview-body";
        const pill = document.createElement("button");
        const guide = document.createElement("span");
        pill.className = "race-story-preview-pill";
        pill.type = "button";
        pill.disabled = !canSendStory;
        pill.title = canSendStory ? "Send Race Story to Program Output" : "No Race Story is ready to send yet";
        guide.className = "race-story-preview-guidance";
        pill.textContent = statusLabel;
        if (canSendStory) {
          pill.onclick = () => {
            sendRaceStoryOutput();
          };
        }
        guide.textContent = guidance;
        body.append(pill, guide);

        const storyCard = document.createElement("div");
        storyCard.className = "race-story-preview-card";
        const storyKicker = document.createElement("span");
        const storyTitle = document.createElement("strong");
        const storyDetail = document.createElement("em");
        const storyMeta = document.createElement("small");
        storyKicker.textContent = spotlight ? raceStoryTypeLabel(spotlight.type) : "Waiting";
        storyTitle.textContent = spotlight ? spotlight.title : "No story ready";
        storyDetail.textContent = spotlight?.detail || "Suggestions will appear when timing creates a real cue.";
        storyMeta.textContent = canSendStory ? "Ready for Program Output" : airCheck.guidance || "Waiting for timing";
        storyCard.append(storyKicker, storyTitle, storyDetail, storyMeta);

        const output = document.createElement("div");
        output.className = "race-story-preview-output";
        const outputLabel = document.createElement("span");
        const outputText = document.createElement("strong");
        outputLabel.textContent = "Pop-up text";
        outputText.textContent = spotlight ? `${spotlight.title}: ${spotlight.detail || "Race story ready"}` : "No automatic story yet.";
        output.append(outputLabel, outputText);

        const spotlightOutput = document.createElement("div");
        spotlightOutput.className = "race-story-preview-output";
        const spotlightLabel = document.createElement("span");
        const spotlightText = document.createElement("strong");
        spotlightLabel.textContent = "Auto cue";
        spotlightText.textContent = autoMessage || "No automatic race story yet.";
        spotlightOutput.append(spotlightLabel, spotlightText);

        raceStoryPreview.append(head, body, storyCard, output, spotlightOutput);
      }

      function toneRank(tone) {
        if (tone === "bad") return 3;
        if (tone === "warn") return 2;
        if (tone === "good") return 1;
        if (tone === "live") return 1;
        return 0;
      }

      function itemToneClass(tone) {
        if (tone === "good" || tone === "warn" || tone === "bad" || tone === "live") return `is-${tone}`;
        return "is-idle";
      }

      function dataAgeSeconds(meta) {
        const live = meta.live || {};
        const lastSignal = meta.lastUpdated || live.lastPoll || "";
        const time = new Date(lastSignal).getTime();
        if (!Number.isFinite(time)) return null;
        return Math.max(0, Math.round((Date.now() - time) / 1000));
      }

      function hasDriverName(driver) {
        return Boolean(displayText(driver?.name) || displayText(driver?.firstName) || displayText(driver?.lastName) || displayText(driver?.fallbackName));
      }

      function hasReviewableDriver(driver) {
        return Boolean(driver && (driverNumber(driver) || hasDriverName(driver)));
      }

      function readyPageTone(value) {
        if (value === true) return { tone: "good", value: "Loaded", detail: "Page reachable" };
        if (value === false) return { tone: "bad", value: "No response", detail: "Open/restart the local server" };
        return { tone: "warn", value: "Checking", detail: "Waiting for page check" };
      }

      function overlayBaseReady() {
        const base = normalizedOverlayBase();
        const host = overlayHost(base);
        if (!base || !host) return { tone: "bad", value: "Missing", detail: "Set Overlay address for vMix" };
        if (isLocalOverlayBase(base)) return { tone: "warn", value: "Local only", detail: "Use the LAN address for vMix" };
        if (isPrivateLanHost(host)) {
          const knownHosts = new Set((networkInfo?.lanBases || []).map(item => item.address));
          if (!knownHosts.size || knownHosts.has(host)) return { tone: "good", value: host, detail: "LAN address selected" };
          return { tone: "warn", value: host, detail: "LAN address may be stale" };
        }
        return { tone: "warn", value: host, detail: "Verify vMix can reach this address" };
      }

      function renderReadyItem(item) {
        const node = document.createElement("div");
        node.className = `race-ready-card ${itemToneClass(item.tone)}`;
        node.dataset.readyLabel = item.label;
        const label = document.createElement("span");
        const value = document.createElement("strong");
        const detail = document.createElement("em");
        label.textContent = item.label;
        value.textContent = item.value;
        detail.textContent = item.detail || "";
        node.append(label, value, detail);
        return node;
      }

      function nextReadyStep(primary, blocked, warnings) {
        if (!primary) {
          return {
            title: "Ready for output",
            action: "Next: press Send / Refresh Output when you are ready.",
            note: "Race Ready: clear"
          };
        }
        const label = String(primary.label || "").toLowerCase();
        let action = primary.detail || "Review this item before sending graphics.";
        if (label.includes("vmix address")) action = "Set or confirm the vMix LAN address.";
        else if (label.includes("program output")) action = "Choose a graphic below, then press Send / Refresh Output.";
        else if (label.includes("banner data")) action = "Use Show Race Data when you are ready to reveal timing data.";
        else if (label.includes("banner page")) action = "Open or refresh the Banner browser page.";
        else if (label.includes("banner")) action = "Show/refresh the banner if it should be on air.";
        else if (label.includes("data age")) action = "Reconnect timing or wait for a fresh timing update.";
        else if (label.includes("class")) action = "Confirm the class name or set manual graphic text.";
        else if (label.includes("mode")) action = "Confirm the qualifying/racing mode in Banner Controls.";
        else if (label.includes("program page")) action = "Open or refresh the Program Overlay browser page.";
        else if (label.includes("driver")) action = "Review missing driver names or car numbers.";
        return {
          title: blocked.length ? "Fix before sending" : warnings.length ? "Review before air" : "Ready for output",
          action: `Next: ${action}`,
          note: `${primary.label}: ${action}`
        };
      }

      function updateReadyGuidance(worst, blocked, warnings) {
        const primary = blocked[0] || warnings[0] || null;
        const toneClass = itemToneClass(worst);
        const nextStep = nextReadyStep(primary, blocked, warnings);
        latestRaceReadyStatus = { tone: worst, blocked, warnings, primary };

        raceReadyAction.className = `race-ready-action ${toneClass}`;
        sendReadinessNote.className = `send-readiness-note ${toneClass}`;
        takeProgramButton.classList.toggle("is-good", worst === "good");
        takeProgramButton.classList.toggle("is-warn", worst === "warn");
        takeProgramButton.classList.toggle("is-bad", worst === "bad");

        raceReadyAction.querySelector("strong").textContent = nextStep.title;
        raceReadyAction.querySelector("span").textContent = nextStep.action;
        sendReadinessNote.textContent = nextStep.note;
        updateHeroRaceReady(worst, blocked, warnings);
      }

      function renderRaceReady(state, rows, missingCount) {
        latestRaceReadyPayload = { state, rows, missingCount };
        const metaData = state.meta || {};
        const style = state.style || getStyleSettings();
        const live = metaData.live || {};
        const health = connectionHealth(metaData);
        const ageSeconds = dataAgeSeconds(metaData);
        const connected = health.tone === "good";
        const staleTone = ageSeconds == null ? "bad" : ageSeconds > 30 ? "bad" : ageSeconds > 12 ? "warn" : "good";
        const rowCount = rows.length;
        const reviewRows = rows.filter(hasReviewableDriver);
        const missingNumbers = reviewRows.filter(driver => !driverNumber(driver)).length;
        const missingNames = reviewRows.filter(driver => !hasDriverName(driver)).length;
        const longNames = reviewRows.filter(driver => driverName(driver).length > 20).length;
        const pageProgram = readyPageTone(readyPageStatus.program);
        const pageBanner = readyPageTone(readyPageStatus.topBanner);
        const baseStatus = overlayBaseReady();
        const mode = detectedMode(metaData);
        const timingTone = connected ? staleTone : health.tone === "warn" ? "warn" : "bad";
        const driverTone = reviewRows.length ? (missingCount || missingNumbers || missingNames ? "warn" : "good") : rowCount ? "warn" : "bad";
        const topBannerTone = pageBanner.tone === "bad" ? "bad" : topBannerVisible ? "live" : "warn";
        const compactItems = [
          {
            label: "vMix Address",
            value: baseStatus.value,
            detail: baseStatus.detail,
            tone: baseStatus.tone
          },
          {
            label: "Program Output",
            value: programGraphic === "blank" ? "Blank" : programGraphic,
            detail: programGraphic === "blank" ? "Nothing currently sent" : "Program output selected",
            tone: programGraphic === "blank" ? "warn" : "live"
          },
          {
            label: "Banner",
            value: pageBanner.tone === "bad" ? pageBanner.value : topBannerVisible ? "Shown" : "Hidden",
            detail: pageBanner.tone === "bad" ? pageBanner.detail : topBannerVisible ? "Banner output active" : "Banner output hidden",
            tone: topBannerTone
          },
          {
            label: "Banner Data",
            value: style.bannerDataOpen === false ? "Hidden" : "Shown",
            detail: style.bannerDataOpen === false ? "Driver/timing data hidden on top banner" : "Driver/timing data visible",
            tone: style.bannerDataOpen === false ? "warn" : "good"
          }
        ];
        const detailItems = [
          {
            label: "Data Age",
            value: ageSeconds == null ? "No updates" : formatAge(metaData.lastUpdated || live.lastPoll),
            detail: ageSeconds == null ? "No timing update yet" : ageSeconds > 30 ? "Timing may be stale" : "Timing is fresh",
            tone: staleTone
          },
          {
            label: "Class",
            value: displayText(metaData.className) || "Missing",
            detail: displayText(metaData.className) ? "Class name available" : "Graphics will fall back",
            tone: displayText(metaData.className) ? "good" : "warn"
          },
          {
            label: "Mode",
            value: mode,
            detail: mode === "Auto" ? "Race mode not obvious" : "Mode detected",
            tone: mode === "Auto" ? "warn" : "good"
          },
          {
            label: "Program Page",
            value: pageProgram.value,
            detail: pageProgram.detail,
            tone: pageProgram.tone
          },
          {
            label: "Banner Page",
            value: pageBanner.value,
            detail: pageBanner.detail,
            tone: pageBanner.tone
          },
          {
            label: "Driver Info",
            value: reviewRows.length ? (missingCount || missingNumbers || missingNames ? "Review" : "Complete") : "Waiting",
            detail: missingCount ? `${missingCount} missing data rows` : missingNumbers || missingNames ? `${missingNumbers} # / ${missingNames} names missing` : reviewRows.length ? (longNames ? `${longNames} long names detected` : "Names and numbers look usable") : "Timing placeholders only",
            tone: missingCount || missingNumbers || missingNames ? "warn" : "good"
          }
        ];
        const items = [...compactItems, ...detailItems];

        const worst = items.reduce((current, item) => toneRank(item.tone) > toneRank(current) ? item.tone : current, "good");
        const blocked = items.filter(item => item.tone === "bad");
        const warnings = items.filter(item => item.tone === "warn");
        raceReadySummary.className = `race-ready-summary ${itemToneClass(worst)}`;
        raceReadySummary.innerHTML = "";
        const dot = document.createElement("span");
        dot.className = `health-dot ${itemToneClass(worst)}`;
        const title = document.createElement("strong");
        title.textContent = blocked.length ? "Not Ready" : warnings.length ? "Needs Review" : "Race Ready";
        raceReadySummary.append(dot, title);
        updateReadyGuidance(worst, blocked, warnings);
        raceReadyGrid.innerHTML = "";
        compactItems.forEach(item => raceReadyGrid.appendChild(renderReadyItem(item)));
        raceReadyIssues.innerHTML = "";
        const issues = [...blocked, ...warnings];
        raceReadyDetailsSummary.textContent = issues.length ? `Details (${issues.length})` : "Details";
        (issues.length ? issues : detailItems.filter(item => item.detail).slice(0, 4)).forEach(item => {
          const issue = document.createElement("span");
          issue.className = itemToneClass(item.tone);
          issue.textContent = `${item.label}: ${item.detail}`;
          raceReadyIssues.appendChild(issue);
        });
      }

      function rerenderRaceReady() {
        if (!latestRaceReadyPayload) return;
        renderRaceReady(latestRaceReadyPayload.state, latestRaceReadyPayload.rows, latestRaceReadyPayload.missingCount);
      }

      async function refreshReadyNetwork() {
        try {
          const response = await fetch("/api/network", { cache: "no-store" });
          networkInfo = await response.json();
        } catch (_err) {
          networkInfo = null;
        }
        rerenderRaceReady();
      }

      async function refreshReadyPages() {
        const check = async path => {
          try {
            const separator = path.includes("?") ? "&" : "?";
            const response = await fetch(`${path}${separator}readyCheck=${Date.now()}`, { cache: "no-store" });
            return response.ok;
          } catch (_err) {
            return false;
          }
        };
        const [program, topBanner, control] = await Promise.all([
          check("/program.html"),
          check(topBannerGraphicPath()),
          check("/control.html")
        ]);
        readyPageStatus = { checkedAt: Date.now(), program, topBanner, control };
        rerenderRaceReady();
      }

      connectState(state => {
        latestTimingState = state;
        const m = state.meta;
        const rows = state.runningOrder.length ? state.runningOrder : state.competitors;
        const missingCount = rows.filter(driver => hasReviewableDriver(driver) && driver.missingData).length;
        meta.textContent = `${m.trackName || "Unknown track"} | ${m.session || "No session"} | ${m.mode || "no mode"} | ${m.clock || ""}`;
        updateControlHeader(state);
        renderLiveHealth(state, rows, missingCount);
        renderRaceReady(state, rows, missingCount);
        renderRaceIntelligence(state);
        renderRaceStoryPreview(state);
        renderControlPreviews(state);
        if (m.live) {
          if (m.live.provider === "racemonitor") {
            if (m.live.error) timingStatus.textContent = m.live.error;
            else if (m.live.connected) timingStatus.textContent = `Race Monitor: race ${m.live.raceId} on ${m.live.host}`;
            else if (m.live.connecting) timingStatus.textContent = "Race Monitor: connecting";
            else timingStatus.textContent = "Disconnected";
          }
          if (m.live.provider === "myracepass") {
            if (m.live.error) timingStatus.textContent = m.live.error;
            else if (m.live.connected) timingStatus.textContent = `MyRacePass: polling ${m.live.lastPoll ? new Date(m.live.lastPoll).toLocaleTimeString() : ""}`;
            else if (m.live.connecting) timingStatus.textContent = "MyRacePass: connecting";
            else timingStatus.textContent = "Disconnected";
          }
        }
        renderCorrections(state);
        renderRows(preview, rows, 8);
        requestAnimationFrame(syncDataPanelHeight);
      });

      document.querySelector("#sample").onclick = () => {
        messages.value = sample;
      };

      document.querySelector("#send").onclick = async () => {
        await postJson("/api/message", { message: messages.value });
      };

      document.querySelector("#reset").onclick = async () => {
        await postJson("/api/reset", {});
      };

      resetLiveStateButton.onclick = () => guardLiveControl(resetLiveStateButton, "Reset Live State", async () => {
        resetLiveStateButton.disabled = true;
        try {
          const result = await postJson("/api/reset", {});
          timingStatus.textContent = result.ok ? "Live state reset" : result.error || "Reset failed";
          if (result.ok) programStatus.textContent = "Live timing state cleared";
        } finally {
          resetLiveStateButton.disabled = false;
        }
      });

      const startNewEventButton = document.querySelector("#startNewEvent");
      if (startNewEventButton) {
        startNewEventButton.onclick = () => guardLiveControl(startNewEventButton, "Start New Event", async () => {
          startNewEventButton.disabled = true;
          try {
            const result = await postJson("/api/new-event", {});
            timingStatus.textContent = result.ok
              ? "New event started: Top 3 photos cleared, timing and held story reset"
              : result.error || "New event reset failed";
            if (result.ok) programStatus.textContent = "Ready for a new event";
          } finally {
            startNewEventButton.disabled = false;
          }
        });
      }

      document.querySelector("#connectTiming").onclick = async () => {
        const url = timingUrl.value.trim();
        if (!url) return;
        localStorage.setItem("raceGraphics.lastTimingSource", timingSource.value);
        localStorage.setItem(sourceUrlKeys[timingSource.value], url);
        timingStatus.textContent = "Connecting";
        const result = timingSource.value === "racemonitor"
          ? await postJson("/api/race-monitor/connect", { raceUrl: url })
          : await postJson("/api/myracepass/connect", { url });
        if (!result.ok) timingStatus.textContent = result.error || "Connection failed";
      };

      document.querySelector("#disconnectTiming").onclick = async () => {
        await postJson("/api/race-monitor/disconnect", {});
        await postJson("/api/myracepass/disconnect", {});
        timingStatus.textContent = "Disconnected";
      };

      async function sendRaceStoryOutput() {
        const { canSendStory, airCheck, spotlight, autoMessage } = raceStoryReadiness(latestTimingState || {});
        if (!canSendStory) {
          programStatus.textContent = `Race Story waiting: ${airCheck.guidance || "No Race Story is ready yet."}`;
          syncRaceStoryActionButtons(false, airCheck);
          return;
        }
        const previousGraphic = programGraphic;
        previewGraphic = "story";
        updateProgramStatus();
        if (shouldKeepTopBannerDuringTemporaryOutput("story", previousGraphic)) {
          await preserveTopBannerOutput();
        }
        const sent = await setProgram("story");
        if (sent) {
          recordRaceStorySent(spotlight, autoMessage);
          if (shouldAutoClearForBanner("story", previousGraphic)) {
            scheduleTemporaryOutputReturn("story", previousGraphic);
          }
        }
      }

      async function hideRaceStoryOutput() {
        if (programGraphic !== "story") {
          programStatus.textContent = "Race Story is already hidden.";
          syncRaceStoryActionButtons(Boolean(sendRaceStoryButton && !sendRaceStoryButton.disabled));
          return;
        }
        await setProgram("blank");
      }

      async function setProgram(graphic) {
        try {
          if (graphic === "story") await saveServerStyle(readRaceStoryControls({ freeze: true }));
          const readyIssue = graphic !== "blank" ? latestRaceReadyStatus.primary : null;
          const graphicName = graphicDisplayName(graphic);
          programStatus.textContent = readyIssue
            ? `Sending output: ${graphicName} | ${readyIssue.label}: ${readyIssue.detail}`
            : `Sending output: ${graphicName}`;
          const result = await postJson("/api/program", { graphic });
          if (result.ok) {
            programGraphic = graphic;
            publishProgramState(result.program || { graphic, version: Date.now() });
            updateProgramStatus();
            flashLiveOutputButton(graphic);
            saveVmixSettings();
            const overlayBase = normalizedOverlayBase();
            if (vmixUrl.value.trim() && vmixInput.value.trim()) {
              postJson("/api/vmix/browser", {
                vmixUrl: vmixUrl.value.trim(),
                input: vmixInput.value.trim(),
                overlayBase,
                graphic
              }).then((vmixResult) => {
                if (!vmixResult.ok) {
                  programStatus.textContent = `Output selected, vMix send failed: ${vmixResult.error || "Unknown error"}`;
                }
              }).catch(() => {
                programStatus.textContent = "Output selected, vMix send unavailable";
              });
            }
            return true;
          } else {
            programStatus.textContent = result.error || "Program change failed";
            return false;
          }
        } catch (_err) {
          programStatus.textContent = "Program control unavailable. Restart the server.";
          return false;
        }
      }

      function publishProgramState(program) {
        const payload = {
          graphic: program.graphic || "blank",
          version: program.version || Date.now(),
          sentAt: Date.now()
        };
        try {
          localStorage.setItem("raceGraphics.programState", JSON.stringify(payload));
        } catch (_err) {}
        if (programChannel) {
          try {
            programChannel.postMessage(payload);
          } catch (_err) {}
        }
      }

      async function setTopBannerOutput(visible) {
        try {
          const graphic = "topbanner";
          const result = await postJson("/api/top-banner-output", { visible });
          if (!result.ok) {
            programStatus.textContent = result.error || "Banner output failed";
            return false;
          }
          topBannerVisible = result.topBanner ? result.topBanner.visible !== false : visible;
          updateProgramStatus();
          saveVmixSettings();
          const overlayBase = normalizedOverlayBase();
          if (vmixUrl.value.trim() && topBannerInput.value.trim()) {
            postJson("/api/vmix/browser", {
              vmixUrl: vmixUrl.value.trim(),
              input: topBannerInput.value.trim(),
              overlayBase,
              graphic,
              direct: true
            }).then((vmixResult) => {
              if (!vmixResult.ok) {
                programStatus.textContent = `Banner ${visible ? "selected" : "cleared"}, vMix send failed: ${vmixResult.error || "Unknown error"}`;
              }
            }).catch(() => {
              programStatus.textContent = `Banner ${visible ? "selected" : "cleared"}, vMix send unavailable`;
            });
          }
          return true;
        } catch (_err) {
          programStatus.textContent = "Banner control unavailable. Restart the server.";
          return false;
        }
      }

      async function sendTopBannerOnly() {
        clearTemporaryOutputTimer();
        previewGraphic = "topbanner";
        const bannerSent = await setTopBannerOutput(true);
        const programSent = await setProgram("topbanner");
        updateProgramStatus();
        return bannerSent && programSent;
      }

      async function clearBannerOutput() {
        await setTopBannerOutput(false);
        if (programGraphic === "topbanner" || programGraphic === "sidebanner") {
          previewGraphic = "blank";
          await setProgram("blank");
        }
      }

      function isBannerGraphic(graphic) {
        return graphic === "topbanner" || graphic === "sidebanner";
      }

      function clearTemporaryOutputTimer() {
        if (temporaryOutputTimer) {
          clearTimeout(temporaryOutputTimer);
          temporaryOutputTimer = null;
        }
      }

      function shouldAutoClearForBanner(graphic, previousGraphic) {
        return TEMPORARY_BANNER_COMPANION_GRAPHICS.has(graphic) && (topBannerVisible || isBannerGraphic(previousGraphic));
      }

      function shouldKeepTopBannerDuringTemporaryOutput(graphic, previousGraphic) {
        return TEMPORARY_BANNER_COMPANION_GRAPHICS.has(graphic) && (topBannerVisible || previousGraphic === "topbanner");
      }

      async function preserveTopBannerOutput() {
        if (topBannerVisible) return true;
        return setTopBannerOutput(true);
      }

      function scheduleTemporaryOutputReturn(graphic, previousGraphic) {
        clearTemporaryOutputTimer();
        const returnGraphic = topBannerVisible ? "topbanner" : (isBannerGraphic(previousGraphic) ? previousGraphic : "blank");
        temporaryOutputTimer = setTimeout(async () => {
          temporaryOutputTimer = null;
          if (programGraphic !== graphic) return;
          const returned = await setProgram(returnGraphic);
          if (returned) {
            programStatus.textContent = returnGraphic === "blank"
              ? `${graphicDisplayName(graphic)} cleared after 5 seconds. Banner remains shown.`
              : `${graphicDisplayName(graphic)} cleared after 5 seconds. Returned to ${graphicDisplayName(returnGraphic)}.`;
          }
        }, 5000);
      }

      function updateProgramStatus() {
        programStatus.textContent = `Preview: ${graphicDisplayName(previewGraphic)} | Live Output: ${graphicDisplayName(programGraphic)} | ${topBannerGraphicLabel()}: ${topBannerVisible ? "shown" : "hidden"}`;
        document.querySelectorAll("[data-preview]").forEach(button => {
          button.classList.toggle("active", button.dataset.preview === previewGraphic);
          button.classList.toggle("is-live-output", button.dataset.preview === programGraphic && programGraphic !== "blank");
          button.classList.toggle("on-program", button.dataset.preview === programGraphic);
        });
        sendTopBannerButton.textContent = "Show/Refresh Banner";
        sendTopBannerButton.classList.toggle("is-live", topBannerVisible);
        clearTopBannerButton.classList.toggle("active", !topBannerVisible);
        if (latestTimingState) {
          const storyStatus = raceStoryReadiness(latestTimingState);
          syncRaceStoryActionButtons(storyStatus.canSendStory, storyStatus.airCheck);
          updateRaceStoryStatusStrip(latestTimingState, storyStatus.canSendStory, storyStatus.airCheck);
        }
        renderControlPreviews(latestTimingState);
        rerenderRaceReady();
      }

      function selectPreviewGraphic(graphic) {
        previewGraphic = graphic || "blank";
        window.lrwPreviewGraphic = previewGraphic;
        updateProgramStatus();
      }

      async function sendPreviewGraphic(graphic) {
        const nextGraphic = graphic || "blank";
        const previousGraphic = programGraphic;
        selectPreviewGraphic(nextGraphic);
        clearTemporaryOutputTimer();
        if (shouldKeepTopBannerDuringTemporaryOutput(nextGraphic, previousGraphic)) {
          await preserveTopBannerOutput();
        }
        const sent = await setProgram(nextGraphic);
        if (sent && shouldAutoClearForBanner(nextGraphic, previousGraphic)) {
          scheduleTemporaryOutputReturn(nextGraphic, previousGraphic);
        }
        return sent;
      }

      window.lrwSelectOutput = selectPreviewGraphic;
      window.lrwSendOutput = sendPreviewGraphic;

      document.querySelectorAll("[data-preview]").forEach(button => {
        button.addEventListener("click", event => {
          event.preventDefault();
          selectPreviewGraphic(button.dataset.preview);
        });
        button.addEventListener("dblclick", event => {
          event.preventDefault();
          event.stopPropagation();
          sendPreviewGraphic(button.dataset.preview);
        });
      });

      document.addEventListener("dblclick", event => {
        const button = event.target.closest("[data-preview]");
        if (!button) return;
        event.preventDefault();
        event.stopPropagation();
        sendPreviewGraphic(button.dataset.preview);
      }, true);

      document.querySelector("#takeProgram").onclick = () => sendPreviewGraphic(previewGraphic);
      clearProgramButton.onclick = () => sendPreviewGraphic("blank");
      if (sendRaceStoryButton) {
        sendRaceStoryButton.onclick = () => guardLiveControl(sendRaceStoryButton, "Send Race Story", sendRaceStoryOutput);
      }
      if (hideRaceStoryButton) {
        hideRaceStoryButton.onclick = () => guardLiveControl(hideRaceStoryButton, "Hide Race Story", hideRaceStoryOutput);
      }
      if (raceStoryAutoBestButton) {
        raceStoryAutoBestButton.onclick = selectAutoRaceStory;
      }
      sendTopBannerButton.onclick = () => sendTopBannerOnly();
      clearTopBannerButton.onclick = () => guardLiveControl(clearTopBannerButton, "Clear Banner", clearBannerOutput);

      fetch("/api/program")
        .then(res => res.json())
        .then(state => {
          const graphic = state.graphic === "running" ? "lineup" : state.graphic;
          programGraphic = graphic;
          previewGraphic = graphic;
          updateProgramStatus();
        })
        .catch(() => {
          programStatus.textContent = "Program control unavailable. Restart the server.";
        });

      fetch("/api/top-banner-output")
        .then(res => res.json())
        .then(state => {
          topBannerVisible = state.visible !== false;
          updateProgramStatus();
        })
        .catch(() => {});

      function flashLiveOutputButton(graphic) {
        const button = document.querySelector(`[data-preview="${graphic}"]`);
        if (!button) return;
        button.classList.remove("send-flash");
        void button.offsetWidth;
        button.classList.add("send-flash");
        setTimeout(() => button.classList.remove("send-flash"), 1000);
      }

      const SESSION_PRESETS = {
        // Auto is the one-engineer mode: the banner and Top 3 follow whatever the
        // timing feed says the session is (qualifying vs racing) and switch on
        // their own. Qualifying/Race lock the mode for the session.
        auto: { bannerRaceMode: "auto", bannerDurationMode: "auto", top3Source: "timing" },
        qualifying: { bannerRaceMode: "qualifying", bannerDurationMode: "auto", top3Source: "qualifying", bannerMetricFields: ["bestLap", "gap"] },
        race: { bannerRaceMode: "racing", bannerDurationMode: "lapsToGo", top3Source: "race", bannerMetricFields: ["lastLap", "gap"] }
      };

      document.querySelectorAll("[data-session-preset]").forEach(button => {
        button.addEventListener("click", async () => {
          const preset = SESSION_PRESETS[button.dataset.sessionPreset];
          if (!preset) return;
          const result = await saveServerStyle(preset);
          programStatus.textContent = result.ok
            ? `Session preset applied: ${button.textContent}`
            : (result.error || "Session preset failed.");
        });
      });

      const heroServerCard = document.querySelector("#heroServerCard");
      const heroServerState = document.querySelector("#heroServerState");
      const heroServerDetail = document.querySelector("#heroServerDetail");
      const staleTimingStrip = document.querySelector("#staleTimingStrip");

      function setServerCard(tone, state, detail) {
        if (!heroServerCard) return;
        heroServerCard.classList.toggle("is-good", tone === "good");
        heroServerCard.classList.toggle("is-warn", tone === "warn");
        heroServerCard.classList.toggle("is-bad", tone === "bad");
        heroServerState.textContent = state;
        heroServerDetail.textContent = detail;
      }

      async function refreshServerHealthCard() {
        try {
          const response = await fetch(`/api/health?t=${Date.now()}`, { cache: "no-store" });
          const health = await response.json();
          const timing = health.timing || {};
          const updatedMs = Date.parse(timing.lastUpdated || "");
          const stale = timing.connected && Number.isFinite(updatedMs) && Date.now() - updatedMs > 20000;
          if (staleTimingStrip) staleTimingStrip.hidden = !stale;
          // Keep the LIVE indicator honest even when the send came from elsewhere
          // (iPad control, another control tab, or the API).
          const rawServerGraphic = health.output ? health.output.program : "";
          const serverGraphic = rawServerGraphic === "running" ? "lineup" : rawServerGraphic;
          if (serverGraphic && serverGraphic !== programGraphic) {
            programGraphic = serverGraphic;
            updateProgramStatus();
          }
          // The browser allows ~6 connections per origin across ALL tabs and
          // every live page holds one stream; when the count crowds the cap,
          // sends and page loads start queueing. Only the operator can tell a
          // surplus tab from a vMix source, so warn instead of auto-closing.
          const liveStreams = Number((health.connections || {}).browserClients) || 0;
          if (liveStreams >= 5) {
            setServerCard("warn", "Connected", `${liveStreams} live connections - close unused graphics tabs (browser cap ~6)`);
          } else if (timing.connected) {
            setServerCard(stale ? "warn" : "good", "Connected", stale
              ? "Timing stalled - holding last data"
              : `Timing live - ${timing.drivers || 0} drivers`);
          } else {
            setServerCard("warn", "Connected", "No timing source connected");
          }
        } catch (_err) {
          setServerCard("bad", "UNREACHABLE", `No reply from ${location.host} - check the server window`);
          if (staleTimingStrip) staleTimingStrip.hidden = true;
        }
      }

      refreshServerHealthCard();
      setInterval(refreshServerHealthCard, 5000);
