const $ = (id) => document.getElementById(id);
const NUMBERPLATE_GENERATOR_NOTICE_ID = "numberplate-generator-png-only-v1";
const CUSTOM_GRID_BRIDGE_GUIDE_NOTICE_ID = "custom-grid-bridge-guide-v1";
const LAYER_STUDIO_INFO_NOTICE_ID = "layer-studio-beta-info-v1";
const LIVERY_PREVIEW_INFO_NOTICE_ID = "livery-preview-workflow-info-v2";
const DRIVER_PROFILES_INFO_NOTICE_ID = "driver-profiles-ferrari-f1-info-v1";
// Disabled legacy Numberplate paths. The normal release workflow is PNG export
// plus AC EVO default/no-numberplate actor visibility.
const NUMBERPLATE_OVERLAY_WORKFLOW_ENABLED = false;
const NUMBERPLATE_SKIN_TEMPLATE_WORKFLOW_ENABLED = false;
const NUMBERPLATE_STUDIO_GAME_EXPORT_ENABLED = false;
const NUMBERPLATE_WORKFLOW_DISABLED_MESSAGE = "Numberplate overlay preview is disabled. Export PNGs from Numberplate Studio and place them directly on the livery texture.";
const DRIVER_PROFILE_SUPPORTED_CARS = new Set(["ks_ferrari_sf_25", "ks_ferrari_f2004"]);
const DRIVER_PROFILE_UNSUPPORTED_MESSAGE = "Driver profiles are currently supported for Ferrari SF25 and Ferrari F2004 only.";
const SKIN_TEMPLATE_UNSUPPORTED_MESSAGE = "Unsupported template: this AC EVO cardesign is not connected to a complete visual/mechanical preset chain.";
const LIVERY_DISPLAY_NAME_MAX_LENGTH = 48;
const LIVERY_DESCRIPTION_MAX_LENGTH = 96;
const LIVERY_TECHNICAL_ID_MAX_LENGTH = 28;
const LIVERY_CAR_SLUG_MAX_LENGTH = 18;
const OFFICIAL_EXTERNAL_FOLDER_MAX_LENGTH = 48;
const APP_BACKGROUND_BY_TAB = {
  home: "1",
  library: "2",
  skin: "3",
  "my-skin-packages": "4",
  "online-league": "5",
  drivers: "5",
  "custom-grids": "1",
  "tyre-brands": "2",
  "numberplate-creator": "3",
  convert: "4",
  mesh: "5",
  preview3d: "1",
  "rim-changer": "2",
  uv: "3",
  material: "4",
  inspect: "5",
  "settings-home": "1",
  package: "1",
  about: "2",
};
const SETUP_GATE_ALLOWED_TABS = new Set(["home", "about"]);
const SHARED_WORKFLOW_TABS = new Set(["home", "library", "skin", "my-skin-packages", "numberplate-creator", "settings-home", "about"]);
const ADVANCED_WORKFLOW_TABS = new Set(["drivers", "custom-grids", "tyre-brands", "convert", "mesh", "preview3d", "uv", "material", "inspect"]);
const OFFICIAL_WORKFLOW_TABS = new Set(["online-league"]);

const state = {
  root: "",
  savedRoot: "",
  rootStatus: null,
  rootStatusTimer: 0,
  rootStatusRequestId: 0,
  contentPackageStatus: null,
  contentPackageStatusRequestId: 0,
  multiplayerSafetyStatus: null,
  multiplayerSafetyStatusRequestId: 0,
  multiplayerSafetyRepairing: false,
  multiplayerSafetyRepairPromise: null,
  liveryLabSafetyBypassed: false,
  currentRel: "",
  selected: null,
  language: "en",
  inspectText: "",
  inspectSearch: "",
  inspectMatchIndex: 0,
  uvMeshData: null,
  uvImageData: null,
  uvTexturePath: "",
  uvGroups: [],
  uvSelectedGroups: new Set(),
  uvMajorOnly: false,
  uvGroupMinTriangles: 100,
  uvBrowserVisible: false,
  uvStored: null,
  uvPersistTimer: 0,
  uvHoveredGroupId: null,
  uvFocusedGroupId: null,
  uvMaterialFilter: "all",
  uvBlink: null,
  uvBlinkRaf: 0,
  uvPickingCanvas: null,
  uvControlTab: "setup",
  browserFavorites: [],
  startupPreloadStarted: false,
  startupPreloadDone: false,
  splashHideTimer: 0,
  numberplateGeneratorNoticeDismissedNotice: "",
  customGridBridgeGuideDismissedNotice: "",
  layerStudioInfoDismissedNotice: "",
  liveryPreviewInfoDismissedNotice: "",
  driverProfilesInfoDismissedNotice: "",
  statusModalOpen: false,
  statusModalOpenFolderPath: "",
  statusModalBlocked: false,
  mySkins: [],
  mySkinsLoaded: false,
  mySkinsLoadedRoot: "",
  mySkinsLoadedScope: "",
  workflowChoiceComplete: false,
  workflowChoiceBusy: false,
  workflowStartupBusy: false,
  workflowScope: "advanced",
  workflowScopeConfigured: false,
  runtimeSwitchPreflight: null,
  runtimeSwitchBusy: false,
  officialRuntimePreparedRoot: "",
  officialRuntimeSafetyStatus: null,
  mySkinsMarkerLoading: false,
  mySkinsLoading: false,
  mySkinSelectedKey: "",
  mySkinDetailTab: "overview",
  mySkinsActionBusy: false,
  mySkinsActionBusyLabel: "",
  mySkinsDetailBusy: false,
  mySkinsDetailBusyButtonId: "",
  mySkinsDetailBusyLabel: "",
  liveryCategories: [],
  liveryCategoryAssignments: {},
  liveryFavoriteSkinKeys: [],
  liveryCategorySortMode: "created",
  liveryCategorySearch: "",
  liveryDefaultCategoryCollapsed: false,
  liveryFavoritesCollapsed: false,
  officialReferencesCollapsed: false,
  liveryCategoryAddOpen: false,
  liveryCategoryDraftName: "",
  liveryCategoryEditingId: "",
  liveryCategoryEditingName: "",
  mySkinExportDir: "",
  officialMySkinExportDir: "",
  mySkinPackageDir: "",
  mySkinPackageInfo: null,
  packageProgress: null,
  packageExtractModalOpen: false,
  packageExtractCancelRequested: false,
  texconvPath: "",
  mySkinThumbCache: {},
  mySkinThumbPending: {},
  mySkinThumbnailWatchTimer: 0,
  mySkinThumbnailWatchSkinKey: "",
  mySkinThumbnailWatchChangeId: 0,
  mySkinRoadPlateWatchTimer: 0,
  mySkinRoadPlateWatchSkinKey: "",
  mySkinRoadPlateWatchPath: "",
  mySkinRoadPlateWatchSignature: "",
  selectedTyreBrandId: "",
  tyreBrandDraft: null,
  tyreBrandPreviewCache: {},
  tyreBrandPreviewRenderId: 0,
  driverProfiles: [],
  driverProfilesLoadedCars: new Set(),
  driverProfilesLoadingCars: new Set(),
  selectedDriverProfileKey: "",
  selectedDriverSourceSkinKey: "",
  driverProfilePackageDir: "",
  driverProfileExportDir: "",
  driverPngPreviewTimer: 0,
  driverPngPreviewRequestId: 0,
  driverPreviewProject: null,
  driverPreviewProfileKey: "",
  driverPreviewSelectedSlot: "helmet",
  driverPreviewWatchTimer: 0,
  driverPreviewLastChangeId: 0,
  carCompatibility: null,
  carCompatibilityCacheStatus: null,
  carCompatibilityLoading: false,
  carCompatibilityProgress: null,
  carAnalysisBypassed: false,
  advancedCreatorSelectedCarId: "",
  advancedCreatorSelectedTemplateId: "",
  advancedCreatorSortMode: "alphabetical",
  advancedCreatorSearch: "",
  advancedCreatorFavoriteCarIds: [],
  liveryTemplatePreviewStatuses: {},
  liveryTemplatePreviewSelectedKeys: {
    advanced: "",
    official: "",
  },
  liveryTemplatePreviewRequestIds: {},
  liveryTemplateCatalogs: {},
  liveryTemplateCatalogSelectedKeys: {
    advanced: "",
    official: "",
  },
  liveryTemplateSelectedIds: {
    advanced: "blank",
    official: "blank",
  },
  liveryTemplateCatalogRequestIds: {},
  storageHealth: null,
  storageHealthLoading: false,
  storageHealthTimer: 0,
  storageHealthRequestId: 0,
  longPathsNoticeShown: false,
  runtimeProfile: null,
  runtimeProfileLoading: false,
  runtimeProfileRequestId: 0,
  officialExternalLiveries: null,
  officialExternalLiveriesLoading: false,
  officialExternalLiveriesRequestId: 0,
  officialSavedCarTemplateStatus: {},
  officialSavedCarTemplateRequestId: 0,
  officialCarResourcesStatus: null,
  officialCarResourcesLoading: false,
  officialCarResourcesRequestId: 0,
  officialCreatorSelectedCarId: "",
  officialCreatorSelectedVariantId: "",
  officialCreatorSortMode: "alphabetical",
  officialCreatorSearch: "",
  officialCreatorFavoriteCarIds: [],
  officialStorageRoots: null,
  setupGate: null,
  appStorageRoots: [],
  preview3dTimer: 0,
  preview3dLastMtimeMs: null,
  preview3dBannerTexturePath: "",
  preview3dExtWindowsTexturePath: "",
  preview3dDetailTexturePath: "",
  preview3dAoTexturePath: "",
  liveryPreviewSkinTextureMode: "color",
  liveryPreviewTextureKey: "ext_skin_c",
  liveryPreviewPreviewMode: "materialView",
  liveryPreviewNumberplateSupport: null,
  liveryPreviewAoEnabled: true,
  liveryPreviewBodyBaseColor: "#ffffff",
  liveryPreviewBodyBaseColorSource: "default",
  liveryPreviewMaterialFinish: {
    metalness: 0,
    roughness: 0.3,
    clearCoat: 1,
    clearCoatRoughness: 0.3,
    scope: "base",
  },
  liveryPreviewScene: "dark",
  liveryPreviewSceneSettings: null,
  liveryPreviewSceneDefaults: null,
  liveryPreviewSceneSettingsPath: "",
  liveryPreviewSceneSettingsDirty: false,
  liveryPreviewSkinKey: "",
  liveryPreviewWatchTimer: 0,
  liveryPreviewLastChangeId: 0,
  liveryPreviewCameraPreset: "free",
  liveryPreviewOpening: false,
  liveryPreviewReadOnly: false,
  liveryPreviewProjectFolder: "",
  liveryPreviewProject: null,
  liveryPreviewMaterialInspection: null,
  liveryPreviewWheelPreview: null,
  liveryPreviewWheelTunerEnabled: false,
  liveryPreviewWheelUnsavedChanges: false,
  liveryPreviewMaterialFilter: {
    query: "",
    category: "all",
    mappedOnly: false,
  },
  liveryPreviewMaterialOverlayActive: false,
  liveryPreviewApproxMaterialsActive: false,
  liveryPreviewRimsVisible: true,
  liveryPreviewWheelPlacementOverrides: true,
  liveryPreviewStatusCollapsed: true,
  confirmModalResolver: null,
  confirmModalMode: "confirm",
  confirmModalBusy: false,
  confirmModalOnConfirm: null,
  confirmModalConfirmLabel: "Confirm",
  confirmModalLoadingLabel: "Working...",
  pendingFileDrop: null,
  liveryPreviewNumberplate: null,
  liveryPreviewNumberplateTunerValues: {},
  liveryPreviewNumberplateGizmoEnabled: false,
  liveryPreviewNumberplateUnsavedChanges: false,
  liveryPreviewNumberplateSavedNeedsApply: false,
  liveryPreviewNumberplateSelectedIndex: 0,
  liveryPreviewNumberplateTunerReadyKey: "",
  liveryPreviewNumberplateUndo: {},
  liveryPreviewNumberplateInputUndoArmed: false,
  liveryPreviewNumberplateActiveInputId: "",
  liveryPreviewNumberplateScaleRatio: 0.28,
  liveryPreviewNumberplatesVisible: false,
  liveryPreviewNumberplateFit: null,
  liveryPreviewNumberplateFitTimer: 0,
  liveryPreviewNumberplateDebugLabels: true,
  liveryPreviewNumberplateActorProjectionOffset: { x: 0, y: 0, z: 0 },
  liveryPreviewActorGhostOffsets: {},
  numberplateBuiltinCalibrations: null,
  numberplateBuiltinCalibrationsPromise: null,
  numberplateStudioTemplates: [],
  numberplateStudioTemplatesPromise: null,
  numberplateTextLayerDefaults: {},
  liveryPreviewActorNudgeConfirmed: false,
  numberplateTemplates: {
    fonts: [],
    backgrounds: [],
  },
  customNumberplates: {
    fonts: [],
    backgrounds: [],
  },
  customTyreBrands: [],
  debugMode: false,
  debugTools: {},
  skinTemplateAutofillRequestId: 0,
  skinPaintVariantOptions: [],
  deletedLiveries: [],
  deletedLiveriesLoading: false,
  selectedDeletedLiveryId: "",
  officialDeletedLiveries: [],
  officialDeletedLiveriesLoading: false,
  selectedOfficialDeletedLiveryId: "",
};

const MODAL_LABELS = {
  convert_to_dds: "DDS Export",
  convert_to_png: "PNG Export",
  convert_to_texturemips: "Texturemips Import",
  inspect_pack_file: "Package Extractor",
  write_pack_file_listing: "Package Extractor",
  extract_pack_file: "Package Extractor",
  extract_selected_pack_entries: "Package Extractor",
  disable_content_package: "Setup",
  export_mesh_obj: "Mesh Export",
  inspect_rimmesh: "Rim Inspect",
  create_rimmesh_swap: "Rim Changer",
  patch_compatiblerim_for_test: "Rim Changer",
  restore_compatiblerim_backup: "Rim Changer",
  create_additional_rim_option: "Rim Changer",
  remove_custom_rim_option: "Rim Changer",
  save_uv_template: "UV Export",
  write_material_reports: "Material Reports",
  export_material_textures: "Material Export",
  save_default_root: "Save root",
  save_texconv_path: "Texconv",
  create_skin: "Livery Creator",
  add_mechanical_visual: "Mechanical Preset",
  remove_mechanical_visual: "Mechanical Preset",
  update_actor_resource: "Actor Editor",
  update_visualpreset: "Visual Preset",
  update_carfinalstate: "Car Final State",
  update_actor_numberplates: "Actor Numberplates",
  save_uv_mesh_preset: "UV Preset",
  delete_uv_mesh_preset: "UV Preset",
  save_preview3d_project: "3D Preview",
  save_livery_material_finish: "Material Finish",
  disable_livery_evo_paint_override: "Material Finish",
  inspect_preview3d_project: "3D Preview",
  add_browser_favorite: "Save favorite",
  delete_browser_favorite: "Delete favorite",
  import_existing_my_skins: "My Skins Import",
  export_my_skin_package: "My Skins Export",
  inspect_skin_package: "Livery Package",
  inspect_advanced_package_official_migration: "Official Migration",
  import_advanced_package_as_official: "Official Migration",
  import_livery_package_smart: "Livery Package",
  install_skin_package: "Livery Package",
  update_game_textures_from_livery_project: "3D Livery Preview",
  set_my_skin_hidden: "My Skins",
  remove_my_skin: "My Skins",
  trash_my_skin: "My Skins",
  list_trashed_my_skins: "Deleted Liveries",
  restore_trashed_my_skin: "Deleted Liveries",
  delete_trashed_my_skin: "Deleted Liveries",
  list_trashed_official_my_skins: "Deleted Official Liveries",
  restore_trashed_official_my_skin: "Deleted Official Liveries",
  delete_trashed_official_my_skin: "Deleted Official Liveries",
  validate_official_external_livery: "Official Validation",
  register_official_external_livery: "My Skins",
  set_my_skin_active: "My Skins",
  activate_official_external_project: "My Skins",
  deactivate_official_external_project: "My Skins",
  remove_my_skin_from_game: "My Skins",
  update_my_skin: "My Skins",
  update_my_skin_tyre_brand: "My Skins",
  update_my_skin_driver_profile: "My Skins",
  create_numberplate_background: "Numberplates",
  create_custom_numberplate_asset: "Numberplates",
  create_racing_tyre_brand: "Tyre Brands",
  save_custom_tyre_brand_template: "Tyre Brands",
  duplicate_custom_tyre_brand_template: "Tyre Brands",
  inspect_custom_tyre_brand_usage: "Tyre Brands",
  delete_custom_tyre_brand_template: "Tyre Brands",
  list_custom_tyre_brands: "Tyre Brands",
  export_custom_tyre_brand_package: "Tyre Brands",
  inspect_custom_tyre_brand_package: "Tyre Brands",
  import_custom_tyre_brand_package: "Tyre Brands",
  list_driver_profiles: "Drivers",
  list_custom_grids: "Custom Grids",
  create_custom_grid: "Custom Grids",
  save_custom_grid: "Custom Grids",
  save_custom_grid_preview_image: "Custom Grids",
  duplicate_custom_grid: "Custom Grids",
  delete_custom_grid: "Custom Grids",
  get_custom_grid_bridge_status: "Custom Grids",
  get_multiplayer_safety_status: "Setup",
  repair_multiplayer_safety_hook: "Setup",
  refresh_multiplayer_safety_manifest: "Setup",
  remove_liverylab_safety_hook: "Setup",
  install_custom_grid_bridge: "Custom Grids",
  remove_custom_grid_bridge: "Custom Grids",
  export_custom_grid_bridge: "Custom Grids",
  get_ai_vehicle_spec_support: "Custom Grids",
  get_ai_name_marker_cleanup_status: "Custom Grids",
  install_ai_name_marker_cleanup: "Custom Grids",
  remove_ai_name_marker_cleanup: "Custom Grids",
  remove_custom_grid_ui_hooks: "Custom Grids",
  get_ace_grid_cache_status: "Custom Grids",
  reset_ace_grid_cache: "Custom Grids",
  restore_ace_grid_cache_backup: "Custom Grids",
  create_driver_profile_from_skin: "Drivers",
  export_driver_profile_package: "Drivers",
  inspect_driver_profile_package: "Drivers",
  import_driver_profile_package: "Drivers",
  delete_driver_profile: "Drivers",
  update_driver_profile_details: "Drivers",
  update_driver_profile_appearance: "Drivers",
  mark_driver_profile_project_changed: "Drivers",
  save_driver_profile_preview_image: "Drivers",
  prepare_driver_preview_project: "Driver 3D Preview",
  start_driver_preview_watcher: "Driver 3D Preview",
  stop_driver_preview_watcher: "Driver 3D Preview",
  get_driver_preview_watcher_status: "Driver 3D Preview",
  replace_custom_numberplate_asset_png: "Numberplates",
  activate_custom_numberplate_asset: "Numberplates",
  deactivate_custom_numberplate_asset: "Numberplates",
  inspect_custom_numberplate_usage: "Numberplates",
  create_update_backup: "Update Backup",
  inspect_update_backup: "Update Backup",
  import_update_backup: "Update Backup",
  restore_ace_default_state: "Restore AC EVO to default",
  prepare_game_for_package_refresh: "Package Refresh",
};

const IMPORTANT_CALLS = new Set(Object.keys(MODAL_LABELS));
const DEFERRED_MY_SKINS_SUCCESS_MODAL_CALLS = new Set([
  "set_my_skin_active",
  "activate_official_external_project",
  "deactivate_official_external_project",
  "remove_my_skin_from_game",
  "remove_my_skin",
  "trash_my_skin",
  "update_my_skin",
  "update_my_skin_tyre_brand",
  "update_my_skin_driver_profile",
  "update_my_skin_numberplate",
]);
const PACKAGE_IMPORT_ERROR_CALLS = new Set([
  "inspect_skin_package",
  "install_skin_package",
  "inspect_advanced_package_official_migration",
  "import_advanced_package_as_official",
  "import_livery_package_smart",
  "inspect_custom_tyre_brand_package",
  "import_custom_tyre_brand_package",
  "inspect_driver_profile_package",
  "import_driver_profile_package",
  "inspect_numberplate_template_package",
  "import_numberplate_template_package",
  "inspect_update_backup",
  "import_update_backup",
]);
const OPEN_FOLDER_RESULT_CALLS = new Set([
  "convert_to_dds",
  "convert_to_png",
  "convert_to_texturemips",
  "export_mesh_obj",
  "save_uv_template",
  "write_material_reports",
  "export_material_textures",
  "create_update_backup",
  "import_update_backup",
  "restore_ace_default_state",
  "prepare_game_for_package_refresh",
  "trash_my_skin",
]);

function normalizeRootPath(value) {
  const raw = String(value || "").trim().replace(/[\\/]+$/, "");
  if (!raw) return "";
  const parts = raw.split(/[\\/]/).filter(Boolean);
  if (parts.length && parts[parts.length - 1].toLowerCase() === "content") {
    return parts.slice(0, -1).join("\\");
  }
  return raw;
}

function pathParts(value) {
  return String(value || "").trim().replace(/[\\/]+$/, "").split(/[\\/]/).filter(Boolean);
}

function relativePathFromRoot(root, target) {
  const rootParts = pathParts(root);
  const targetParts = pathParts(target);
  if (!rootParts.length || targetParts.length < rootParts.length) return null;
  for (let index = 0; index < rootParts.length; index += 1) {
    if (rootParts[index].toLowerCase() !== targetParts[index].toLowerCase()) return null;
  }
  return targetParts.slice(rootParts.length).join("\\");
}

function joinPath(folder, fileName) {
  const base = String(folder || "").trim().replace(/[\\/]+$/, "");
  const file = String(fileName || "").trim().replace(/^[\\/]+/, "");
  return base && file ? `${base}\\${file}` : base || file;
}

function normalizedPathKey(value) {
  return String(value || "").trim().replace(/[\\/]+$/, "").replace(/\//g, "\\").toLowerCase();
}

function pathsMatch(left, right) {
  const leftKey = normalizedPathKey(left);
  const rightKey = normalizedPathKey(right);
  return Boolean(leftKey && rightKey && leftKey === rightKey);
}

function expectedContentPackagePath(root) {
  const normalized = normalizeRootPath(root);
  return normalized ? joinPath(normalized, "content.kspkg") : "";
}

function syncHomePackageDefaultsForRoot(root, previousRoot = "") {
  const packageInput = $("homePackagePath");
  const outputInput = $("homePackageOutDir");
  const normalized = normalizeRootPath(root);
  const previousPackagePath = expectedContentPackagePath(previousRoot);
  const packagePath = expectedContentPackagePath(normalized);
  if (packageInput && packagePath) {
    const currentPackagePath = packageInput.value.trim();
    if (!currentPackagePath || pathsMatch(currentPackagePath, previousPackagePath)) {
      packageInput.value = packagePath;
    }
  }
  if (outputInput && normalized && !outputInput.value.trim()) {
    outputInput.value = normalized;
  }
}

function syncHomePackagePathFromStatus(packageState = null) {
  const packageInput = $("homePackagePath");
  const sourcePath = String(packageState?.contentPackagePath || packageState?.activePath || "").trim();
  if (!packageInput || !sourcePath) return;
  const expectedPath = expectedContentPackagePath(packageState.root || state.root);
  const currentPackagePath = packageInput.value.trim();
  if (!currentPackagePath || pathsMatch(currentPackagePath, expectedPath)) {
    packageInput.value = sourcePath;
  }
}

const I18N = {
  de: {
    home: "Setup",
    editing: "Editing",
    liveries: "Liveries",
    settings: "Settings",
    analyze: "Analysieren",
    load_texture: "Textur laden",
    export_package: "Paket exportieren",
    inspect_open: "Inspect oeffnen",
    to_editing: "Zu Editing",
    to_liveries: "Zu Liveries",
    to_inspect: "Zu Inspect",
    to_settings: "Zu Settings",
    open_texture_converter: "TextureConverter oeffnen",
    open_mesh_converter: "MeshConverter oeffnen",
    open_preview3d: "3D Preview oeffnen",
    open_rim_changer: "Rim Changer oeffnen",
    open_uv_creator: "UV Inspect oeffnen",
    open_material_viewer: "MaterialViewer oeffnen",
    open_my_liveries: "MyLiveries oeffnen",
    open_skin_creator: "LiveryCreator oeffnen",
    copy_source: "Quellordner kopieren",
    create_skin: "Skin erstellen",
    create_visual: "Visual erstellen",
    dds_output: "Ziel DDS",
    png_output: "Ziel PNG",
    default_output_textures: "Standardziel: LiveryLibrary/Converted/Textures/<car_id>",
    default_output_meshes: "Standardziel: LiveryLibrary/Converted/Meshes/<car_id>",
    default_output_uv: "Standardziel: LiveryLibrary/Converted/UV/<car_id>",
    display_name: "Name",
    export_title: ".texturemips nach DDS + PNG",
    file: "Datei",
    files: "Dateien",
    folder: "Ordner",
    favorites: "Favoriten",
    open: "Oeffnen",
    remove: "Entfernen",
    file_list: "Dateiliste",
    remember_current_folder: "Aktuellen Ordner merken",
    package_folder: "Paketordner",
    check_manifest: "Manifest pruefen",
    install_package: "In My Liveries importieren",
    update: "Aktualisieren",
    show_hidden: "Versteckte anzeigen",
    import_existing: "Bestehende importieren",
    document_root: "Document Root",
    general: "General",
    package_extractor: "Package Extractor",
    selection_paths: "Pfade / Ordnerauswahl",
    write_file_list: "Dateiliste schreiben",
    extract_all: "Alles entpacken",
    extract_selection: "Nur Auswahl entpacken",
    texture_image: "Texture/Image",
    summary: "Summary",
    presets_groups: "Presets & Gruppen",
    setup: "Setup",
    language_select: "Language Select",
    my_liveries: "My Liveries",
    custom_grids: "Custom Grids",
    my_skin_packages: "Import / Export",
    online_league: "Online League",
    skin_creator: "Livery Creator",
    numberplate_creator: "Numberplates",
    tyre_brands: "Tyre Brands",
    drivers: "Drivers",
    texture_converter: "Texture Converter",
    mesh_converter: "Mesh Converter",
    preview3d: "3D Inspect",
    rim_changer: "Rim Changer",
    uv_creator: "UV Inspect",
    material_viewer: "Material Viewer",
    file_inspect: "Datei-Inspect",
    file_info: "DATEI INFO",
    inspect_intro: "Zum Lesen, Debuggen und gezielten Bearbeiten von ACEvo-Dateiformaten direkt in der App.",
    home_intro: "Complete these setup steps before creating or activating liveries.",
    editing_intro: "Werkzeuge zum Umwandeln, Analysieren und Vorbereiten von Assets fuer deine Bearbeitungspipeline.",
    liveries_intro: "Alles rund um deine eigenen Skins: erstellen, verwalten, exportieren, installieren und nachbearbeiten.",
    settings_intro: "Wartung, Speicherstatus und lokale App-Grundlagen fuer deinen LiveryLab-Arbeitsbereich.",
    about: "About",
    about_intro: "LiveryLab ist ein fokussierter Workspace zum Erstellen, Pruefen und Verfeinern eigener Liveries fuer Assetto Corsa EVO.",
    about_features: "Features",
    about_feature_liveries_title: "Custom Liveries",
    about_feature_liveries_text: "Erstelle, installiere, verwalte, aktiviere und exportiere lokale My Liveries, ohne ins Spiel zu schreiben, bis du sie bewusst synchronisierst.",
    about_feature_layer_studio_title: "Layer Studio",
    about_feature_layer_studio_text: "Gestalte Livery-Texturen mit PNG-Assets, Formen, Text, Guides, Alignment-Tools und Live-Updates in der 2D- und 3D-Vorschau.",
    about_feature_drivers_title: "Custom Drivers",
    about_feature_drivers_text: "Erstelle und teile Driver-Profile fuer unterstuetzte Autos, inklusive lokaler Assets, Manifeste und wiederverwendbarer Import-/Export-Workflows.",
    about_feature_tyres_title: "Custom Tyre Brands",
    about_feature_tyres_text: "Erstelle lokale Racing-Tyre-Brand-Sets mit Slick-, Blur- und Sidewall-Assets und teile sie als ZIP-Pakete.",
    about_feature_grids_title: "Custom Grids",
    about_feature_grids_text: "Baue Grid-Presets aus deinen lokalen Liveries, inklusive Driver Names, damit Race-Setups schnell neu erstellt und geteilt werden koennen.",
    about_feature_textures_title: "Layer Studio",
    about_feature_textures_text: "Gestalte Livery-Texturen mit PNG-Assets, Formen, Text, Guides, Alignment-Tools und Live-Updates in der 2D- und 3D-Vorschau.",
    about_feature_preview_title: "3D Preview",
    about_feature_preview_text: "Pruefe Liveries auf einem 3D-Modell, lade exportierte Texturaenderungen neu und inspiziere gemappte Materialien.",
    about_feature_uv_title: "UV Inspect",
    about_feature_uv_text: "Lade Mesh-UVs, pruefe Material-Slots und exportiere saubere Textur-Templates fuer Paint-Arbeit.",
    about_feature_numberplate_title: "Numberplate Tools",
    about_feature_numberplate_text: "Erstelle wiederverwendbare Numberplate-Templates, generiere gebackene PNGs und verwalte Custom-Plate-Assets fuer lokale Liveries.",
    about_feature_inspect_title: "Inspect Tools",
    about_feature_inspect_text: "Browse, analysiere und bearbeite ACEvo-nahe Dateien vorsichtig mit backup-bewussten Workflows.",
    about_creator: "Creator",
    about_creator_text: "LiveryLab wird von einem Sim-Racing- und Livery-Enthusiasten gebaut, um den AC-EVO-Livery-Workflow schneller, sicherer und leichter verstaendlich zu machen. Das Projekt orientiert sich an praktischen Modding-Aufgaben, vorsichtigen Backups und einem Workflow, der kreative Arbeit nah an den echten Gamefiles haelt.",
    about_car_compatibility: "Car Livery Compatibility",
    car_quick_analysis: "Car Quick Analysis",
    about_car_compatibility_intro: "Read-only Scan von content/cars. Prueft Stock-Designs, EXT_SKIN-Materialien und gemappte Exterior-UVs.",
    about_car_compatibility_pending: "Klicke Analyze Cars, um den Scan zu starten.",
    analyze_cars: "Analyze Cars",
    analyze_cars_again: "Erneut analysieren",
    about_car_compatibility_loading: "Analysiere Autos...",
    about_car_compatibility_checking: "Pruefe",
    about_car_compatibility_done: "Analyse abgeschlossen",
    about_car_compatibility_empty: "Keine Autos gefunden.",
    ready: "Ready",
    paint_only: "Ready",
    unsupported: "Unsupported",
    unknown: "Unknown",
    car: "Auto",
    status: "Status",
    present: "Vorhanden",
    missing: "Fehlt",
    designs: "Designs",
    skin_local_template: "skin-lokales Template",
    car_level_paint: "Car-Level Paint",
    exterior_mesh: "Exterior Mesh",
    ext_skin_uv: "EXT_SKIN UV",
    skin_blocks: "Skin-Bloecke",
    uv_area: "UV-Flaeche",
    no_missing_signals: "Keine fehlenden Kernsignale",
    count_ready: "Ready",
    count_paint_only: "Generated setup",
    count_unsupported: "Unsupported",
    editing_blurb: "Texture-, Mesh-, UV- und Material-Werkzeuge fuer den taeglichen Asset-Workflow.",
    liveries_blurb: "Eigene Skins erstellen, verwalten, exportieren, installieren und nachtraeglich pflegen.",
    settings_blurb: "Document Root und Package Extractor an einem Ort fuer die App-weiten Grundlagen.",
    inspect_blurb: "Dateiformate lesen, debuggen und gezielt editieren, wenn du bis auf Datei-Ebene runter musst.",
    texture_converter_blurb: "Konvertiert zwischen `.texturemips` und DDS, damit du ACEvo-Texturen ausserhalb des Spiels bearbeiten kannst.",
    mesh_converter_blurb: "Liest `.mesh`-Dateien, zeigt Kerndaten an und exportiert Geometrie als OBJ/MTL fuer externe Tools.",
    preview3d_blurb: "Ueberwacht eine aus Photoshop exportierte PNG und bereitet Live-Texturupdates fuer ein GLB-Fahrzeugpreview vor.",
    rim_changer_blurb: "Prueft `.rimmesh`-Dateien und erstellt vorsichtige Test-Swaps mit Ziel-Reifen und Ziel-Rimdesign.",
    uv_creator_blurb: "Prueft UVs aus Meshes, legt sie ueber Texturen und exportiert bei Bedarf Templates fuer Livery-Arbeit.",
    material_viewer_blurb: "Analysiert `.material`-Dateien, zeigt Referenzen und hilft dir beim Export relevanter Texturen.",
    my_liveries_blurb: "Zeigt deine registrierten Liveries mit Status, Dateien, Thumbnail, Export und Aktivierungsfunktionen.",
    custom_grids_blurb: "Lokale Grid-Presets aus deinen My-Liveries-Skins vorbereiten.",
    my_skin_packages_intro: "Einzelne Liveries importieren, Skin Packs teilen oder lokale LiveryLab-Daten per Transfer-ZIP verschieben.",
    skin_creator_blurb: "Erstellt neue Skins aus bestehenden Templates und verdrahtet Presets, Thumbnails und Localization automatisch.",
    numberplate_creator_blurb: "Bake numberplate PNGs for manual placement on your livery texture.",
    numberplate_creator_intro: "Build baked numberplate PNGs from left door, right door and hood backgrounds plus one shared font.",
    drivers_intro: "Wiederverwendbare Driver-Profile fuer fahrerspezifische Helmet-, Suit-, Gloves- und Visor-Assets.",
    open_numberplate_creator: "Numberplates oeffnen",
    document_root_blurb: "Legt das Hauptverzeichnis fuer deine ACEvo-Dateien fest. Darauf bauen Dateibrowser, MyLiveries und viele Automationen auf.",
    language_blurb: "Schaltet die Sprache der App um und hilft dir dabei, Workflows fuer unterschiedliche Nutzer angenehmer zu halten.",
    package_extractor_blurb: "Analysiert `.kspkg`-Archive, schreibt Dateilisten und extrahiert komplette Inhalte oder gezielte Teilmengen.",
    format: "Format",
    import_title: "PNG/DDS nach .texturemips",
    inspect: "Inspect",
    language: "Sprache",
    mips_output: "Ziel .texturemips",
    name: "Name",
    nav_tools: "Werkzeuge",
    overwrite: "Ueberschreiben",
    patch_actor: "Actor patchen",
    pick_root: "Root-Ordner waehlen",
    quit: "Beenden",
    refresh: "Refresh",
    register_visual: "Visual registrieren",
    save: "Speichern",
    rename: "Umbenennen",
    delete: "Löschen",
    selection: "Auswahl",
    size: "Groesse",
    target: "Ziel",
    type: "Typ",
    update_mechanical: "Mechanical aktualisieren",
    active: "Aktiv",
    inactive: "Deaktiviert",
    active_in_game: "Im Spiel aktiv",
    inactive_in_game: "Nicht im Spiel aktiv",
    activate_in_game: "Im Spiel aktivieren",
    deactivate_in_game: "Im Spiel deaktivieren",
    update_in_game: "Im Spiel aktualisieren",
    game_update_needed: "Update noetig",
    game_sync_outdated: "Die lokale Livery wurde geaendert. Aktualisiere sie im Spiel, damit die Aenderungen sichtbar werden.",
    remove_from_game: "Aus Spiel entfernen",
    move_livery_to_trash: "Remove livery from LiveryLab",
    livery_tools: "Livery Tools",
    open_project_folder: "Projektordner öffnen",
    local_storage: "Lokale Ablage",
    game_root: "Game Root",
    storage_mode: "Speicherort",
    local_library_source: "Lokale Library-Quelle",
    local_livery: "Lokale Livery",
    game_livery: "Game-Livery",
    local_root_missing: "Lokale Ablage fehlt",
    hide: "Ausblenden",
    unhide: "Einblenden",
    save_changes: "Speichern",
    relevant_files: "Relevante Dateien",
    localization: "Localization",
    edit: "Bearbeiten",
    complete: "Vollstaendig",
    partial: "Teilweise",
    missing: "Fehlend",
    status_files: "Dateien",
    browser_show: "Dateibrowser einblenden",
    browser_hide: "Dateibrowser ausblenden",
    export_package_title: "Livery Package Export",
    target_folder: "Paket-Zielordner",
    export_package_hint: "Exportierte Pakete sind Snapshots fuer Backup, Sharing oder Reinstall. Arbeitsdateien bleiben in LiveryProjects.",
    storage_health: "Storage Health",
    run_health_check: "Health Check ausfuehren",
    storage_health_intro: "LiveryProjects ist fuer bearbeitbare Arbeitsdateien, LiveryLibrary ist die lokale Quelle fuer My Liveries, und LiveryPackageExport enthaelt nur portable Paket-Exporte.",
    storage_health_pending: "Fuehre den Health Check aus, um App-Ordner, Spiel-Schreibrechte und texconv zu pruefen.",
    storage_health_checking: "Storage Health wird geprueft...",
    update_backup: "Update Backup",
    update_backup_intro: "Erstellt ein portables Backup fuer App-Settings, installierte lokale Skins, Numberplate Templates, Driver Profiles, Custom Grids und Tyre Brands fuer eine neuere LiveryLab-Version.",
    update_backup_workfiles: "Bearbeitbare Arbeitsdateien wie PSD, PSB, Krita- und Affinity-Dateien sind nicht enthalten. Behalte deinen alten LiveryProjects-Ordner oder sichere diese Dateien manuell.",
    create_update_backup: "Update Backup erstellen",
    import_update_backup: "Update Backup importieren",
    preview_png: "Preview PNG",
    no_registered_skins: "Noch keine registrierten Skins.",
    no_registered_skins_found: "Noch keine mit LiveryLab registrierten Skins gefunden.",
    no_driver_profiles: "Noch keine Driver-Profile.",
    no_driver_profiles_found: "Noch keine Driver-Profile in der lokalen Library gefunden.",
    no_loc_registered: "Keine LOC-Dateien registriert.",
    move_to_trash: "In Trash verschieben",
    remove_from_library: "Aus Bibliothek entfernen",
    use_selection: "Auswahl nutzen",
    write_dds: "DDS schreiben",
    write_png: "PNG schreiben",
    write_texturemips: "Texturemips schreiben",
    log_done: "Fertig",
    log_folder_loaded: "Ordner geladen",
    log_path_selected: "Pfad gewaehlt",
    log_ready: "Bereit",
    log_root_empty: "Root ist leer",
    log_running: "Laeuft",
    log_set: "Gesetzt",
  },
  en: {
    home: "Setup",
    editing: "Editing",
    liveries: "Liveries",
    settings: "Settings",
    analyze: "Analyze",
    load_texture: "Load texture",
    export_package: "Export package",
    inspect_open: "Open Inspect",
    to_editing: "Go to Editing",
    to_liveries: "Go to Liveries",
    to_inspect: "Go to Inspect",
    to_settings: "Go to Settings",
    open_texture_converter: "Open TextureConverter",
    open_mesh_converter: "Open MeshConverter",
    open_preview3d: "Open 3D Preview",
    open_rim_changer: "Open Rim Changer",
    open_uv_creator: "Open UV Inspect",
    open_material_viewer: "Open MaterialViewer",
    open_my_liveries: "Open MyLiveries",
    open_skin_creator: "Open LiveryCreator",
    copy_source: "Copy source folder",
    create_skin: "Create skin",
    create_visual: "Create visual",
    dds_output: "DDS output",
    png_output: "PNG output",
    default_output_textures: "Default output: LiveryLibrary/Converted/Textures/<car_id>",
    default_output_meshes: "Default output: LiveryLibrary/Converted/Meshes/<car_id>",
    default_output_uv: "Default output: LiveryLibrary/Converted/UV/<car_id>",
    display_name: "Name",
    export_title: ".texturemips to DDS + PNG",
    file: "File",
    files: "Files",
    folder: "Folder",
    favorites: "Favorites",
    open: "Open",
    remove: "Remove",
    file_list: "File list",
    remember_current_folder: "Remember current folder",
    package_folder: "Package ZIP",
    check_manifest: "Check manifest",
    install_package: "Import to My Liveries",
    update: "Refresh",
    show_hidden: "Show hidden",
    import_existing: "Import existing",
    document_root: "Document Root",
    general: "General",
    package_extractor: "Package Extractor",
    selection_paths: "Paths / folder selection",
    write_file_list: "Write file list",
    extract_all: "Extract all",
    extract_selection: "Extract selection only",
    texture_image: "Texture/Image",
    summary: "Summary",
    presets_groups: "Presets & Groups",
    setup: "Setup",
    language_select: "Language Select",
    my_liveries: "My Liveries",
    custom_grids: "Custom Grids",
    my_skin_packages: "Import / Export",
    online_league: "Online League",
    skin_creator: "Livery Creator",
    numberplate_creator: "Numberplates",
    tyre_brands: "Tyre Brands",
    drivers: "Drivers",
    texture_converter: "Texture Converter",
    mesh_converter: "Mesh Converter",
    preview3d: "3D Inspect",
    rim_changer: "Rim Changer",
    uv_creator: "UV Inspect",
    material_viewer: "Material Viewer",
    file_inspect: "File Inspect",
    file_info: "FILE INFO",
    inspect_intro: "Read, debug and edit ACEvo file formats directly in the app.",
    home_intro: "Complete these setup steps before creating or activating liveries.",
    editing_intro: "Tools for converting, analyzing and preparing assets for your editing pipeline.",
    liveries_intro: "Everything around your custom skins: create, manage, export, install and refine them.",
    settings_intro: "Maintenance, storage status and local app basics for your LiveryLab workspace.",
    about: "About",
    about_intro: "LiveryLab is a focused workspace for creating, inspecting and refining custom liveries for Assetto Corsa EVO.",
    about_features: "Features",
    about_feature_liveries_title: "Custom Liveries",
    about_feature_liveries_text: "Create, install, manage, activate and export local My Liveries without writing into the game until you choose to sync them.",
    about_feature_layer_studio_title: "Layer Studio",
    about_feature_layer_studio_text: "Design livery textures with PNG assets, shapes, text, guides, alignment tools and live 2D/3D preview updates.",
    about_feature_drivers_title: "Custom Drivers",
    about_feature_drivers_text: "Build and share driver profiles for supported cars, including local assets, manifests and reusable import/export workflows.",
    about_feature_tyres_title: "Custom Tyre Brands",
    about_feature_tyres_text: "Create local racing tyre brand sets with slick, blur and sidewall assets, then reuse or share them through ZIP packages.",
    about_feature_grids_title: "Custom Grids",
    about_feature_grids_text: "Assemble grid presets from your local liveries, including driver names, so race setups can be rebuilt and shared quickly.",
    about_feature_textures_title: "Layer Studio",
    about_feature_textures_text: "Design livery textures with PNG assets, shapes, text, guides, alignment tools and live 2D/3D preview updates.",
    about_feature_preview_title: "3D preview",
    about_feature_preview_text: "Preview liveries on a live 3D model, reload exported texture changes and inspect mapped materials.",
    about_feature_uv_title: "UV Inspect",
    about_feature_uv_text: "Load mesh UVs, inspect material slots and export clean texture templates for paint work.",
    about_feature_numberplate_title: "Numberplate Tools",
    about_feature_numberplate_text: "Create reusable numberplate templates, generate baked PNGs and manage custom plate assets for local liveries.",
    about_feature_inspect_title: "Inspection tools",
    about_feature_inspect_text: "Browse, analyze and carefully edit ACEvo-related files with backup-aware workflows.",
    about_creator: "Creator",
    about_creator_text: "LiveryLab is built by a sim-racing and livery enthusiast to make the AC EVO livery workflow faster, safer and easier to understand. The project is shaped around practical modding tasks, careful backups and a workflow that keeps creative work close to the actual game files.",
    about_car_compatibility: "Car Livery Compatibility",
    car_quick_analysis: "Car Quick Analysis",
    about_car_compatibility_intro: "Read-only scan of content/cars. It checks stock designs, EXT_SKIN materials and mapped exterior UVs.",
    about_car_compatibility_pending: "Click Analyze Cars to run the scan.",
    analyze_cars: "Analyze Cars",
    analyze_cars_again: "Analyze again",
    about_car_compatibility_loading: "Analyzing cars...",
    about_car_compatibility_checking: "Checking",
    about_car_compatibility_done: "Analysis complete",
    about_car_compatibility_empty: "No cars found.",
    ready: "Ready",
    paint_only: "Ready",
    unsupported: "Unsupported",
    unknown: "Unknown",
    car: "Car",
    status: "Status",
    present: "Present",
    missing: "Missing",
    designs: "Designs",
    skin_local_template: "skin-local template",
    car_level_paint: "car-level paint",
    exterior_mesh: "Exterior mesh",
    ext_skin_uv: "EXT_SKIN UV",
    skin_blocks: "Skin blocks",
    uv_area: "UV area",
    no_missing_signals: "No missing core signals",
    count_ready: "Ready",
    count_paint_only: "Generated setup",
    count_unsupported: "Unsupported",
    editing_blurb: "Texture, mesh, UV and material tools for everyday asset workflows.",
    liveries_blurb: "Create, manage, export, install and maintain custom skins.",
    settings_blurb: "Document root and package extraction in one place for app-wide basics.",
    inspect_blurb: "Read file formats, debug issues and make targeted edits when you need to work at file level.",
    texture_converter_blurb: "Converts between `.texturemips` and DDS so you can edit ACEvo textures outside the game.",
    mesh_converter_blurb: "Reads `.mesh` files, shows core data and exports geometry as OBJ/MTL for external tools.",
    preview3d_blurb: "Watches a Photoshop-exported PNG and prepares live texture updates for a GLB vehicle preview.",
    rim_changer_blurb: "Inspects `.rimmesh` files and creates cautious test swaps while keeping target tyre and rim design data.",
    uv_creator_blurb: "Inspects UVs from meshes, overlays them on textures and exports templates for livery work when needed.",
    material_viewer_blurb: "Analyzes `.material` files, shows references and helps export relevant textures.",
    my_liveries_blurb: "Shows your registered liveries with status, files, thumbnails, export and activation tools.",
    custom_grids_blurb: "Prepare local grid presets from your My Liveries skins.",
    my_skin_packages_intro: "Import single liveries, share category-based skin packs or move local LiveryLab data with transfer ZIPs.",
    skin_creator_blurb: "Creates new skins from existing templates and automatically wires presets, thumbnails and localization.",
    numberplate_creator_blurb: "Bake numberplate PNGs for manual placement on your livery texture.",
    numberplate_creator_intro: "Build baked numberplate PNGs from left door, right door and hood backgrounds plus one shared font.",
    drivers_intro: "Reusable driver profiles for driver-specific helmet, suit, gloves and visor assets.",
    open_numberplate_creator: "Open Numberplates",
    document_root_blurb: "Defines the main folder for your ACEvo files. The file browser, MyLiveries and many automations depend on it.",
    language_blurb: "Switches the app language and helps keep workflows comfortable for different users.",
    package_extractor_blurb: "Analyzes `.kspkg` archives, writes file lists and extracts full contents or selected subsets.",
    format: "Format",
    import_title: "PNG/DDS to .texturemips",
    inspect: "Inspect",
    language: "Language",
    mips_output: "Output .texturemips",
    name: "Name",
    nav_tools: "Tools",
    overwrite: "Overwrite",
    patch_actor: "Patch actor",
    pick_root: "Choose root folder",
    quit: "Quit",
    refresh: "Refresh",
    register_visual: "Register visual",
    save: "Save",
    rename: "Rename",
    delete: "Delete",
    selection: "Selection",
    size: "Size",
    target: "Target",
    type: "Type",
    update_mechanical: "Update mechanical",
    numberplate_variant: "Numberplate Variant",
    numberplate_font: "Numberplate font",
    numberplate_background: "Numberplate background",
    numberplate_mode: "Numberplate mode",
    numberplate_mode_same: "Same for all",
    numberplate_mode_surface: "Separated for all",
    numberplate_hood_background: "Hood background",
    numberplate_left_background: "Vehicle-left door background",
    numberplate_right_background: "Vehicle-right door background",
    numberplate_text_color: "Number color",
    numberplate_text_alpha: "Number alpha",
    save_numberplate: "Save numberplate",
    active: "Active",
    inactive: "Inactive",
    active_in_game: "Active in game",
    inactive_in_game: "Not active in game",
    activate_in_game: "Activate in Game",
    deactivate_in_game: "Deactivate in Game",
    update_in_game: "Update in Game",
    game_update_needed: "Update needed",
    game_sync_outdated: "This local livery changed. Update it in game to make the changes visible.",
    remove_from_game: "Remove from Game",
    move_livery_to_trash: "Remove livery from LiveryLab",
    livery_tools: "Livery Tools",
    open_project_folder: "Project Folder",
    local_storage: "Local storage",
    game_root: "Game root",
    storage_mode: "Storage",
    local_library_source: "Local library source",
    local_livery: "Local livery",
    game_livery: "Game livery",
    local_root_missing: "Local storage missing",
    hide: "Hide",
    unhide: "Show",
    save_changes: "Save",
    relevant_files: "Relevant files",
    localization: "Localization",
    edit: "Edit",
    complete: "Complete",
    partial: "Partial",
    missing: "Missing",
    status_files: "files",
    browser_show: "Show file browser",
    browser_hide: "Hide file browser",
    export_package_title: "Livery Package Export",
    target_folder: "Package target folder",
    export_package_hint: "Exported packages are snapshots for backup, sharing or reinstall. Work files stay in LiveryProjects.",
    storage_health: "Storage Health",
    run_health_check: "Run Health Check",
    storage_health_intro: "LiveryProjects is for editable work files, LiveryLibrary is the local My Liveries source, and LiveryPackageExport only stores portable package exports.",
    storage_health_pending: "Run the health check to verify app folders, game write access and texconv.",
    storage_health_checking: "Checking storage health...",
    update_backup: "Update Backup",
    update_backup_intro: "Create a portable backup for moving app settings, installed local skins, numberplate templates, driver profiles, custom grids and tyre brands into a newer LiveryLab version.",
    update_backup_workfiles: "Editable work files such as PSD, PSB, Krita and Affinity files are not included. Keep your old LiveryProjects folder or back those files up manually.",
    create_update_backup: "Create Update Backup",
    import_update_backup: "Import Update Backup",
    preview_png: "Preview PNG",
    no_registered_skins: "No registered skins yet.",
    no_registered_skins_found: "No skins registered with LiveryLab yet.",
    no_driver_profiles: "No driver profiles yet.",
    no_driver_profiles_found: "No driver profiles found in the local library.",
    no_loc_registered: "No LOC files registered.",
    move_to_trash: "Move to Trash",
    remove_from_library: "Remove from library",
    use_selection: "Use selection",
    write_dds: "Write DDS",
    write_png: "Write PNG",
    write_texturemips: "Write texturemips",
    log_done: "Done",
    log_folder_loaded: "Folder loaded",
    log_path_selected: "Path selected",
    log_ready: "Ready",
    log_root_empty: "Root is empty",
    log_running: "Running",
    log_set: "Set",
  },
};

function t(key) {
  return I18N[state.language]?.[key] || I18N.en[key] || key;
}

function setButtonWorking(buttonOrId, working = false, label = "Working...") {
  const button = typeof buttonOrId === "string" ? $(buttonOrId) : buttonOrId;
  if (!button) return;
  if (working) {
    if (!button.dataset.idleHtml) button.dataset.idleHtml = button.innerHTML;
    button.disabled = true;
    button.classList.add("is-working");
    button.setAttribute("aria-busy", "true");
    button.innerHTML = `<span class="button-spinner" aria-hidden="true"></span><span>${escapeHtml(label || "Working...")}</span>`;
    return;
  }
  button.disabled = false;
  button.classList.remove("is-working");
  button.removeAttribute("aria-busy");
  if (button.dataset.idleHtml) {
    button.innerHTML = button.dataset.idleHtml;
    delete button.dataset.idleHtml;
    decorateIconButtons(button);
  }
}

function setToolLoading(surface, loading = false) {
  const key = String(surface || "").trim();
  if (!key) return;
  const root = document.querySelector(`[data-loading-surface="${key}"]`);
  const overlay = document.querySelector(`[data-loading-overlay="${key}"]`);
  root?.classList.toggle("is-loading", Boolean(loading));
  if (overlay) {
    overlay.hidden = !loading;
    overlay.setAttribute("aria-hidden", loading ? "false" : "true");
  }
}

async function withButtonWorking(buttonOrId, label, task) {
  const button = typeof buttonOrId === "string" ? $(buttonOrId) : buttonOrId;
  setButtonWorking(button, true, label);
  try {
    return await task();
  } finally {
    setButtonWorking(button, false);
  }
}

function delay(ms) {
  return new Promise((resolve) => window.setTimeout(resolve, ms));
}

function applyLanguage(language) {
  state.language = I18N[language] ? language : "en";
  document.documentElement.lang = state.language;
  document.querySelectorAll("[data-i18n]").forEach((node) => {
    node.textContent = t(node.dataset.i18n);
  });
  document.querySelectorAll("[data-i18n-attr]").forEach((node) => {
    node.dataset.i18nAttr.split(";").forEach((entry) => {
      const [attr, key] = entry.split(":").map((part) => part.trim());
      if (attr && key) node.setAttribute(attr, t(key));
    });
  });
  updateTexconvUi();
  updateLayoutForTab(activeTab());
  decorateIconButtons();
  if (state.mySkins?.length) renderMySkins();
  renderDriverSourceSkins();
  if (state.mySkinPackageInfo) renderMySkinPackageSummary();
  if (state.carCompatibility) renderCarCompatibility();
}

function updateTexconvUi() {
  const path = state.texconvPath || "";
  const status = $("texconvStatusText");
  const configured = Boolean(path);
  if (status) {
    status.textContent = configured
      ? `${state.language === "en" ? "Texconv active" : "Texconv aktiv"}: ${path}`
      : (state.language === "en"
        ? "Texconv not found. For custom preview thumbnails, place texconv.exe in LiveryLab/tools."
        : "Texconv nicht gefunden. Fuer benutzerdefinierte Preview-Thumbnails bitte texconv.exe in LiveryLab/tools ablegen.");
  }
}

function setSplashProgress(message, percent) {
  const text = $("splashProgressText");
  const bar = $("splashProgressBar");
  if (text && message) text.textContent = message;
  if (bar && Number.isFinite(percent)) {
    bar.style.width = `${Math.max(0, Math.min(100, percent))}%`;
  }
}

function showSplashLoading(message = "Loading workspace...", percent = 8) {
  const splash = $("splashScreen");
  if (!splash) return;
  showStartupBackdrop();
  window.clearTimeout(state.splashHideTimer);
  state.splashHideTimer = 0;
  splash.hidden = false;
  splash.classList.remove("is-hidden");
  setSplashProgress(message, percent);
}

function showStartupBackdrop() {
  document.body?.classList.add("startup-backdrop-active");
}

function hideStartupBackdrop() {
  document.body?.classList.remove("startup-backdrop-active");
}

function hideSplashOverlay({ keepBackdrop = false } = {}) {
  const splash = $("splashScreen");
  if (!splash) {
    if (!keepBackdrop) hideStartupBackdrop();
    return;
  }
  window.clearTimeout(state.splashHideTimer);
  splash.classList.add("is-hidden");
  state.splashHideTimer = window.setTimeout(() => {
    splash.hidden = true;
    state.splashHideTimer = 0;
    if (!keepBackdrop) hideStartupBackdrop();
  }, 700);
}

function hideSplashWhenReady() {
  const splash = $("splashScreen");
  if (!splash) {
    finishStartupScreen();
    return;
  }
  window.setTimeout(() => {
    finishStartupScreen();
    hideSplashOverlay({ keepBackdrop: true });
  }, 250);
}

function finishStartupScreen() {
  if (!state.workflowChoiceComplete) showWorkflowChoiceScreen();
}

function currentWorkflowScope() {
  return normalizeWorkflowScope(state.workflowScope || "advanced");
}

function workflowAreaAllowed(area = "", scope = currentWorkflowScope()) {
  const key = String(area || "shared").trim().toLowerCase();
  if (!key || key === "shared") return true;
  return normalizeWorkflowScope(scope) === key;
}

function workflowTabAllowed(tab = "", scope = currentWorkflowScope()) {
  const key = String(tab || "home").trim();
  if (SHARED_WORKFLOW_TABS.has(key)) return true;
  if (normalizeWorkflowScope(scope) === "advanced") return ADVANCED_WORKFLOW_TABS.has(key);
  if (normalizeWorkflowScope(scope) === "official") return OFFICIAL_WORKFLOW_TABS.has(key);
  return false;
}

function coerceWorkflowTab(tab = "home", scope = currentWorkflowScope()) {
  return workflowTabAllowed(tab, scope) ? tab : "home";
}

function showWorkflowChoiceScreen() {
  const screen = $("workflowChoiceScreen");
  if (!screen) {
    enterWorkflowChoice(state.workflowScope || "advanced");
    return;
  }
  showStartupBackdrop();
  document.body?.classList.add("workflow-choice-pending", "workflow-choice-active");
  screen.hidden = false;
}

function hideWorkflowChoiceScreen() {
  const screen = $("workflowChoiceScreen");
  if (screen) screen.hidden = true;
  document.body?.classList.remove("workflow-choice-pending", "workflow-choice-active");
}

function setWorkflowChoiceBusy(scope = "", busy = false) {
  const selectedScope = normalizeWorkflowScope(scope || "advanced");
  state.workflowChoiceBusy = Boolean(busy);
  const screen = $("workflowChoiceScreen");
  if (screen) screen.setAttribute("aria-busy", busy ? "true" : "false");
  document.querySelectorAll("[data-workflow-choice]").forEach((button) => {
    const selected = busy && normalizeWorkflowScope(button.dataset.workflowChoice || "advanced") === selectedScope;
    button.disabled = Boolean(busy);
    button.classList.toggle("is-loading", selected);
    button.classList.toggle("is-waiting", Boolean(busy) && !selected);
  });
}

function updateImportExportCopy(scope = currentWorkflowScope()) {
  const official = normalizeWorkflowScope(scope) === "official";
  const copy = {
    importExportIntro: official
      ? "Install or share Official liveries, exchange category-based skin packs or move a complete LiveryLab workspace. Imports remain local until you activate a livery from My Liveries."
      : "Import single liveries, share category-based skin packs or move local LiveryLab data with transfer ZIPs.",
    singleLiveryPackageIntro: official
      ? "Import one read-only or editable LiveryLab package into Official My Liveries. Compatible Advanced packages can be converted into new Official projects. To export a single livery, open it in My Liveries and choose Export Package."
      : "Import one LiveryLab livery package into My Liveries. Official packages are converted into separate Advanced projects; the source package and any installed Official livery remain unchanged. Activate the new Advanced livery later from My Liveries.",
    skinPackIntro: official
      ? "Share all liveries from one Official My Liveries category in a single ZIP. Category assignments, supported project or library data and known dependencies are restored on import. Use Workspace Transfer when you need a broader backup of projects and app data."
      : "Export a My Liveries category as a shareable skin pack, or import a skin pack that only contains My Liveries. Categories and known livery dependencies are restored on import.",
    skinPackImportIntro: official
      ? "Use this only for Skin Pack ZIPs containing My Liveries data. Select the ZIP and inspect it before importing. Full workspace ZIPs must be imported through Workspace Transfer."
      : "Use this for skin-pack transfer ZIPs that contain My Liveries only. Use Workspace Transfer for full transfer ZIPs.",
    workspaceTransferIntro: official
      ? "Create or restore a broader LiveryLab backup containing selected Official libraries, editable projects, required SavedCars, app settings and other local project data. Use this when moving LiveryLab to another installation or PC."
      : "Export or import a larger transfer ZIP for moving local LiveryLab data such as My Liveries, app settings, profiles, numberplates, grids and project files.",
  };
  Object.entries(copy).forEach(([id, text]) => {
    const element = $(id);
    if (element) element.textContent = text;
  });
}

function updateSettingsCopy(scope = currentWorkflowScope()) {
  const settingsIntro = $("settingsIntro");
  if (!settingsIntro) return;
  settingsIntro.textContent = normalizeWorkflowScope(scope) === "official"
    ? "Manage Official liveries that were removed from My Liveries and moved to LiveryLab-managed trash. These tools affect local LiveryLab data only."
    : t("settings_intro");
}

function updateAboutCopy(scope = currentWorkflowScope()) {
  const official = normalizeWorkflowScope(scope) === "official";
  const intro = $("aboutIntro");
  const title = $("aboutFeaturesTitle");
  if (intro) {
    intro.textContent = official
      ? "LiveryLab EVO is a local workspace for creating, previewing, sharing and safely activating liveries through AC EVO's official ExternalLiveries workflow."
      : t("about_intro");
  }
  if (title) title.textContent = official ? "Official Workflow Features" : t("about_features");
}

function applyWorkflowScopeUi() {
  const scope = currentWorkflowScope();
  if (document.body) document.body.dataset.workflowScope = scope;
  document.querySelectorAll("[data-workflow-area]").forEach((element) => {
    element.classList.toggle("is-workflow-hidden", !workflowAreaAllowed(element.dataset.workflowArea, scope));
  });
  document.querySelectorAll(".my-skins-scope-tabs").forEach((tabs) => {
    tabs.hidden = true;
  });
  document.querySelectorAll('input[name="skinProjectType"]').forEach((input) => {
    const value = String(input.value || "");
    const official = value === "official_external_livery";
    input.checked = scope === "official" ? official : !official;
    input.disabled = true;
  });
  updateImportExportCopy(scope);
  updateSettingsCopy(scope);
  updateAboutCopy(scope);
  updateWorkflowSetupCopy();
  renderOfficialCarResourcesStatus(state.officialCarResourcesStatus);
  renderContentPackageStatus(state.contentPackageStatus);
  renderOfficialRuntimeStatus();
  syncHomePackageStatus();
  updateSkinProjectTypeMode();
  updateMySkinsScopeButtons();
  applySetupGateStatus();
  const tab = activeTab();
  if (!workflowTabAllowed(tab, scope)) setTab("home");
}

function runtimeSwitchTargetLabel(scope = currentWorkflowScope()) {
  return normalizeWorkflowScope(scope) === "official" ? "Official" : "Advanced";
}

function runtimeSwitchConfirmText(scope = currentWorkflowScope()) {
  if (normalizeWorkflowScope(scope) === "official") {
    return "LiveryLab will switch AC EVO to the Official External Liveries runtime. content.kspkg will be restored if needed. Advanced LiveryLab garage entries will be stored in LiveryLab's local runtime vault, matching Official garage entries will be restored, and Advanced LiveryLab Safety hooks will be removed. Loose content files stay untouched and can become active again when you switch back to Advanced.";
  }
  return "LiveryLab will switch AC EVO to the Advanced extracted-content runtime. content.kspkg will be disabled if needed so loose content is used. Official External Livery garage entries will be stored in LiveryLab's local runtime vault, and matching Advanced garage entries will be restored. After a new full extraction, previously active Advanced liveries and their supported dependencies are reapplied automatically. LiveryLab Safety will be checked during Advanced loading.";
}

function runtimeSwitchDetails(preflight = {}) {
  const savedPlan = preflight?.savedCars?.plan || {};
  const packageState = preflight?.package || {};
  const lines = [
    `Current runtime: ${preflight.currentRuntime || "unknown"}`,
    `Target runtime: ${preflight.targetRuntime || "-"}`,
    `Package action: ${preflight.packageAction || "none"}`,
    `Package source: ${packageState.contentPackagePath || packageState.activePath || packageState.disabledPath || "not found"}`,
    `SavedCars to store: ${savedPlan.toStoreCount || 0}`,
    `SavedCars to restore: ${savedPlan.restoreEligibleCount || 0}`,
    `Unknown SavedCars left untouched: ${savedPlan.unknownCount || 0}`,
    `Conflict SavedCars: ${savedPlan.conflictCount || 0}`,
  ];
  if (packageState.mixedPackageState || (packageState.activeExists && packageState.disabledExists)) {
    const active = packageState.activePackage || {};
    const disabled = Array.isArray(packageState.disabledPackages) ? packageState.disabledPackages : [];
    lines.push(
      "",
      "Content package conflict:",
      `Active: ${active.path || packageState.activePath || "-"} | ${formatUpdateBackupBytes(active.size || 0)} | modified ${active.modifiedAt || "unknown"}`,
      ...disabled.map((item) => `Renamed: ${item.path || "-"} | ${formatUpdateBackupBytes(item.size || 0)} | modified ${item.modifiedAt || "unknown"}`),
      "LiveryLab will not replace, rename or delete either file automatically."
    );
  }
  if (preflight?.requiresAdvancedContentSetup) {
    lines.push(`Advanced content setup: ${preflight?.advancedContentSetup?.reason || "Full extraction required"}`);
  }
  if (preflight?.officialSafetyCleanupNeeded) {
    lines.push("LiveryLab Safety: Advanced multiplayer safety hooks will be removed for the Official runtime.");
  }
  const warnings = Array.isArray(preflight.warnings) ? preflight.warnings : [];
  const blockers = Array.isArray(preflight.blockers) ? preflight.blockers : [];
  if (warnings.length) lines.push("", "Warnings:", ...warnings.map((item) => `- ${item}`));
  if (blockers.length) lines.push("", "Blockers:", ...blockers.map((item) => `- ${item}`));
  return lines.join("\n");
}

async function guideContentPackageUpdateConflict(scope, preflight = {}, root = "", recheckCount = 0) {
  const conflict = preflight?.packageUpdateConflict || {};
  if (!conflict.manualResolutionSupported || !conflict.obsoleteDisabledPath) return "unsupported";
  if (typeof window.LiveryLabEvoUpdateWorkflow?.open === "function") {
    const opened = await window.LiveryLabEvoUpdateWorkflow.open(root || preflight?.package?.root || "");
    if (opened) return "cancel";
  }
  const active = conflict.activePackage || preflight?.package?.activePackage || {};
  const obsolete = conflict.obsoleteDisabledPackage || {};
  const details = [
    "DELETE MANUALLY:",
    conflict.obsoleteDisabledPath,
    `${formatUpdateBackupBytes(obsolete.size || 0)} | modified ${obsolete.modifiedAt || "unknown"}`,
    "",
    "KEEP:",
    conflict.activePath || active.path || preflight?.package?.activePath || "content.kspkg",
    `${formatUpdateBackupBytes(active.size || 0)} | modified ${active.modifiedAt || "unknown"}`,
    "",
    "LiveryLab will not delete, rename or move either package file.",
    normalizeWorkflowScope(scope) === "advanced"
      ? "After the old disabled file is gone, Check Again will continue to the required full Advanced extraction."
      : "After the old disabled file is gone, Check Again will continue preparing the Official runtime.",
  ].join("\n");

  while (true) {
    const choice = await showChoiceModal({
      type: "warning",
      title: "AC EVO Update Detected",
      subtitle: recheckCount ? "The old disabled package is still present" : "Steam installed a new content package",
      message: `Keep the active content.kspkg. Manually delete only this old LiveryLab-disabled file:\n${conflict.obsoleteDisabledPath}`,
      details,
      confirmLabel: "Check Again",
      alternateLabel: "Open AC EVO Folder",
      cancelLabel: "Close",
    });
    if (choice !== "alternate") return choice === true ? "recheck" : "cancel";
    if (typeof window.pywebview?.api?.open_folder !== "function") return "cancel";
    const opened = await window.pywebview.api.open_folder(root || preflight?.package?.root || "");
    if (!opened?.ok) {
      showResult(opened || { ok: false, message: "The AC EVO folder could not be opened." }, "Open AC EVO folder", "open_folder");
      return "cancel";
    }
  }
}

function runtimeSwitchHasChanges(preflight = {}) {
  const savedPlan = preflight?.savedCars?.plan || {};
  return String(preflight.packageAction || "none") !== "none"
    || Number(savedPlan.toStoreCount || 0) > 0
    || Number(savedPlan.restoreEligibleCount || 0) > 0;
}

function officialSafetyCleanupNeeded(status = state.officialRuntimeSafetyStatus) {
  if (!status || typeof status !== "object") return false;
  return Boolean(
    status.installed
    || status.multiplayerHasSafetyTag
    || status.multiplayerHasUnsafeSkinsTag
    || status.multiplayerHasSmokeTag
    || status.safetyScriptExists
    || status.unsafeSkinsScriptExists
    || status.smokeScriptExists
    || status.manifestExists
  );
}

async function loadOfficialRuntimeSafetyStatus(root = "") {
  const normalized = normalizeRootPath(root || $("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || state.root || "");
  if (!normalized || typeof window.pywebview?.api?.get_multiplayer_safety_status !== "function") {
    state.officialRuntimeSafetyStatus = null;
    return null;
  }
  try {
    const result = await window.pywebview.api.get_multiplayer_safety_status({ root: normalized });
    state.officialRuntimeSafetyStatus = result?.ok ? (result.data || null) : null;
  } catch (_error) {
    state.officialRuntimeSafetyStatus = null;
  }
  return state.officialRuntimeSafetyStatus;
}

function officialRuntimeRootReady() {
  const root = normalizeRootPath(state.root || $("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || "");
  const savedRoot = normalizeRootPath(state.savedRoot || "");
  return Boolean(root && savedRoot && root === savedRoot && setupGateRootReady());
}

function setupGateOfficialRuntimeReady() {
  const root = normalizeRootPath(state.root || $("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || "");
  const packageState = state.contentPackageStatus;
  return Boolean(
    officialRuntimeRootReady()
    && root === normalizeRootPath(state.officialRuntimePreparedRoot || "")
    && packageState?.activeExists
    && !packageState?.mixedPackageState
    && !packageState?.disabledAmbiguous
    && !officialSafetyCleanupNeeded()
  );
}

function renderOfficialRuntimeStatus() {
  const button = $("prepareOfficialRuntime");
  const target = $("officialRuntimeStatus");
  const rootReady = officialRuntimeRootReady();
  const ready = setupGateOfficialRuntimeReady();
  if (button) {
    button.disabled = !rootReady || state.runtimeSwitchBusy;
    button.textContent = state.runtimeSwitchBusy ? "Preparing..." : (ready ? "Official Runtime Ready" : "Prepare Official Runtime");
  }
  if (!target) return;
  if (!rootReady) {
    target.className = "root-status is-warning";
    target.textContent = "Select and save the AC EVO Game Folder first.";
  } else if (ready) {
    target.className = "root-status is-ok";
    target.textContent = "Official runtime ready: content.kspkg is active, SavedCars are switched and Advanced Safety hooks are removed.";
  } else if (state.contentPackageStatus?.mixedPackageState || state.contentPackageStatus?.disabledAmbiguous) {
    target.className = "root-status is-error";
    target.textContent = "Resolve the content package conflict before preparing the Official runtime.";
  } else if (officialSafetyCleanupNeeded()) {
    target.className = "root-status is-warning";
    target.textContent = "Advanced LiveryLab Safety is installed and will be removed when the Official runtime is prepared.";
  } else if (state.contentPackageStatus?.disabledExists && !state.contentPackageStatus?.activeExists) {
    target.className = "root-status is-warning";
    target.textContent = "The renamed content.kspkg and runtime SavedCars are ready to switch after confirmation.";
  } else {
    target.className = "root-status is-warning";
    target.textContent = "Run the Official runtime preparation to verify content.kspkg, SavedCars and LiveryLab Safety.";
  }
}

function currentAdvancedContentSetup() {
  const preflight = state.runtimeSwitchPreflight;
  if (!preflight || normalizeWorkflowScope(preflight.targetRuntime || "advanced") !== "advanced") return null;
  return preflight.advancedContentSetup && typeof preflight.advancedContentSetup === "object"
    ? preflight.advancedContentSetup
    : null;
}

function advancedContentNeedsFullExtraction() {
  const setup = currentAdvancedContentSetup();
  return Boolean(setup && setup.ready !== true);
}

const ADVANCED_ACTIVE_REFRESH_STORAGE_PREFIX = "liverylab.advancedActiveRefresh.v1";

function advancedActiveRefreshContext(preflight = state.runtimeSwitchPreflight || {}) {
  const setup = preflight?.advancedContentSetup && typeof preflight.advancedContentSetup === "object"
    ? preflight.advancedContentSetup
    : {};
  const manifest = setup.manifest && typeof setup.manifest === "object" ? setup.manifest : {};
  const root = normalizeRootPath(preflight?.package?.root || setup.root || $("rootPath")?.value.trim() || state.root || "");
  const extractionId = String(manifest.completedAt || "").trim();
  return {
    root,
    extractionId,
    ready: setup.ready === true && setup.legacy !== true && Boolean(extractionId),
    storageKey: root && extractionId
      ? `${ADVANCED_ACTIVE_REFRESH_STORAGE_PREFIX}:${encodeURIComponent(root.toLowerCase())}:${encodeURIComponent(extractionId)}`
      : "",
  };
}

function loadAdvancedActiveRefresh(context = {}) {
  if (!context.storageKey) return null;
  try {
    const value = JSON.parse(window.localStorage.getItem(context.storageKey) || "null");
    return value && typeof value === "object" ? value : null;
  } catch (_error) {
    return null;
  }
}

function saveAdvancedActiveRefresh(context = {}, value = {}) {
  if (!context.storageKey) return;
  try {
    window.localStorage.setItem(context.storageKey, JSON.stringify({
      ...value,
      root: context.root,
      extractionId: context.extractionId,
      updatedAt: new Date().toISOString(),
    }));
  } catch (error) {
    console.warn("Advanced active-livery refresh state could not be saved", error);
  }
}

function advancedActiveRefreshSummary(items = {}) {
  const rows = Object.values(items && typeof items === "object" ? items : {});
  return {
    total: rows.length,
    refreshed: rows.filter((row) => row?.status === "refreshed").length,
    failed: rows.filter((row) => row?.status === "failed").length,
    pending: rows.filter((row) => row?.status === "pending").length,
  };
}

async function fallbackRefreshActiveAdvancedLiveries(context) {
  let refresh = loadAdvancedActiveRefresh(context);
  let items = refresh?.items && typeof refresh.items === "object" ? { ...refresh.items } : {};
  if (!refresh || !Object.keys(items).length) {
    const listed = await window.pywebview.api.get_my_skins({
      root: context.root,
      includeHidden: true,
      scope: "advanced",
    });
    if (!listed?.ok) throw new Error(listed?.message || "Active Advanced liveries could not be listed.");
    const rootKey = normalizeRootPath(context.root).toLowerCase();
    const skins = Array.isArray(listed.data?.skins) ? listed.data.skins : [];
    const candidates = skins.filter((skin) => {
      const projectType = String(skin?.project_type || skin?.projectType || "").trim().toLowerCase();
      const skinRoot = normalizeRootPath(skin?.game_root || "").toLowerCase();
      return String(skin?.key || "").trim()
        && projectType !== "official_external_livery"
        && skin?.stored_active !== false
        && skin?.active !== false
        && skinRoot === rootKey;
    });
    items = Object.fromEntries(candidates.map((skin) => [String(skin.key), {
      status: "pending",
      displayName: String(skin.display_name || skin.skin_id || skin.key || "Advanced livery"),
      error: "",
    }]));
    refresh = { state: "pending", items, summary: advancedActiveRefreshSummary(items) };
    saveAdvancedActiveRefresh(context, refresh);
  }

  const pendingKeys = Object.entries(items)
    .filter(([, row]) => row?.status !== "refreshed")
    .map(([key]) => key);
  for (let index = 0; index < pendingKeys.length; index += 1) {
    const key = pendingKeys[index];
    const row = { ...(items[key] || {}) };
    showStatusModal({
      type: "info",
      title: "Refresh Active Advanced Liveries",
      subtitle: `Livery ${index + 1} of ${pendingKeys.length}`,
      message: `Restoring ${row.displayName || key} after the full extraction...`,
      details: "Livery files and supported Advanced dependencies are reapplied through the normal Activate in Game workflow.",
      blockClose: true,
    });
    let result = null;
    try {
      result = await window.pywebview.api.set_my_skin_active({ skinKey: key, active: true });
    } catch (error) {
      result = { ok: false, message: error?.message || String(error) };
    }
    if (result?.ok) {
      row.status = "refreshed";
      row.error = "";
    } else {
      row.status = "failed";
      row.error = String(result?.message || "Refresh failed.").trim();
    }
    row.updatedAt = new Date().toISOString();
    items[key] = row;
    const summary = advancedActiveRefreshSummary(items);
    refresh = {
      state: summary.failed || summary.pending ? "partial" : "completed",
      items,
      summary,
    };
    saveAdvancedActiveRefresh(context, refresh);
  }
  if (!pendingKeys.length) {
    const summary = advancedActiveRefreshSummary(items);
    refresh = { state: summary.failed || summary.pending ? "partial" : "completed", items, summary };
    saveAdvancedActiveRefresh(context, refresh);
  }
  return refresh;
}

async function refreshActiveAdvancedLiveriesAfterExtraction(preflight = state.runtimeSwitchPreflight || {}) {
  const context = advancedActiveRefreshContext(preflight);
  if (!context.ready) return { skipped: true, reason: "no current full extraction" };
  const saved = loadAdvancedActiveRefresh(context);
  if (saved?.state === "completed") return { ...saved, skipped: true, reason: "already refreshed for this extraction" };
  const manifestRefresh = preflight?.advancedContentSetup?.manifest?.activeLiveryRefresh;
  if (manifestRefresh?.state === "completed") {
    saveAdvancedActiveRefresh(context, manifestRefresh);
    log("Advanced active-livery refresh already completed for this extraction.");
    return { ...manifestRefresh, skipped: true, reason: "already refreshed for this extraction" };
  }

  showStatusModal({
    type: "info",
    title: "Refresh Active Advanced Liveries",
    subtitle: "Checking the last full extraction",
    message: "LiveryLab is checking whether previously active Advanced liveries need to be reapplied.",
    blockClose: true,
  });
  let refresh = null;
  if (typeof window.pywebview?.api?.refresh_active_advanced_liveries === "function") {
    const result = await window.pywebview.api.refresh_active_advanced_liveries({ root: context.root });
    if (!result?.ok) {
      closeStatusModal({ force: true });
      showResult(result || { ok: false, message: "Active Advanced liveries could not be refreshed." }, "Active Advanced livery refresh failed", "refresh_active_advanced_liveries");
      return { state: "partial", summary: { total: 0, refreshed: 0, failed: 1, pending: 0 }, failures: [{ error: result?.message || "Refresh failed." }] };
    }
    refresh = result.data || {};
    saveAdvancedActiveRefresh(context, refresh);
  } else {
    try {
      refresh = await fallbackRefreshActiveAdvancedLiveries(context);
    } catch (error) {
      closeStatusModal({ force: true });
      showStatusModal({
        type: "error",
        title: "Refresh Active Advanced Liveries",
        subtitle: "Refresh could not start",
        error: error?.message || String(error),
      });
      return { state: "partial", summary: { total: 0, refreshed: 0, failed: 1, pending: 0 }, failures: [{ error: error?.message || String(error) }] };
    }
  }

  const summary = refresh?.summary && typeof refresh.summary === "object"
    ? refresh.summary
    : advancedActiveRefreshSummary(refresh?.items || {});
  const failures = Array.isArray(refresh?.failures)
    ? refresh.failures
    : Object.entries(refresh?.items || {})
      .filter(([, row]) => row?.status === "failed")
      .map(([skinKey, row]) => ({ skinKey, displayName: row?.displayName || skinKey, error: row?.error || "Refresh failed." }));
  if (refresh?.skipped || (!failures.length && Number(summary.total || 0) === 0)) {
    closeStatusModal({ force: true });
    log("Advanced active-livery refresh completed without pending liveries.");
    return { ...refresh, summary, failures, skipped: true, reason: refresh?.reason || "no active liveries required refresh" };
  }
  const details = [
    `Extraction: ${context.extractionId}`,
    `Active Advanced liveries: ${summary.total || 0}`,
    `Refreshed: ${summary.refreshed || 0}`,
    `Failed: ${summary.failed || 0}`,
  ];
  if (failures.length) {
    details.push("", "Failed liveries:", ...failures.slice(0, 12).map((row) => `- ${row.displayName || row.skinKey || "Livery"}: ${row.error || "Refresh failed."}`));
  }
  showStatusModal({
    type: failures.length ? "warning" : "success",
    title: "Refresh Active Advanced Liveries",
    subtitle: failures.length ? "Completed with warnings" : "Refresh completed",
    message: failures.length
      ? "All other active Advanced liveries were restored. Failed liveries will be retried the next time Advanced is selected."
      : summary.total
        ? "Previously active Advanced liveries were restored after the full extraction."
        : "No active Advanced liveries needed to be restored.",
    details: details.join("\n"),
  });
  log(`Advanced active-livery refresh: ${summary.refreshed || 0} refreshed, ${summary.failed || 0} failed.`);
  return { ...refresh, summary, failures, skipped: false };
}

async function saveWorkflowScopePreference(scope = currentWorkflowScope()) {
  if (typeof window.pywebview?.api?.save_workflow_scope !== "function") return;
  const result = await window.pywebview.api.save_workflow_scope(normalizeWorkflowScope(scope));
  if (!result?.ok) log(result.message || "Workspace preference could not be saved", true);
}

async function setDiagnosticWorkflowScope(scope = currentWorkflowScope()) {
  if (typeof window.pywebview?.api?.set_diagnostic_context !== "function") return;
  const workflowScope = normalizeWorkflowScope(scope);
  const result = await window.pywebview.api.set_diagnostic_context({ workflowScope });
  if (!result?.ok) log(result.message || "Diagnostic workflow context could not be updated", true);
}

async function preflightWorkflowRuntime(scope = currentWorkflowScope()) {
  if (typeof window.pywebview?.api?.preflight_runtime_switch !== "function") {
    return { ok: true, data: { status: "ready", packageAction: "none", targetRuntime: normalizeWorkflowScope(scope), savedCars: { plan: {} } } };
  }
  const root = $("rootPath")?.value.trim() || state.root || "";
  return window.pywebview.api.preflight_runtime_switch({
    targetRuntime: normalizeWorkflowScope(scope),
    root,
  });
}

async function prepareWorkflowRuntime(scope = currentWorkflowScope()) {
  const nextScope = normalizeWorkflowScope(scope);
  if (!await ensureLongPathsSetupReady()) {
    return { ready: false, blocked: true, setupRequired: true, changed: false, reason: "long_paths_required" };
  }
  const root = normalizeRootPath($("rootPath")?.value.trim() || $("homeRootPath")?.value.trim() || state.root || "");
  if (!root) {
    if (nextScope === "official") {
      state.officialRuntimePreparedRoot = "";
      state.officialRuntimeSafetyStatus = null;
      renderOfficialRuntimeStatus();
    }
    return { ready: false, setupRequired: true, changed: false, reason: "game_root_missing" };
  }
  if (state.runtimeSwitchBusy) return false;
  state.runtimeSwitchBusy = true;
  renderOfficialRuntimeStatus();
  try {
    let preflightResult = await preflightWorkflowRuntime(nextScope);
    let packageConflictRechecks = 0;
    while (true) {
    if (!preflightResult?.ok) {
      showResult(preflightResult || { ok: false, error: "Runtime preflight failed" }, "Runtime switch preflight failed", "preflight_runtime_switch");
      return { ready: false, blocked: true };
    }
    const preflight = preflightResult.data || {};
    if (nextScope === "official") {
      await loadOfficialRuntimeSafetyStatus(root);
      preflight.officialSafetyCleanupNeeded = officialSafetyCleanupNeeded();
    }
    state.runtimeSwitchPreflight = preflight;
    const blockers = Array.isArray(preflight.blockers) ? preflight.blockers : [];
    if (blockers.length || preflight.status === "blocked") {
      if (preflight?.packageUpdateConflict?.manualResolutionSupported) {
        const action = await guideContentPackageUpdateConflict(nextScope, preflight, root, packageConflictRechecks);
        if (action === "recheck") {
          packageConflictRechecks += 1;
          preflightResult = await preflightWorkflowRuntime(nextScope);
          continue;
        }
        return { ready: false, blocked: true, cancelled: action === "cancel", reason: "content_package_update_conflict" };
      }
      showStatusModal({
        type: "error",
        title: `${runtimeSwitchTargetLabel(nextScope)} runtime blocked`,
        subtitle: "Open Setup to resolve the runtime state",
        error: blockers[0] || "Runtime switch preflight is blocked.",
        details: runtimeSwitchDetails(preflight),
      });
      return { ready: false, blocked: true };
    }
    if (preflight.requiresAdvancedContentSetup || preflight.status === "setup_required") {
      log(preflight?.advancedContentSetup?.reason || "Advanced full-content extraction is required before runtime switching.");
      return { ready: false, setupRequired: true, changed: false };
    }
    const runtimeChanges = runtimeSwitchHasChanges(preflight);
    const safetyCleanup = nextScope === "official" && preflight.officialSafetyCleanupNeeded === true;
    if (!runtimeChanges && !safetyCleanup) {
      if (nextScope === "official") state.officialRuntimePreparedRoot = root;
      const refresh = nextScope === "advanced"
        ? await refreshActiveAdvancedLiveriesAfterExtraction(preflight)
        : null;
      const advancedSafety = nextScope === "advanced"
        ? await ensureAdvancedRuntimeSafetyReady(root)
        : null;
      if (nextScope === "advanced" && !advancedSafety?.ok) {
        return { ready: false, blocked: true, changed: false, refresh, advancedSafety };
      }
      renderOfficialRuntimeStatus();
      applySetupGateStatus();
      return { ready: true, changed: false, refresh, advancedSafety };
    }
    if (runtimeChanges && typeof window.pywebview?.api?.apply_runtime_switch !== "function") {
      showStatusModal({
        type: "error",
        title: `${runtimeSwitchTargetLabel(nextScope)} runtime unavailable`,
        subtitle: "Rebuild required",
        error: "This build does not expose the runtime switch apply API yet.",
        details: runtimeSwitchDetails(preflight),
      });
      return { ready: false, blocked: true };
    }
    if (safetyCleanup && typeof window.pywebview?.api?.remove_liverylab_safety_hook !== "function") {
      showStatusModal({
        type: "error",
        title: "Official runtime unavailable",
        subtitle: "Safety cleanup API missing",
        error: "This runtime cannot remove the Advanced LiveryLab Safety hook before entering Official.",
        details: runtimeSwitchDetails(preflight),
      });
      return { ready: false, blocked: true };
    }
    const confirmed = await showConfirmModal({
      type: "warning",
      title: `Prepare ${runtimeSwitchTargetLabel(nextScope)} Runtime`,
      subtitle: "Close AC EVO before continuing",
      message: `${runtimeSwitchConfirmText(nextScope)} SavedCars are moved, not deleted, and can be restored when switching back.`,
      details: runtimeSwitchDetails(preflight),
      confirmLabel: `Prepare ${runtimeSwitchTargetLabel(nextScope)} Runtime`,
      cancelLabel: "Cancel",
      loadingLabel: "Preparing runtime...",
      onConfirm: async () => {
        let runtimeResult = { ok: true, message: "Official runtime package state already ready", data: { after: preflight } };
        if (runtimeChanges) {
          runtimeResult = await window.pywebview.api.apply_runtime_switch({
            targetRuntime: nextScope,
            root,
            confirmToken: "APPLY_RUNTIME_SWITCH",
          });
          if (!runtimeResult?.ok) return runtimeResult;
        }
        let safetyResult = null;
        if (safetyCleanup) {
          safetyResult = await window.pywebview.api.remove_liverylab_safety_hook({ root });
          if (!safetyResult?.ok) return safetyResult;
        }
        return {
          ok: true,
          message: [runtimeResult?.message, safetyResult?.message].filter(Boolean).join("\n"),
          data: {
            after: runtimeResult?.data?.after || preflight,
            runtime: runtimeResult?.data || {},
            safetyCleanup: safetyResult?.data || null,
          },
        };
      },
    });
    if (!confirmed) return { ready: false, cancelled: true };
    if (!confirmed.ok) {
      showResult(confirmed, "Runtime switch failed", "apply_runtime_switch");
      return { ready: false, blocked: true };
    }
    state.runtimeSwitchPreflight = confirmed.data?.after || null;
    if (nextScope === "official") {
      state.officialRuntimePreparedRoot = root;
      state.officialRuntimeSafetyStatus = confirmed.data?.safetyCleanup?.after || state.officialRuntimeSafetyStatus;
      if (safetyCleanup && !confirmed.data?.safetyCleanup?.after) {
        state.officialRuntimeSafetyStatus = null;
      }
      const packageAfter = state.runtimeSwitchPreflight?.package;
      if (packageAfter && typeof packageAfter === "object") state.contentPackageStatus = packageAfter;
      await loadOfficialRuntimeSafetyStatus(root);
      renderOfficialRuntimeStatus();
      applySetupGateStatus();
    }
    const refresh = nextScope === "advanced"
      ? await refreshActiveAdvancedLiveriesAfterExtraction(state.runtimeSwitchPreflight || preflight)
      : null;
    const advancedSafety = nextScope === "advanced"
      ? await ensureAdvancedRuntimeSafetyReady(root)
      : null;
    if (nextScope === "advanced" && !advancedSafety?.ok) {
      return { ready: false, blocked: true, changed: true, refresh, advancedSafety };
    }
    showResult(confirmed, `${runtimeSwitchTargetLabel(nextScope)} runtime prepared`, "apply_runtime_switch");
    return { ready: true, changed: true, refresh, advancedSafety };
    }
  } finally {
    state.runtimeSwitchBusy = false;
    renderOfficialRuntimeStatus();
  }
}

async function chooseWorkflow(scope = "advanced") {
  if (state.workflowChoiceBusy) return;
  const nextScope = normalizeWorkflowScope(scope);
  const previousScope = currentWorkflowScope();
  setWorkflowChoiceBusy(nextScope, true);
  try {
    await setDiagnosticWorkflowScope(nextScope);
    const prepared = await prepareWorkflowRuntime(nextScope);
    if (!prepared?.ready && prepared?.cancelled) {
      await setDiagnosticWorkflowScope(previousScope);
      return;
    }
    await saveWorkflowScopePreference(nextScope);
    if (typeof window.LiveryLabLegacyMigration?.handleWorkflowSelected === "function") {
      await window.LiveryLabLegacyMigration.handleWorkflowSelected(nextScope);
    }
    enterWorkflowChoice(nextScope, { forceSetup: Boolean(prepared?.blocked || prepared?.setupRequired) });
  } catch (error) {
    log(`Workflow selection failed: ${error?.message || String(error)}`, true);
    showResult(
      { ok: false, error: error?.message || String(error) },
      "Workflow could not be opened",
      "choose_workflow",
    );
  } finally {
    setWorkflowChoiceBusy(nextScope, false);
  }
}

async function reconcileAdvancedRuntimeOnStartup(root = "") {
  const normalized = normalizeRootPath(root || $("rootPath")?.value.trim() || state.root || "");
  if (!normalized || typeof window.pywebview?.api?.startup_multiplayer_safety_reconcile !== "function") return;
  const safetyStartup = await window.pywebview.api.startup_multiplayer_safety_reconcile({ root: normalized });
  if (safetyStartup?.ok) {
    const safetyData = safetyStartup.data || {};
    if (safetyData.safetyStatus) {
      state.multiplayerSafetyStatus = safetyData.safetyStatus;
      state.liveryLabSafetyBypassed = false;
      renderMultiplayerSafetyStatus(state.multiplayerSafetyStatus);
      syncHomePackageStatus();
      applySetupGateStatus();
    }
    if (["repaired", "refreshed", "warning"].includes(String(safetyData.state || ""))) {
      log(safetyStartup.message || "LiveryLab Safety startup check completed");
    }
  } else if (safetyStartup?.message) {
    log(safetyStartup.message, true);
  }
}

async function ensureAdvancedRuntimeSafetyReady(root = "") {
  const normalized = normalizeRootPath(root || $("rootPath")?.value.trim() || state.root || "");
  if (!normalized || typeof window.pywebview?.api?.startup_multiplayer_safety_reconcile !== "function") {
    const result = {
      ok: false,
      error: "This build cannot verify required Advanced LiveryLab Safety before opening the workspace.",
    };
    showResult(result, "Advanced runtime Safety blocked", "startup_multiplayer_safety_reconcile");
    return result;
  }
  const result = await window.pywebview.api.startup_multiplayer_safety_reconcile({ root: normalized });
  if (!result?.ok) {
    showResult(result || { ok: false, error: "Advanced LiveryLab Safety could not be verified." }, "Advanced runtime Safety blocked", "startup_multiplayer_safety_reconcile");
    return result || { ok: false };
  }
  const safetyData = result.data || {};
  const safetyState = String(safetyData.state || "").trim().toLowerCase();
  const safetyReason = String(safetyData.reason || "").trim();
  const activeCount = Number(safetyData.activeSkinCount || 0);
  const intentionallySkipped = safetyState === "skipped"
    && activeCount === 0
    && safetyReason === "no active My Liveries";
  const verified = ["current", "repaired", "refreshed"].includes(safetyState)
    && activeCount > 0
    && safetyData.installed === true
    && safetyData.needsRepair !== true;
  if (!intentionallySkipped && !verified) {
    const blocked = {
      ok: false,
      error: `Advanced LiveryLab Safety could not be verified: ${safetyData.error || safetyReason || safetyData.label || safetyState || "unknown Safety state"}`,
      data: safetyData,
    };
    showResult(blocked, "Advanced runtime Safety blocked", "startup_multiplayer_safety_reconcile");
    return blocked;
  }
  if (safetyData.safetyStatus) {
    state.multiplayerSafetyStatus = safetyData.safetyStatus;
    state.liveryLabSafetyBypassed = false;
    renderMultiplayerSafetyStatus(state.multiplayerSafetyStatus);
    syncHomePackageStatus();
    applySetupGateStatus();
  }
  return result;
}

async function runWorkflowStartupTasks() {
  const scope = currentWorkflowScope();
  const root = $("rootPath")?.value.trim() || state.root || "";
  if (scope === "official") {
    if (root) {
      setSplashProgress("Checking AC EVO root...", 18);
      await updateRootStatus(root);
    }
    setSplashProgress("Checking Windows setup...", 32);
    await refreshStorageHealth({ silent: true, root });
    setSplashProgress("Checking official preview assets...", 50);
    await refreshOfficialCarResourcesStatus({ silent: true, root });
    setSplashProgress("Scanning official liveries...", 76);
    await refreshOfficialExternalLiveries({ silent: true });
    setSplashProgress("Opening official workspace...", 94);
    return;
  }
  if (scope !== "advanced") return;
  setSplashProgress("Loading 3D preview project...", 16);
  await loadPreview3dProject();
  setSplashProgress("Checking Windows setup...", 24);
  await refreshStorageHealth({ silent: true, root });
  if (!root) return;
  setSplashProgress("Checking AC EVO root...", 34);
  await updateRootStatus(root);
  setSplashProgress("Checking runtime profile...", 46);
  await refreshRuntimeProfile({ silent: true, root });
  setSplashProgress("Checking Advanced LiveryLab Safety...", 62);
  await reconcileAdvancedRuntimeOnStartup(root);
  setSplashProgress("Loading livery registry...", 74);
  await ensureMySkinsForBrowserMarkers();
  setSplashProgress("Loading car compatibility...", 78);
  await loadCachedCarCompatibility();
  setSplashProgress("Preparing file browser...", 88);
  scheduleInitialBrowserReloads();
  await ensureInitialBrowserLoaded();
  setSplashProgress("Opening advanced workspace...", 96);
}

function enterWorkflowChoice(scope = "advanced", options = {}) {
  const nextScope = normalizeWorkflowScope(scope);
  state.workflowChoiceComplete = true;
  state.workflowScope = nextScope;
  state.mySkinSelectedKey = "";
  state.liveryCategorySearch = "";
  state.mySkinsLoaded = false;
  state.mySkinsLoadedScope = "";
  state.mySkins = [];
  clearMySkinThumbnailCache();
  applyWorkflowScopeUi();
  hideWorkflowChoiceScreen();
  state.workflowStartupBusy = true;
  showSplashLoading(nextScope === "official" ? "Loading official workspace..." : "Loading advanced workspace...", 8);
  runWorkflowStartupTasks()
    .then(() => {
      if (currentWorkflowScope() !== nextScope) return;
      if (options.forceSetup) {
        applySetupGateStatus();
        setTab("home");
      } else {
        applyStartupLandingTab();
      }
    })
    .catch((error) => {
      log(`Workflow startup failed: ${error?.message || String(error)}`, true);
    })
    .finally(() => {
      state.workflowStartupBusy = false;
      hideSplashOverlay();
      if (typeof window.LiveryLabLegacyMigration?.handleSetupGate === "function") {
        Promise.resolve(window.LiveryLabLegacyMigration.handleSetupGate(currentWorkflowScope(), state.setupGate?.ready === true)).catch(() => {});
      }
      queueStartupPreload();
    });
}

function queueStartupPreload() {
  if (state.startupPreloadStarted) return;
  state.startupPreloadStarted = true;
  const run = () => {
    preloadStartupCaches().catch((error) => {
      console.debug("Startup cache preload skipped", error);
    });
  };
  if (typeof window.requestIdleCallback === "function") {
    window.requestIdleCallback(run, { timeout: 1500 });
  } else {
    window.setTimeout(run, 350);
  }
}

async function preloadStartupCaches() {
  const root = $("rootPath")?.value.trim() || "";
  const scope = currentWorkflowScope();
  const tasks = [
    loadNumberplateBuiltinCalibrations(),
    loadLiveryPreviewSceneSettings(),
    loadNumberplateStudioTemplatesForPreview(),
  ];
  if (root && scope === "advanced") {
    tasks.push(loadCustomTyreBrands());
  }
  await Promise.allSettled(tasks);
  state.startupPreloadDone = true;
}

function apiReady() {
  return new Promise((resolve) => {
    if (window.pywebview?.api) {
      resolve();
      return;
    }
    window.addEventListener("pywebviewready", resolve, { once: true });
  });
}

function log(message, isError = false) {
  const out = $("logOutput");
  const stamp = new Date().toLocaleTimeString();
  out.textContent += `[${stamp}] ${isError ? "ERROR: " : ""}${message}\n`;
  out.scrollTop = out.scrollHeight;
}

function writeFileInfoToLog(text) {
  const out = $("logOutput");
  if (!out) return;
  const body = String(text ?? "").trim();
  if (!body) return;
  const stamp = new Date().toLocaleTimeString();
  out.textContent += `\n[${stamp}] ${t("file_info")}\n${body}\n`;
  out.scrollTop = out.scrollHeight;
}

function devModeEnabled() {
  return Boolean(state.debugMode);
}

function normalizeNumberplateSupport(source = {}) {
  const support = source?.numberplate_support && typeof source.numberplate_support === "object"
    ? source.numberplate_support
    : (source?.numberplateSupport && typeof source.numberplateSupport === "object" ? source.numberplateSupport : {});
  return {
    supported: support.supported !== false,
    known: support.known === true,
    message: String(support.message || support.reason || "This car does not support numberplates yet").trim(),
    reason: String(support.reason || support.message || "").trim(),
    surfaces: Array.isArray(support.surfaces) ? support.surfaces : [],
    source: String(support.source || support.sourceActor || "").trim(),
  };
}

function numberplatesBlockedForNormalMode(source = {}) {
  return normalizeNumberplateSupport(source).supported === false;
}

function numberplateVariantSource(variant = {}) {
  const source = String(variant?.source_type || variant?.numberplate_source || "").trim().toLowerCase();
  if (source === "none" || variant?.numberplate_hidden === true) return "none";
  if (variant?.baked_template || variant?.template_id || source === "template") return "template";
  return "acevo";
}

function roadPlateInfo(source = {}) {
  return source?.road_plate && typeof source.road_plate === "object"
    ? source.road_plate
    : (source?.roadPlate && typeof source.roadPlate === "object" ? source.roadPlate : {});
}

function roadPlateSupported(source = {}) {
  const plate = roadPlateInfo(source);
  return plate.supported === true && String(plate.kind || "").toLowerCase() === "road_rear";
}

function roadPlateSource(source = {}) {
  const plate = roadPlateInfo(source);
  const value = String(plate.source || plate.selection || "").trim().toLowerCase();
  return value === "custom" ? "custom" : "default";
}

function numberplateVariantHidden(source = {}) {
  const variant = source?.numberplate_variant && typeof source.numberplate_variant === "object"
    ? source.numberplate_variant
    : (source?.numberplateVariant && typeof source.numberplateVariant === "object" ? source.numberplateVariant : source);
  return numberplateVariantSource(variant) === "none";
}

function firstMessageLine(value, fallback = "Action failed") {
  const firstLine = String(value || "").split(/\r?\n/).map((line) => line.trim()).find(Boolean);
  return firstLine || fallback;
}

function technicalDetailsFromResult(result) {
  if (!devModeEnabled()) return "";
  return [
    result?.exceptionType ? `Exception: ${result.exceptionType}` : "",
    result?.errorDetail || "",
    result?.traceback || "",
  ].filter(Boolean).join("\n\n").trim();
}

function displayDetails(userDetails = "", technicalDetails = "") {
  const lines = [String(userDetails || "").trim()].filter(Boolean);
  if (technicalDetails && devModeEnabled()) {
    lines.push(["Technical details:", technicalDetails].join("\n"));
  }
  return lines.join("\n\n");
}

function modalSummaryFromResult(result) {
  if (result?.ok) {
    return String(result?.message || "").trim() || "Action completed successfully.";
  }
  return firstMessageLine(result?.message, "Action failed");
}

function modalMessageFromResult(name, result) {
  const message = modalSummaryFromResult(result);
  return message;
}

function packageImportErrorSummary(message = "", callName = "") {
  if (!PACKAGE_IMPORT_ERROR_CALLS.has(callName)) return "";
  const text = String(message || "").trim();
  if (!text) return "";
  if (/manifest.*(?:missing|not found|invalid|could not be read)|selected zip is not|unsupported .*version/i.test(text)) {
    return "This does not look like a valid LiveryLab package.";
  }
  if (/package (?:is )?incomplete|package file.*missing|missing required file/i.test(text)) {
    return "This package is incomplete.";
  }
  if (
    /blocked (?:dangerous|disguised)|unsafe .*zip|unsafe .*package|unsafe .*target|path traversal|absolute .*path|null byte|duplicate zip member|nested archive|encrypted zip|symlink|special file|unsupported .*top-level|unsupported zip file type|compression ratio|too many files|too large|outside allowed liverylab roots/i.test(text)
  ) {
    return "Import blocked for safety reasons.";
  }
  return "";
}

function packageImportErrorDetails(message = "") {
  const text = String(message || "").trim();
  if (!text) return "";
  return `Original error:\n${text}`;
}

function errorDetailsFromResult(result) {
  return [
    String(result?.errorDetails || "").trim(),
    result?.errorId ? `Error reference: ${result.errorId}` : "",
  ].filter(Boolean).join("\n\n");
}

function displayErrorFromResult(name, result, fallback = "Unknown error") {
  const original = firstMessageLine(result?.message, fallback);
  const summary = packageImportErrorSummary(original, name);
  const userDetails = errorDetailsFromResult(result);
  const fullOriginal = [original, userDetails].filter(Boolean).join("\n");
  return {
    summary: summary || original,
    original,
    details: summary ? packageImportErrorDetails(fullOriginal) : userDetails,
  };
}

window.LiveryLabFormatImportError = (message = "", callName = "") => displayErrorFromResult(
  callName,
  { ok: false, message },
  "Package import failed.",
);

function modalTypeLabel(type, fallback = "Info") {
  return {
    danger: "Danger",
    error: "Error",
    info: "Info",
    success: "Success",
    warning: "Warning",
  }[String(type || "").toLowerCase()] || fallback;
}

function setStatusModalBlocked(blocked = false) {
  const modal = $("statusModal");
  const closeButton = $("statusModalClose");
  const closeX = $("statusModalCloseX");
  const isBlocked = Boolean(blocked);
  state.statusModalBlocked = isBlocked;
  if (modal) modal.dataset.blocked = isBlocked ? "true" : "false";
  if (closeButton) {
    closeButton.disabled = isBlocked;
    if (isBlocked) {
      closeButton.innerHTML = `<span class="button-spinner" aria-hidden="true"></span><span>Working...</span>`;
    } else {
      closeButton.textContent = "Close";
    }
  }
  if (closeX) {
    closeX.hidden = isBlocked;
    closeX.disabled = isBlocked;
  }
}

function showStatusModal({ type = "info", title = "Status", subtitle = "", message = "", details = "", technicalDetails = "", error = "", messageHtml = "", openFolderPath = "", blockClose = false, modalClass = "", detailsOpen = false }) {
  const modal = $("statusModal");
  if (!modal) return;
  const badge = $("statusModalBadge");
  const titleNode = $("statusModalTitle");
  const subtitleNode = $("statusModalSubtitle");
  const errorNode = $("statusModalError");
  const messageNode = $("statusModalMessage");
  const detailsWrap = $("statusModalDetailsWrap");
  const detailsNode = $("statusModalDetails");
  const openFolderButton = $("statusModalOpenFolder");
  modal.hidden = false;
  modal.dataset.type = type;
  if (modalClass) {
    modal.dataset.modalClass = modalClass;
  } else {
    delete modal.dataset.modalClass;
  }
  state.statusModalOpen = true;
  state.statusModalOpenFolderPath = type === "success" ? String(openFolderPath || "").trim() : "";
  setStatusModalBlocked(blockClose);
  if (openFolderButton) {
    openFolderButton.hidden = !state.statusModalOpenFolderPath;
    openFolderButton.textContent = "Open Folder";
  }
  if (badge) badge.textContent = modalTypeLabel(type, "Info");
  if (titleNode) titleNode.textContent = title;
  if (subtitleNode) subtitleNode.textContent = subtitle || "";
  if (errorNode) {
    const errorText = String(error || "").trim();
    errorNode.hidden = !errorText;
    errorNode.textContent = errorText;
  }
  if (messageNode) {
    if (messageHtml) {
      messageNode.innerHTML = messageHtml;
    } else {
      messageNode.textContent = message || "";
    }
  }
  if (detailsWrap && detailsNode) {
    const visibleDetails = displayDetails(details, technicalDetails);
    const hasDetails = Boolean(visibleDetails);
    detailsWrap.hidden = !hasDetails;
    detailsWrap.open = Boolean(hasDetails && detailsOpen);
    detailsNode.textContent = hasDetails ? visibleDetails : "";
  }
}
window.showStatusModal = showStatusModal;

function closeStatusModal({ force = false } = {}) {
  if (state.statusModalBlocked && !force) return;
  const modal = $("statusModal");
  if (!modal) return;
  modal.hidden = true;
  state.statusModalOpen = false;
  state.statusModalOpenFolderPath = "";
  setStatusModalBlocked(false);
}

function isNumberplateGeneratorInfoDismissed() {
  return state.numberplateGeneratorNoticeDismissedNotice === NUMBERPLATE_GENERATOR_NOTICE_ID;
}

async function setNumberplateGeneratorInfoDismissed() {
  state.numberplateGeneratorNoticeDismissedNotice = NUMBERPLATE_GENERATOR_NOTICE_ID;
  if (typeof window.pywebview?.api?.save_numberplate_generator_notice !== "function") return;
  try {
    const result = await window.pywebview.api.save_numberplate_generator_notice(NUMBERPLATE_GENERATOR_NOTICE_ID);
    if (!result?.ok) {
      console.debug("Numberplate notice preference was not persisted", result?.message || result);
    }
  } catch (error) {
    console.debug("Numberplate notice preference was not persisted", error);
  }
}

function showNumberplateGeneratorInfoModalWhenNeeded() {
  const modal = $("numberplateGeneratorInfoModal");
  if (!modal || isNumberplateGeneratorInfoDismissed()) return;
  modal.hidden = false;
}

async function closeNumberplateGeneratorInfoModal() {
  const modal = $("numberplateGeneratorInfoModal");
  if (!modal) return;
  if ($("numberplateGeneratorInfoDoNotShow")?.checked) {
    await setNumberplateGeneratorInfoDismissed();
  }
  modal.hidden = true;
}

function isCustomGridBridgeGuideDismissed() {
  return state.customGridBridgeGuideDismissedNotice === CUSTOM_GRID_BRIDGE_GUIDE_NOTICE_ID;
}

async function setCustomGridBridgeGuideDismissed() {
  state.customGridBridgeGuideDismissedNotice = CUSTOM_GRID_BRIDGE_GUIDE_NOTICE_ID;
  if (typeof window.pywebview?.api?.save_custom_grid_bridge_guide_notice !== "function") return;
  try {
    const result = await window.pywebview.api.save_custom_grid_bridge_guide_notice(CUSTOM_GRID_BRIDGE_GUIDE_NOTICE_ID);
    if (!result?.ok) {
      console.debug("Custom Grid guide preference was not persisted", result?.message || result);
    }
  } catch (error) {
    console.debug("Custom Grid guide preference was not persisted", error);
  }
}

function isLayerStudioInfoDismissed() {
  return state.layerStudioInfoDismissedNotice === LAYER_STUDIO_INFO_NOTICE_ID;
}

async function setLayerStudioInfoDismissed() {
  state.layerStudioInfoDismissedNotice = LAYER_STUDIO_INFO_NOTICE_ID;
  if (typeof window.pywebview?.api?.save_layer_studio_info_notice !== "function") return;
  try {
    const result = await window.pywebview.api.save_layer_studio_info_notice(LAYER_STUDIO_INFO_NOTICE_ID);
    if (!result?.ok) {
      console.debug("Layer Studio notice preference was not persisted", result?.message || result);
    }
  } catch (error) {
    console.debug("Layer Studio notice preference was not persisted", error);
  }
}

function showLayerStudioInfoModalWhenNeeded() {
  const modal = $("layerStudioInfoModal");
  if (!modal || isLayerStudioInfoDismissed()) return false;
  const checkbox = $("layerStudioInfoDoNotShow");
  if (checkbox) checkbox.checked = false;
  modal.hidden = false;
  return true;
}

async function closeLayerStudioInfoModal() {
  const modal = $("layerStudioInfoModal");
  if (!modal) return;
  if ($("layerStudioInfoDoNotShow")?.checked) {
    await setLayerStudioInfoDismissed();
  }
  modal.hidden = true;
}

function isLiveryPreviewInfoDismissed() {
  return state.liveryPreviewInfoDismissedNotice === LIVERY_PREVIEW_INFO_NOTICE_ID;
}

async function setLiveryPreviewInfoDismissed() {
  state.liveryPreviewInfoDismissedNotice = LIVERY_PREVIEW_INFO_NOTICE_ID;
  if (typeof window.pywebview?.api?.save_livery_preview_info_notice !== "function") return;
  try {
    const result = await window.pywebview.api.save_livery_preview_info_notice(LIVERY_PREVIEW_INFO_NOTICE_ID);
    if (!result?.ok) {
      console.debug("3D Livery Preview notice preference was not persisted", result?.message || result);
    }
  } catch (error) {
    console.debug("3D Livery Preview notice preference was not persisted", error);
  }
}

function showLiveryPreviewInfoModalWhenNeeded() {
  const modal = $("liveryPreviewInfoModal");
  if (!modal || isLiveryPreviewInfoDismissed()) return false;
  const textureName = $("liveryPreviewInfoTextureName");
  if (textureName) {
    textureName.textContent = normalizeWorkflowScope(state.workflowScope) === "official"
      ? "EXT_Skin_C.png"
      : "ext_skin_c.png";
  }
  const checkbox = $("liveryPreviewInfoDoNotShow");
  if (checkbox) checkbox.checked = false;
  modal.hidden = false;
  return true;
}

async function closeLiveryPreviewInfoModal() {
  const modal = $("liveryPreviewInfoModal");
  if (!modal) return;
  if ($("liveryPreviewInfoDoNotShow")?.checked) {
    await setLiveryPreviewInfoDismissed();
  }
  modal.hidden = true;
}

function isDriverProfilesInfoDismissed() {
  return state.driverProfilesInfoDismissedNotice === DRIVER_PROFILES_INFO_NOTICE_ID
    || window.localStorage?.getItem("liverylab.driverProfilesInfoDismissedNotice") === DRIVER_PROFILES_INFO_NOTICE_ID;
}

async function setDriverProfilesInfoDismissed() {
  state.driverProfilesInfoDismissedNotice = DRIVER_PROFILES_INFO_NOTICE_ID;
  window.localStorage?.setItem("liverylab.driverProfilesInfoDismissedNotice", DRIVER_PROFILES_INFO_NOTICE_ID);
  if (typeof window.pywebview?.api?.save_driver_profiles_info_notice !== "function") return;
  try {
    const result = await window.pywebview.api.save_driver_profiles_info_notice(DRIVER_PROFILES_INFO_NOTICE_ID);
    if (!result?.ok) {
      console.debug("Driver Profiles notice preference was not persisted", result?.message || result);
    }
  } catch (error) {
    console.debug("Driver Profiles notice preference was not persisted", error);
  }
}

function showDriverProfilesInfoModalWhenNeeded() {
  const modal = $("driverProfilesInfoModal");
  if (!modal || isDriverProfilesInfoDismissed()) return false;
  const checkbox = $("driverProfilesInfoDoNotShow");
  if (checkbox) checkbox.checked = false;
  modal.hidden = false;
  return true;
}

async function closeDriverProfilesInfoModal() {
  const modal = $("driverProfilesInfoModal");
  if (!modal) return;
  if ($("driverProfilesInfoDoNotShow")?.checked) {
    await setDriverProfilesInfoDismissed();
  }
  modal.hidden = true;
}

window.LiveryLabUiNotices = {
  isCustomGridBridgeGuideDismissed,
  setCustomGridBridgeGuideDismissed,
};

function setConfirmModalBusy(busy, label = "") {
  const modal = $("confirmModal");
  const confirm = $("confirmModalConfirm");
  const cancel = $("confirmModalCancel");
  const alternate = $("confirmModalAlternate");
  const closeX = $("confirmModalCloseX");
  state.confirmModalBusy = Boolean(busy);
  if (modal) modal.dataset.busy = state.confirmModalBusy ? "true" : "false";
  if (confirm) {
    confirm.disabled = state.confirmModalBusy;
    if (state.confirmModalBusy) {
      confirm.innerHTML = `<span class="button-spinner" aria-hidden="true"></span><span>${escapeHtml(label || state.confirmModalLoadingLabel || "Working...")}</span>`;
    } else {
      confirm.textContent = state.confirmModalConfirmLabel || "Confirm";
    }
  }
  if (cancel) cancel.disabled = state.confirmModalBusy;
  if (alternate) alternate.disabled = state.confirmModalBusy;
  if (closeX) closeX.disabled = state.confirmModalBusy;
}

function resetConfirmModalState() {
  state.confirmModalBusy = false;
  state.confirmModalOnConfirm = null;
  state.confirmModalLoadingLabel = "Working...";
  const modal = $("confirmModal");
  const optionWrap = $("confirmModalOptionWrap");
  const optionCheckbox = $("confirmModalOptionCheckbox");
  const optionMeta = $("confirmModalOptionMeta");
  if (modal) {
    delete modal.dataset.actionLayout;
    delete modal.dataset.modalClass;
  }
  if (optionWrap) optionWrap.hidden = true;
  if (optionCheckbox) {
    optionCheckbox.checked = false;
    optionCheckbox.title = "";
  }
  if (optionMeta) optionMeta.textContent = "";
  $("confirmModalCancel")?.removeAttribute("hidden");
  $("confirmModalAlternate")?.classList.remove("primary");
  setConfirmModalBusy(false);
}

function closeConfirmModal(value = null) {
  if (state.confirmModalBusy) return;
  if (value === true && typeof state.confirmModalOnConfirm === "function") {
    runConfirmModalAction();
    return;
  }
  const optionChecked = Boolean($("confirmModalOptionCheckbox")?.checked && !$("confirmModalOptionWrap")?.hidden);
  const modal = $("confirmModal");
  if (modal) modal.hidden = true;
  const resolver = state.confirmModalResolver;
  state.confirmModalResolver = null;
  resetConfirmModalState();
  if (resolver) resolver(value, { optionChecked });
}

async function runConfirmModalAction() {
  const action = state.confirmModalOnConfirm;
  const resolver = state.confirmModalResolver;
  if (typeof action !== "function" || !resolver) {
    closeConfirmModal(true);
    return;
  }
  setConfirmModalBusy(true, state.confirmModalLoadingLabel);
  try {
    const result = await action();
    const modal = $("confirmModal");
    if (modal) modal.hidden = true;
    state.confirmModalResolver = null;
    resetConfirmModalState();
    resolver(result ?? true);
  } catch (error) {
    const modal = $("confirmModal");
    if (modal) modal.hidden = true;
    state.confirmModalResolver = null;
    resetConfirmModalState();
    resolver({ ok: false, message: error?.message || String(error), error });
  }
}

function showConfirmModal({
  type = "info",
  title = "Confirm action",
  subtitle = "",
  message = "",
  details = "",
  confirmLabel = "Confirm",
  cancelLabel = "Cancel",
  loadingLabel = "",
  actionLayout = "",
  option = null,
  resolveOptionState = false,
  onConfirm = null,
} = {}) {
  const modal = $("confirmModal");
  if (!modal) return Promise.resolve(false);
  state.confirmModalMode = "confirm";
  state.confirmModalConfirmLabel = confirmLabel;
  state.confirmModalLoadingLabel = loadingLabel || `${confirmLabel}...`;
  state.confirmModalOnConfirm = typeof onConfirm === "function" ? onConfirm : null;
  modal.hidden = false;
  modal.dataset.type = type;
  modal.dataset.mode = "confirm";
  if (actionLayout) {
    modal.dataset.actionLayout = actionLayout;
  } else {
    delete modal.dataset.actionLayout;
  }
  $("confirmModalBadge").textContent = modalTypeLabel(type, "Confirm");
  $("confirmModalTitle").textContent = title;
  $("confirmModalSubtitle").textContent = subtitle;
  $("confirmModalMessage").textContent = message;
  $("confirmModalConfirm").textContent = confirmLabel;
  $("confirmModalCancel").textContent = cancelLabel;
  $("confirmModalAlternate").hidden = true;
  $("confirmModalInputWrap").hidden = true;
  const optionWrap = $("confirmModalOptionWrap");
  const optionCheckbox = $("confirmModalOptionCheckbox");
  const optionLabel = $("confirmModalOptionLabel");
  const optionMeta = $("confirmModalOptionMeta");
  if (optionWrap && optionCheckbox && optionLabel && optionMeta) {
    const hasOption = Boolean(option && String(option.label || "").trim());
    optionWrap.hidden = !hasOption;
    optionCheckbox.checked = Boolean(option?.checked);
    optionCheckbox.title = String(option?.title || "");
    optionLabel.textContent = hasOption ? String(option.label || "") : "";
    optionMeta.textContent = hasOption ? String(option.meta || "") : "";
    optionWrap.title = String(option?.title || "");
  }
  setConfirmModalBusy(false);
  const detailNode = $("confirmModalDetails");
  const hasDetails = Boolean(String(details || "").trim());
  detailNode.hidden = !hasDetails;
  detailNode.textContent = hasDetails ? String(details).trim() : "";
  const resolveActionResult = typeof onConfirm === "function";
  return new Promise((resolve) => {
    state.confirmModalResolver = (value, meta = {}) => {
      if (resolveActionResult) {
        resolve(value);
        return;
      }
      if (resolveOptionState) {
        resolve({
          confirmed: Boolean(value),
          optionChecked: Boolean(value && meta.optionChecked),
          value,
        });
        return;
      }
      resolve(Boolean(value));
    };
  });
}
window.showConfirmModal = showConfirmModal;

function showChoiceModal({
  type = "warning",
  title = "Choose action",
  subtitle = "",
  message = "",
  details = "",
  confirmLabel = "Save",
  alternateLabel = "Discard",
  cancelLabel = "Cancel",
} = {}) {
  const modal = $("confirmModal");
  if (!modal) return Promise.resolve("cancel");
  state.confirmModalMode = "choice";
  state.confirmModalOnConfirm = null;
  state.confirmModalConfirmLabel = confirmLabel;
  modal.hidden = false;
  modal.dataset.type = type;
  modal.dataset.mode = "choice";
  delete modal.dataset.actionLayout;
  $("confirmModalBadge").textContent = modalTypeLabel(type, "Choose");
  $("confirmModalTitle").textContent = title;
  $("confirmModalSubtitle").textContent = subtitle;
  $("confirmModalMessage").textContent = message;
  $("confirmModalConfirm").textContent = confirmLabel;
  $("confirmModalCancel").textContent = cancelLabel;
  $("confirmModalInputWrap").hidden = true;
  $("confirmModalOptionWrap").hidden = true;
  const alternate = $("confirmModalAlternate");
  if (alternate) {
    alternate.hidden = false;
    alternate.textContent = alternateLabel;
  }
  setConfirmModalBusy(false);
  const detailNode = $("confirmModalDetails");
  const hasDetails = Boolean(String(details || "").trim());
  detailNode.hidden = !hasDetails;
  detailNode.textContent = hasDetails ? String(details).trim() : "";
  return new Promise((resolve) => {
    state.confirmModalResolver = (value) => resolve(value || "cancel");
  });
}

function isFormControlTarget(target) {
  return Boolean(target?.closest?.("input, select, textarea, button, [contenteditable='true']"));
}

function stopPreviewCameraKeyboardLeak(event) {
  const workspace = $("liveryPreviewWorkspace");
  if (!workspace || workspace.hidden) return;
  if (!isFormControlTarget(event.target)) return;
  event.stopPropagation();
}

function showInputModal({
  type = "info",
  title = "Enter value",
  subtitle = "",
  message = "",
  label = "Value",
  value = "",
  details = "",
  confirmLabel = "Confirm",
  alternateLabel = "",
  cancelLabel = "Cancel",
  hideCancel = false,
  modalClass = "",
  loadingLabel = "",
  onConfirm = null,
  onAlternate = null,
} = {}) {
  const modal = $("confirmModal");
  if (!modal) return Promise.resolve(null);
  state.confirmModalMode = "input";
  state.confirmModalConfirmLabel = confirmLabel;
  state.confirmModalLoadingLabel = loadingLabel || `${confirmLabel}...`;
  modal.hidden = false;
  modal.dataset.type = type;
  modal.dataset.mode = "input";
  modal.dataset.modalClass = modalClass || "";
  delete modal.dataset.actionLayout;
  $("confirmModalBadge").textContent = "Input";
  $("confirmModalTitle").textContent = title;
  $("confirmModalSubtitle").textContent = subtitle;
  $("confirmModalMessage").textContent = message;
  $("confirmModalConfirm").textContent = confirmLabel;
  const cancel = $("confirmModalCancel");
  if (cancel) {
    cancel.hidden = Boolean(hideCancel);
    cancel.textContent = cancelLabel;
  }
  const alternate = $("confirmModalAlternate");
  if (alternate) {
    alternate.hidden = !alternateLabel;
    alternate.textContent = alternateLabel || "";
    alternate.classList.toggle("primary", Boolean(alternateLabel && modalClass === "export-package"));
  }
  $("confirmModalInputWrap").hidden = false;
  $("confirmModalOptionWrap").hidden = true;
  setConfirmModalBusy(false);
  $("confirmModalInputLabel").textContent = label;
  const input = $("confirmModalInput");
  input.value = value || "";
  state.confirmModalOnConfirm = typeof onConfirm === "function"
    ? () => onConfirm(input.value || "")
    : null;
  const detailNode = $("confirmModalDetails");
  const hasDetails = Boolean(String(details || "").trim());
  detailNode.hidden = !hasDetails;
  detailNode.textContent = hasDetails ? String(details).trim() : "";
  window.setTimeout(() => {
    input.focus();
    input.select();
  }, 0);
  const resolveActionResult = typeof onConfirm === "function";
  return new Promise((resolve) => {
    state.confirmModalResolver = (value) => {
      if (value === "alternate" && typeof onAlternate === "function") {
        try {
          resolve(Promise.resolve(onAlternate(input.value || "")).catch((error) => ({ ok: false, message: error?.message || String(error), error })));
        } catch (error) {
          resolve({ ok: false, message: error?.message || String(error), error });
        }
        return;
      }
      if (resolveActionResult) {
        resolve(value);
        return;
      }
      resolve(value ? input.value : null);
    };
  });
}
window.showInputModal = showInputModal;

function multipleSavedCarProfileConflict(result = {}) {
  const marker = "Automatic SavedCar deployment found multiple profile SavedCars targets";
  const source = [result?.message, result?.errorDetails, result?.errorDetail].filter(Boolean).join("\n");
  if (!source.includes(marker)) return null;
  const countMatch = source.match(/found\s+(\d+)/i);
  const count = Number(countMatch?.[1] || 0);
  const detectedPaths = [...new Set(source
    .split(/\r?\n/)
    .map((line) => line.trim().replace(/^-\s*/, ""))
    .filter((line) => /[\\/]ProfileData[\\/].+[\\/]OpenData[\\/]SavedCars$/i.test(line)))];
  const countLabel = count > 0 ? String(count) : "multiple";
  const detectedSection = detectedPaths.length
    ? ["", "Detected profile folders:", ...detectedPaths.map((path) => `- ${path}`)]
    : [];
  const errorReference = result?.errorId ? ["", `Error reference: ${result.errorId}`] : [];
  return {
    message: [
      `Official livery activation was stopped because LiveryLab found ${countLabel} SavedCars profile folders and cannot safely determine which AC EVO profile is currently active.`,
      "",
      "No files were changed.",
    ].join("\n"),
    details: [
      "To continue:",
      "",
      "1. Close AC EVO and LiveryLab.",
      "2. Start AC EVO, save or modify a car in the garage, then close the game.",
      "3. Check which folder below contains a newly created or updated SavedCar file:",
      "",
      "   <Saved Games folder>\\ACE\\ProfileData\\<profile ID>\\OpenData\\SavedCars",
      "",
      "4. Back up the inactive SavedCars folder outside Saved Games\\ACE.",
      "5. Temporarily rename the inactive folder to SavedCars.inactive.",
      "6. Start LiveryLab and activate the livery again.",
      "7. Restore the original SavedCars folder name afterwards.",
      ...detectedSection,
      "",
      "Do not delete either profile folder.",
      ...errorReference,
    ].join("\n"),
  };
}

function maybeShowResultModal(name, result) {
  if (!IMPORTANT_CALLS.has(name)) return;
  if (result?.ok && DEFERRED_MY_SKINS_SUCCESS_MODAL_CALLS.has(name)) return;
  const savedCarProfileConflict = name === "activate_official_external_project" && !result?.ok
    ? multipleSavedCarProfileConflict(result)
    : null;
  if (savedCarProfileConflict) {
    showStatusModal({
      type: "error",
      title: "Multiple AC EVO profiles found",
      subtitle: "Official activation stopped",
      error: savedCarProfileConflict.message,
      details: savedCarProfileConflict.details,
      technicalDetails: technicalDetailsFromResult(result),
      modalClass: "saved-car-profile-conflict",
      detailsOpen: true,
    });
    return;
  }
  const title = MODAL_LABELS[name] || name;
  const subtitle = result.ok ? "Action completed" : "Action failed";
  const firstLineSuccessCalls = new Set(["set_my_skin_active", "deactivate_official_external_project", "trash_my_skin", "prepare_game_for_package_refresh"]);
  const errorDisplay = result.ok ? null : displayErrorFromResult(name, result);
  const message = result.ok && firstLineSuccessCalls.has(name)
    ? firstMessageLine(result.message, modalMessageFromResult(name, result))
    : (result.ok ? modalMessageFromResult(name, result) : errorDisplay.summary);
  const details = result.ok ? (result.message || "") : errorDisplay.details;
  const technicalDetails = result.ok ? "" : technicalDetailsFromResult(result);
  const openFolderPath = result.ok ? openFolderPathFromResult(name, result) : "";
  showStatusModal({
    type: result.ok ? "success" : "error",
    title,
    subtitle,
    message: result.ok ? message : "",
    error: result.ok ? "" : message,
    details,
    technicalDetails,
    openFolderPath,
  });
}

function firstStringValue(source, keys) {
  if (!source || typeof source !== "object") return "";
  for (const key of keys) {
    const value = source[key];
    if (typeof value === "string" && value.trim()) return value.trim();
  }
  return "";
}

function openFolderPathFromResult(name, result) {
  if (!OPEN_FOLDER_RESULT_CALLS.has(name) || !result?.ok) return "";
  const data = result.data && typeof result.data === "object" ? result.data : {};
  return firstStringValue(data, ["folder", "outputFolder", "outDir", "directory"])
    || firstStringValue(data, ["outputPath", "path", "ddsPath", "pngPath", "texturemipsPath", "obj_path", "mtl_path"]);
}

function showResult(result, fallback = "Done", callName = "") {
  if (!result.ok) {
    const errorDisplay = displayErrorFromResult(callName, result, "Unknown error");
    log(errorDisplay.summary, true);
    setInspectOutputText(displayDetails([errorDisplay.summary, errorDisplay.details].filter(Boolean).join("\n\n"), technicalDetailsFromResult(result)));
    if (callName) maybeShowResultModal(callName, result);
    return false;
  }
  if (result.message) log(result.message);
  else log(fallback);
  if (callName) maybeShowResultModal(callName, result);
  return true;
}

function applyAppBackground(tab) {
  if (!document.body) return;
  document.body.dataset.appBackground = APP_BACKGROUND_BY_TAB[tab] || "1";
}

async function call(name, ...args) {
  log(`${t("log_running")} ${name}...`);
  try {
    const result = await window.pywebview.api[name](...args);
    showResult(result, name, name);
    return result;
  } catch (error) {
    window.LiveryLabStartupLog?.reportError?.(error, { stage: "api_call", operation: name });
    throw error;
  }
}

async function refreshLogStatus() {
  const status = $("logFileStatus");
  if (!window.pywebview?.api?.get_log_status) return;
  try {
    const result = await window.pywebview.api.get_log_status();
    const data = result?.data || {};
    if (status) {
      status.textContent = data.enabled && data.path ? `Current log: ${data.path}` : "Error logging is not available.";
      status.classList.toggle("is-valid", Boolean(data.enabled));
      status.classList.toggle("is-invalid", !data.enabled);
      status.classList.remove("is-unknown");
    }
  } catch (error) {
    if (status) status.textContent = "Log status could not be loaded.";
    window.LiveryLabStartupLog?.reportError?.(error, { stage: "settings", operation: "get_log_status" });
  }
}

window.handleBackendProgress = function handleBackendProgress(eventName, payload) {
  if (eventName === "package_extract") {
    updatePackageProgress(payload || {});
  } else if (eventName === "advanced_active_livery_refresh") {
    const progress = payload || {};
    showStatusModal({
      type: "info",
      title: "Refresh Active Advanced Liveries",
      subtitle: `Livery ${progress.index || 0} of ${progress.count || 0}`,
      message: `Restoring ${progress.displayName || progress.skinKey || "Advanced livery"} after the full extraction...`,
      details: "Livery files and supported Advanced dependencies are reapplied through the normal Activate in Game workflow.",
      blockClose: true,
    });
  } else if (eventName === "car_compatibility") {
    state.carCompatibilityProgress = payload || null;
    renderCarCompatibility();
  }
};

function setTab(tab) {
  const removedOverviewTabs = {
    "editing-home": "convert",
    "liveries-home": "library",
  };
  tab = removedOverviewTabs[tab] || tab;
  tab = coerceWorkflowTab(tab);
  if (isSetupGateLocked() && !SETUP_GATE_ALLOWED_TABS.has(tab)) {
    showSetupGateNotice(tab);
    tab = "home";
  }
  applyAppBackground(tab);
  const parentMap = {
    home: "home",
    package: "settings-home",
    inspect: "inspect",
  };
  const parentTab = parentMap[tab] || tab;
  document.querySelectorAll(".nav-button").forEach((button) => {
    const buttonTab = button.dataset.tab;
    const isCategory = button.classList.contains("nav-category");
    button.classList.toggle("active", isCategory ? buttonTab === parentTab : buttonTab === tab);
  });
  document.querySelectorAll(".nav-section").forEach((section) => {
    const category = section.querySelector(".nav-category");
    const sectionParent = section.dataset.navParent || category?.dataset.tab || category?.dataset.navLabel || "";
    section.classList.toggle("is-active", sectionParent === parentTab);
  });
  document.querySelectorAll(".tab").forEach((panel) => {
    panel.classList.toggle("active", panel.id === `tab-${tab}`);
  });
  updateLayoutForTab(tab);
  applySelectionForTab(tab);
  if (tab !== "library") {
    stopMySkinThumbnailWatcher();
    stopMySkinRoadPlateWatcher();
  }
  if (tab === "library") {
    loadMySkins();
  } else if (tab === "custom-grids") {
    window.LiveryLabCustomGrids?.init?.();
    window.LiveryLabCustomGrids?.load?.();
  } else if (tab === "convert" || tab === "inspect") {
    focusContentCarsBrowserStartForEnduser(tab);
  } else if (tab === "skin") {
    if (currentWorkflowScope() === "official") {
      refreshOfficialCreatorCars({ force: false });
    } else {
      focusSkinCreatorBrowserStart();
    }
  } else if (tab === "numberplate-creator") {
    window.LiveryLabNumberplateStudio?.refresh?.();
    showNumberplateGeneratorInfoModalWhenNeeded();
  } else if (tab === "tyre-brands") {
    loadCustomTyreBrands();
  } else if (tab === "drivers") {
    loadDriverProfiles();
    loadDriverSourceSkins();
    showDriverProfilesInfoModalWhenNeeded();
  } else if (tab === "my-skin-packages") {
    window.clearTimeout(state.driverPngPreviewTimer);
    window.LiveryLabTransferBundle?.refresh?.();
  } else {
    window.clearTimeout(state.driverPngPreviewTimer);
  }
}

function activeTab() {
  return document.querySelector(".nav-category.active")?.dataset.tab || "home";
}

function ensureTabsInsideToolContent() {
  const toolPane = document.querySelector(".tool-pane");
  const toolContent = document.querySelector(".tool-content");
  if (!toolPane || !toolContent) return;
  Array.from(toolPane.children).forEach((child) => {
    if (child.matches?.("section.tab")) {
      toolContent.appendChild(child);
    }
  });
}

function updateLayoutForTab(tab = activeTab()) {
  ensureTabsInsideToolContent();
  const workArea = document.querySelector(".work-area");
  const filePanel = document.querySelector(".file-panel");
  const browserAnalysisLegend = $("browserAnalysisLegend");
  const uvToggle = $("toggleUvBrowser");
  const uvMode = tab === "uv";
  const libraryMode = tab === "library";
  const filePanelVisible = tab === "convert";
  const fullWidthMode = !filePanelVisible;
  if (workArea) workArea.classList.toggle("uv-mode", uvMode);
  if (workArea) workArea.classList.toggle("uv-browser-visible", false);
  if (workArea) workArea.classList.toggle("library-mode", libraryMode);
  if (workArea) workArea.classList.toggle("library-browser-visible", false);
  if (workArea) workArea.classList.toggle("full-width-mode", fullWidthMode);
  if (filePanel) filePanel.classList.toggle("is-hidden", !filePanelVisible);
  if (browserAnalysisLegend) browserAnalysisLegend.hidden = true;
  if (uvToggle) {
    uvToggle.hidden = true;
    uvToggle.textContent = t("browser_show");
  }
}

function toggleUvBrowser() {
  state.uvBrowserVisible = false;
  updateLayoutForTab();
}

function setUvControlTab(tab) {
  state.uvControlTab = tab || "setup";
  document.querySelectorAll(".uv-control-tab").forEach((button) => {
    button.classList.toggle("active", button.dataset.uvTab === state.uvControlTab);
  });
  document.querySelectorAll(".uv-control-panel").forEach((panel) => {
    panel.classList.toggle("active", panel.dataset.uvPanel === state.uvControlTab);
  });
}

async function applySelectionForTab(tab) {
  if (!state.selected || state.selected.isDir) return;
  if (browserItemBlockedForCurrentMode(state.selected, tab)) {
    showBrowserLockedNotice();
    return;
  }
  if (!enduserBrowserFileAllowedForTool(state.selected.path, tab)) {
    showBrowserToolMismatchNotice(tab);
    return;
  }
  if (tab === "convert") {
    await applySelectionForExport(true);
  } else if (tab === "package" && extensionOf(state.selected.path) === "kspkg") {
    applySelectionForPackage(true);
  } else if (tab === "mesh" && extensionOf(state.selected.path) === "mesh") {
    await applySelectionForMesh(true);
  } else if (tab === "preview3d" && ["glb", "gltf"].includes(extensionOf(state.selected.path))) {
    setPath("preview3dModelPath", state.selected.path);
    inspectPreview3dProject(false);
  } else if (tab === "preview3d" && ["png", "jpg", "jpeg", "webp", "bmp", "tga", "tif", "tiff"].includes(extensionOf(state.selected.path))) {
    setPath("preview3dTexturePath", state.selected.path);
    reloadPreview3dTexture();
    inspectPreview3dProject(false);
  } else if (tab === "rim-changer" && extensionOf(state.selected.path) === "rimmesh") {
    applySelectionForRim(true);
  } else if (tab === "rim-changer" && extensionOf(state.selected.path) === "compatiblerim") {
    setPath("rimBaseCompatPath", state.selected.path);
  } else if (tab === "uv") {
    await applySelectionForUv(true);
  } else if (tab === "material" && extensionOf(state.selected.path) === "material") {
    setPath("materialPath", state.selected.path);
    await setMaterialOutputDefaults(state.selected.path, true);
  } else if (tab === "skin") {
    useSelected();
  } else if (tab === "numberplate-creator" && extensionOf(state.selected.path) === "png") {
    setPath("numberplateCreatorPng", state.selected.path);
  }
}

async function choose(mode, target, filter, saveName = "") {
  const filters = filter ? [filter, "All files (*.*)"] : ["All files (*.*)"];
  const suggestedName = saveNameForTarget(target, saveName);
  const result = await window.pywebview.api.choose_path(mode, filters, suggestedName);
  if (showResult(result, t("log_path_selected")) && result.data.path) {
    const nextPath = target === "rootPath" ? normalizeRootPath(result.data.path) : result.data.path;
    setPath(target, nextPath);
    if (target === "rootPath") {
      syncRootFields(nextPath);
      await refreshBrowser("");
    } else {
      await applyPathDefaults(target, result.data.path);
      if (target === "skinPreviewImage") {
        await validateSkinPreviewImage(false);
      }
    }
  }
}

function pathFromDropEvent(event) {
  const transfer = event.dataTransfer;
  if (!transfer) return "";
  const file = transfer.files?.[0];
  if (file?.path) return file.path;
  const uriList = transfer.getData("text/uri-list");
  if (uriList) {
    const firstUri = uriList.split(/\r?\n/).find((line) => line && !line.startsWith("#"));
    if (firstUri?.startsWith("file://")) {
      const decoded = decodeURIComponent(firstUri.replace(/^file:\/+/, ""));
      return decoded.replace(/^\/([A-Za-z]:\/)/, "$1").replaceAll("/", "\\");
    }
  }
  const text = transfer.getData("text/plain");
  if (text && /^[A-Za-z]:[\\/]/.test(text.trim())) return text.trim();
  return "";
}

function acceptedExtensionsFromFilter(filter = "") {
  return [...String(filter).matchAll(/\*\.([A-Za-z0-9]+)/g)]
    .map((match) => match[1].toLowerCase());
}

function pathMatchesFilter(path, filter) {
  const allowed = acceptedExtensionsFromFilter(filter);
  if (!allowed.length) return true;
  const ext = extensionOf(path);
  return allowed.includes(ext);
}

function beginPendingFileDrop(target, filter) {
  if (state.pendingFileDrop?.timer) {
    window.clearTimeout(state.pendingFileDrop.timer);
  }
  state.pendingFileDrop = {
    target,
    filter,
    timer: window.setTimeout(() => {
      state.pendingFileDrop = null;
      log("Dropped file path could not be read by the webview.", true);
    }, 1200),
  };
}

function clearPendingFileDrop() {
  if (state.pendingFileDrop?.timer) {
    window.clearTimeout(state.pendingFileDrop.timer);
  }
  state.pendingFileDrop = null;
}

function requestNativeDropPath(event) {
  if (!window.pywebview?._jsApiCallback) return;
  try {
    window.pywebview._jsApiCallback(
      "pywebviewEventHandler",
      { event, nodeId: "document" },
      "eventHandler",
    );
  } catch (error) {
    log(error?.message || String(error), true);
  }
}

async function applyDroppedPath(target, path) {
  const input = $(target);
  if (!input || !path) return;
  const nextPath = target === "rootPath" ? normalizeRootPath(path) : path;
  setPath(target, nextPath);
  input.dataset.skipPathDefaults = "1";
  input.dispatchEvent(new Event("input", { bubbles: true }));
  input.dispatchEvent(new Event("change", { bubbles: true }));
  delete input.dataset.skipPathDefaults;
  if (target === "rootPath") {
    syncRootFields(nextPath);
    await refreshBrowser("");
    return;
  }
  await applyPathDefaults(target, nextPath);
  if (target === "skinPreviewImage") {
    await validateSkinPreviewImage(false);
  }
}

function setupPathInputDrop(input, { target, filter = "" } = {}) {
  if (!input || input.dataset.dropReady === "1") return;
  input.dataset.dropReady = "1";
  input.classList.add("path-drop-target");

  const hasPathTransfer = (event) => {
    const types = [...(event.dataTransfer?.types || [])];
    return types.includes("Files") || types.includes("text/plain") || types.includes("application/x-liverylab-browser-path");
  };
  const setActive = (active) => input.classList.toggle("is-drag-over", active);
  input.addEventListener("dragenter", (event) => {
    if (!hasPathTransfer(event)) return;
    event.preventDefault();
    setActive(true);
  });
  input.addEventListener("dragover", (event) => {
    if (!hasPathTransfer(event)) return;
    event.preventDefault();
    event.dataTransfer.dropEffect = "copy";
    setActive(true);
  });
  input.addEventListener("dragleave", () => setActive(false));
  input.addEventListener("drop", async (event) => {
    event.preventDefault();
    setActive(false);
    const path = pathFromDropEvent(event);
    if (!path) {
      beginPendingFileDrop(target || input.id, filter);
      requestNativeDropPath(event);
      return;
    }
    clearPendingFileDrop();
    if (!pathMatchesFilter(path, filter)) {
      const allowed = acceptedExtensionsFromFilter(filter).map((ext) => `.${ext}`).join(", ");
      log(`Dropped file type is not valid for this field${allowed ? ` (${allowed})` : ""}.`, true);
      return;
    }
    await applyDroppedPath(target || input.id, path);
  });
}

window.handleNativeFileDrop = async function handleNativeFileDrop(paths) {
  const path = Array.isArray(paths) ? paths[0] : "";
  const pending = state.pendingFileDrop;
  clearPendingFileDrop();
  if (!pending?.target || !path) return;
  if (!pathMatchesFilter(path, pending.filter)) {
    const allowed = acceptedExtensionsFromFilter(pending.filter).map((ext) => `.${ext}`).join(", ");
    log(`Dropped file type is not valid for this field${allowed ? ` (${allowed})` : ""}.`, true);
    return;
  }
  await applyDroppedPath(pending.target, path);
};

function enablePathInputDrops(root = document) {
  root.querySelectorAll("[data-pick], [data-save], [data-folder]").forEach((button) => {
    const target = button.dataset.pick || button.dataset.save || button.dataset.folder || "";
    const input = target ? $(target) : null;
    setupPathInputDrop(input, { target, filter: button.dataset.filter || "" });
  });

  document.addEventListener("dragover", (event) => {
    const types = [...(event.dataTransfer?.types || [])];
    if (!types.includes("Files") && !types.includes("application/x-liverylab-browser-path")) return;
    event.preventDefault();
  });
  document.addEventListener("drop", (event) => {
    const types = [...(event.dataTransfer?.types || [])];
    if (!types.includes("Files") && !types.includes("application/x-liverylab-browser-path")) return;
    event.preventDefault();
  });
}

function saveNameForTarget(target, fallback) {
  if (target === "ddsOut") {
    const source = $("texturePath").value.trim() || state.selected?.path || "";
    return source ? fileNameWithExtension(source, ".dds") : fallback;
  }
  if (target === "pngOut") {
    const source = $("texturePath").value.trim() || state.selected?.path || "";
    return source ? fileNameWithExtension(source, ".png") : fallback;
  }
  if (target === "mipsOut") {
    const source = $("pngIn")?.value.trim() || $("ddsIn").value.trim() || $("backTexture").value.trim() || state.selected?.path || "";
    return source ? fileNameWithExtension(source, ".texturemips") : fallback;
  }
  if (target === "meshObjOut") {
    const source = $("meshPath").value.trim() || state.selected?.path || "";
    return source ? fileNameWithExtension(source, ".obj") : fallback;
  }
  if (target === "rimOutPath") {
    const source = $("rimTargetPath")?.value.trim() || state.selected?.path || "";
    return source ? `${fileNameWithoutExtension(source)}_swap_test.rimmesh` : fallback;
  }
  if (target === "uvExportPath") {
    const source = $("uvMeshPath").value.trim() || state.selected?.path || "";
    return source ? fileNameWithExtension(source, ".png") : fallback;
  }
  if (target === "packageListOut") {
    const source = $("packagePath").value.trim() || state.selected?.path || "";
    return source ? `${fileNameWithoutExtension(source)}_file_list.txt` : fallback;
  }
  return fallback;
}

const LIVERY_PREVIEW_TEXTURE_META = {
  ext_skin_c: { label: "EXT_SKIN_C", role: "BaseColor / Livery", pathKeys: ["texturePath", "liveTexturePath"] },
  ext_skin_d: { label: "EXT_Skin_D", role: "SpatMap / Material mask", pathKeys: ["detailTexturePath", "liveDetailTexturePath"] },
  ext_skin_d_2: { label: "EXT_Skin_D_2", role: "Detail mask 2", pathKeys: ["liveDetail2TexturePath"] },
  ext_skin_drift_d: { label: "EXT_Skin_Drift_D", role: "Drift SpatMap", pathKeys: ["liveDriftDetailTexturePath"] },
  ext_skin_drift_ao: { label: "EXT_Skin_Drift_AO", role: "Drift AO", pathKeys: ["liveDriftAoTexturePath"] },
  ext_skin_m: { label: "EXT_Skin_M", role: "Metalness", pathKeys: ["liveMetalnessTexturePath"] },
  ext_skin_r: { label: "EXT_Skin_R", role: "Roughness", pathKeys: ["liveRoughnessTexturePath"] },
  ext_skin_cc: { label: "EXT_Skin_CC", role: "ClearCoat", pathKeys: ["liveClearCoatTexturePath"] },
  ext_skin_cr: { label: "EXT_Skin_CR", role: "ClearCoat Roughness", pathKeys: ["liveClearCoatRoughnessTexturePath"] },
  ext_skin_ao: { label: "ext_skin_ao", role: "Ambient Occlusion", pathKeys: ["aoTexturePath", "liveAoTexturePath"] },
  ext_skin_s: { label: "EXT_Skin_S", role: "Scuff", pathKeys: ["liveScuffTexturePath"] },
};

const LIVERY_PREVIEW_TEXTURE_ORDER = [
  "ext_skin_c",
  "ext_skin_d",
  "ext_skin_m",
  "ext_skin_r",
  "ext_skin_cc",
  "ext_skin_cr",
  "ext_skin_ao",
  "ext_skin_d_2",
  "ext_skin_drift_d",
  "ext_skin_drift_ao",
  "ext_skin_s",
];

const LIVERY_PREVIEW_MODE_META = {
  liveryOnly: { label: "Livery Only", status: "Livery Only" },
  textureInspect: { label: "Texture Inspect", status: "Texture Inspect" },
  liveryAo: { label: "Livery + AO", status: "Livery + AO" },
  materialView: { label: "Final Livery", status: "Final Livery" },
  evoApprox: { label: "EVO Approx", status: "EVO Approx" },
  materialMask: { label: "Material Mask", status: "Material Mask" },
};

const LIVERY_MATERIAL_FINISH_DEFAULT = Object.freeze({
  metalness: 0,
  roughness: 0.3,
  clearCoat: 1,
  clearCoatRoughness: 0.3,
  scope: "base",
});

const LIVERY_MATERIAL_FINISH_PRESETS = {
  gloss: { metalness: 0, roughness: 0.43, clearCoat: 0.6, clearCoatRoughness: 0 },
  matte: { metalness: 0, roughness: 0.86, clearCoat: 0.1, clearCoatRoughness: 0.57 },
  metallic: { metalness: 0.9, roughness: 0.71, clearCoat: 0.8, clearCoatRoughness: 0 },
  satinMetallic: { metalness: 0.9, roughness: 0.71, clearCoat: 0.5, clearCoatRoughness: 0.43 },
  matteMetallic: { metalness: 0.9, roughness: 0.86, clearCoat: 0.1, clearCoatRoughness: 0.57 },
  chrome: { metalness: 1, roughness: 0.29, clearCoat: 0.8, clearCoatRoughness: 0 },
  reset: LIVERY_MATERIAL_FINISH_DEFAULT,
};

function normalizeLiveryPreviewTextureKey(value) {
  return String(value || "").trim().toLowerCase() || "ext_skin_c";
}

function liveryPreviewTextureOrderValue(key) {
  const index = LIVERY_PREVIEW_TEXTURE_ORDER.indexOf(normalizeLiveryPreviewTextureKey(key));
  return index >= 0 ? index : 1000;
}

function liveryPreviewTextureLabel(key) {
  const normalized = normalizeLiveryPreviewTextureKey(key);
  if (LIVERY_PREVIEW_TEXTURE_META[normalized]) return LIVERY_PREVIEW_TEXTURE_META[normalized].label;
  return normalized
    .split("_")
    .filter(Boolean)
    .map((part, index) => index === 0 ? part.toUpperCase() : part.charAt(0).toUpperCase() + part.slice(1))
    .join("_");
}

function liveryPreviewTextureRole(key) {
  return LIVERY_PREVIEW_TEXTURE_META[normalizeLiveryPreviewTextureKey(key)]?.role || "EXT_SKIN texture";
}

function isAbsoluteLocalPath(path) {
  return /^[a-zA-Z]:[\\/]/.test(path) || /^\\\\/.test(path);
}

function resolveLiveryPreviewProjectPath(project = {}, path = "") {
  const text = String(path || "").trim();
  if (!text || isAbsoluteLocalPath(text)) return text;
  const folder = String(project.projectFolder || state.liveryPreviewProjectFolder || "").trim();
  if (!folder) return text;
  return `${folder.replace(/[\\/]+$/, "")}\\${text.replace(/^[\\/]+/, "")}`;
}

function deriveCarAoTexturePath(project = {}) {
  const carModel = String(project.carModel || "").trim();
  const projectFolder = String(project.projectFolder || "").trim();
  if (!carModel || !projectFolder) return "";
  const normalized = projectFolder.replace(/\//g, "\\");
  const marker = "\\LiveryProjects\\";
  const markerIndex = normalized.toLowerCase().indexOf(marker.toLowerCase());
  if (markerIndex < 0) return "";
  const releaseRoot = normalized.slice(0, markerIndex);
  return `${releaseRoot}\\LiveryLibrary\\Converted\\Textures\\${carModel}\\ext_skin_ao.png`;
}

function preview3dAoTexturePathForProject(project = {}) {
  return String(project.aoTexturePath || project.liveAoTexturePath || deriveCarAoTexturePath(project) || "").trim();
}

function liveryPreviewTexturePathForKey(project = {}, key = "ext_skin_c") {
  const normalized = normalizeLiveryPreviewTextureKey(key);
  if (normalized === "ext_skin_ao") return state.preview3dAoTexturePath || preview3dAoTexturePathForProject(project);
  if (normalized === "ext_skin_c") return $("preview3dTexturePath")?.value.trim() || resolveLiveryPreviewProjectPath(project, project.texturePath || project.liveTexturePath || "");
  if (normalized === "ext_skin_d") return state.preview3dDetailTexturePath || resolveLiveryPreviewProjectPath(project, project.detailTexturePath || project.liveDetailTexturePath || "");
  const meta = LIVERY_PREVIEW_TEXTURE_META[normalized] || {};
  for (const pathKey of meta.pathKeys || []) {
    const path = resolveLiveryPreviewProjectPath(project, project[pathKey]);
    if (path) return path;
  }
  const maps = Array.isArray(project.liveMaterialTextureMaps) ? project.liveMaterialTextureMaps : [];
  const map = maps.find((item) => normalizeLiveryPreviewTextureKey(item?.stem) === normalized);
  return resolveLiveryPreviewProjectPath(project, map?.path || "");
}

function upsertLiveryPreviewTextureEntry(entries, entry) {
  const key = normalizeLiveryPreviewTextureKey(entry?.key || entry?.stem);
  const path = String(entry?.path || "").trim();
  if (!key || !path) return;
  const existing = entries.get(key) || {};
  entries.set(key, {
    key,
    label: entry.label || existing.label || liveryPreviewTextureLabel(key),
    role: entry.role || existing.role || liveryPreviewTextureRole(key),
    path,
  });
}

function liveryPreviewTextureEntries(project = state.liveryPreviewProject || {}) {
  const entries = new Map();
  ["ext_skin_c", "ext_skin_d", "ext_skin_m", "ext_skin_r", "ext_skin_cc", "ext_skin_cr", "ext_skin_ao", "ext_skin_s"].forEach((key) => {
    upsertLiveryPreviewTextureEntry(entries, {
      key,
      path: liveryPreviewTexturePathForKey(project, key),
    });
  });
  (Array.isArray(project.liveMaterialTextureMaps) ? project.liveMaterialTextureMaps : []).forEach((item) => {
    const key = normalizeLiveryPreviewTextureKey(item?.stem);
    upsertLiveryPreviewTextureEntry(entries, {
      key,
      label: item?.label || item?.name || liveryPreviewTextureLabel(key),
      role: liveryPreviewTextureRole(key),
      path: resolveLiveryPreviewProjectPath(project, item?.path || ""),
    });
  });
  return [...entries.values()].sort((a, b) => {
    const orderDiff = liveryPreviewTextureOrderValue(a.key) - liveryPreviewTextureOrderValue(b.key);
    return orderDiff || String(a.label).localeCompare(String(b.label));
  });
}

function liveryPreviewTextureEntryForKey(key = state.liveryPreviewTextureKey, project = state.liveryPreviewProject || {}) {
  const entries = liveryPreviewTextureEntries(project);
  const normalized = normalizeLiveryPreviewTextureKey(key);
  return entries.find((entry) => entry.key === normalized)
    || entries.find((entry) => entry.key === "ext_skin_c")
    || entries[0]
    || null;
}

function currentLiveryPreviewTextureEntry(project = state.liveryPreviewProject || {}) {
  return liveryPreviewTextureEntryForKey(state.liveryPreviewTextureKey || "ext_skin_c", project);
}

function normalizeLiveryPreviewMode(mode) {
  return LIVERY_PREVIEW_MODE_META[mode] ? mode : "materialView";
}

function clampLiveryMaterialFinishValue(value, fallback = 0) {
  const number = Number(value);
  if (!Number.isFinite(number)) return fallback;
  return Math.max(0, Math.min(number, 1));
}

function normalizeLiveryMaterialFinish(value = null) {
  const raw = value && typeof value === "object" ? value : {};
  return {
    metalness: clampLiveryMaterialFinishValue(raw.metalness, LIVERY_MATERIAL_FINISH_DEFAULT.metalness),
    roughness: clampLiveryMaterialFinishValue(raw.roughness, LIVERY_MATERIAL_FINISH_DEFAULT.roughness),
    clearCoat: clampLiveryMaterialFinishValue(raw.clearCoat, LIVERY_MATERIAL_FINISH_DEFAULT.clearCoat),
    clearCoatRoughness: clampLiveryMaterialFinishValue(raw.clearCoatRoughness, LIVERY_MATERIAL_FINISH_DEFAULT.clearCoatRoughness),
    scope: "base",
  };
}

const liveryMaterialFinishSubscribers = new Set();

function notifyLiveryMaterialFinishSubscribers(finish, meta = {}) {
  const normalized = normalizeLiveryMaterialFinish(finish);
  liveryMaterialFinishSubscribers.forEach((callback) => {
    try {
      callback(normalized, meta);
    } catch (error) {
      console.warn("Material finish subscriber failed", error);
    }
  });
}

function liveryPreviewModeLabel(mode = state.liveryPreviewPreviewMode) {
  return LIVERY_PREVIEW_MODE_META[normalizeLiveryPreviewMode(mode)]?.label || "Final Livery";
}

function liveryPreviewModeUsesSelectedTexture(mode = state.liveryPreviewPreviewMode) {
  return normalizeLiveryPreviewMode(mode) === "textureInspect";
}

function liveryPreviewModeUsesAo(mode = state.liveryPreviewPreviewMode) {
  return ["liveryAo", "materialView", "evoApprox"].includes(normalizeLiveryPreviewMode(mode));
}

function liveryPreviewMaterialMaskDisplayMode(key = state.liveryPreviewTextureKey) {
  const normalized = normalizeLiveryPreviewTextureKey(key);
  return ["ext_skin_d", "ext_skin_d_2", "ext_skin_drift_d"].includes(normalized) ? "channels" : "grayscale";
}

function liveryPreviewBodyBaseColorEnabled(project = state.liveryPreviewProject || {}) {
  return project?.bodyBaseColorEnabled === true;
}

function liveryPreviewBodyBaseColorMode(project = state.liveryPreviewProject || {}) {
  const capability = project?.bodyBaseColorCapability && typeof project.bodyBaseColorCapability === "object" ? project.bodyBaseColorCapability : {};
  return String(project?.bodyBaseColorMode || capability.mode || "").trim().toLowerCase();
}

function syncLiveryPreviewBodyBaseColorControls(project = state.liveryPreviewProject || {}) {
  const button = $("liveryPreviewSaveBodyBaseColor");
  const mode = liveryPreviewBodyBaseColorMode(project);
  if (button) {
    button.textContent = mode === "saved_car_synced"
      ? "Apply Body Color to Game"
      : mode === "official_external_red_channel" ? "Save Fixed Livery Color" : "Save Base Color";
  }
  const detectButton = $("liveryPreviewDetectBodyBaseColor");
  if (detectButton) {
    detectButton.hidden = String(project?.projectType || "").trim().toLowerCase() === "official_external_livery";
  }
}

function liveryPreviewBodyBaseColorStatus(project = state.liveryPreviewProject || {}) {
  const mode = liveryPreviewBodyBaseColorMode(project);
  if (liveryPreviewBodyBaseColorEnabled(project)) {
    return mode === "blank_enabled" ? "enabled for advanced blank livery" : "enabled by livery metadata";
  }
  if (mode === "disabled") return "not used by this livery";
  if (mode === "unknown") return "not detected for this livery";
  return "not active";
}

function payloadPreview3dProject() {
  const colorTexturePath = $("preview3dTexturePath")?.value.trim() || "";
  const detailTexturePath = state.preview3dDetailTexturePath || "";
  const liveryProject = state.liveryPreviewProject || {};
  const previewMode = normalizeLiveryPreviewMode(state.liveryPreviewPreviewMode);
  const selectedTexture = currentLiveryPreviewTextureEntry(liveryProject);
  const activeTexturePath = liveryPreviewModeUsesSelectedTexture(previewMode)
    ? (selectedTexture?.path || colorTexturePath)
    : colorTexturePath;
  const sourceAoTexturePath = $("preview3dAoTexturePath")?.value.trim()
    || state.preview3dAoTexturePath
    || preview3dAoTexturePathForProject(liveryProject)
    || "";
  const aoTexturePath = state.liveryPreviewAoEnabled && liveryPreviewModeUsesAo(previewMode) ? sourceAoTexturePath : "";
  return {
    modelPath: $("preview3dModelPath")?.value.trim() || "",
    auxiliaryModelPaths: liveryProject.auxiliaryModelPaths || [],
    auxiliaryModels: liveryProject.auxiliaryModels || [],
    texturePath: activeTexturePath || colorTexturePath,
    textureDataUrl: liveryProject.textureDataUrl || "",
    colorTexturePath,
    detailTexturePath,
    aoTexturePath,
    bannerTexturePath: state.preview3dBannerTexturePath || "",
    materialName: $("preview3dMaterialName")?.value.trim() || "EXT_SKIN",
    bannerMaterialName: "EXT_BANNER",
    hiddenMaterialNames: Array.isArray(liveryProject.hiddenMaterialNames) ? liveryProject.hiddenMaterialNames : [],
    hiddenExteriorMaterialNames: Array.isArray(liveryProject.hiddenExteriorMaterialNames) ? liveryProject.hiddenExteriorMaterialNames : [],
    hideExteriorModel: liveryProject.hideExteriorModel === true,
    variantPartPreview: liveryProject.variantPartPreview || {},
    gameRoot: liveryProject.gameRoot || state.root || $("rootPath")?.value.trim() || "",
    carModel: liveryProject.carModel || "",
    carContentPath: liveryProject.carContentPath || "",
    skinKey: liveryProject.skinKey || state.liveryPreviewSkinKey || "",
    projectType: liveryProject.projectType || "",
    projectFolder: liveryProject.projectFolder || state.liveryPreviewProjectFolder || "",
    projectPath: liveryProject.projectPath || "",
    exteriorMeshPath: liveryProject.exteriorMeshPath || "",
    autoRefresh: !!$("preview3dAutoRefresh")?.checked,
    watchIntervalMs: Number($("preview3dWatchInterval")?.value || 1000),
    lastTextureMtimeMs: state.preview3dLastMtimeMs,
    sceneMode: state.liveryPreviewScene || "light",
    bodyBaseColor: state.liveryPreviewBodyBaseColor || "#ffffff",
    bodyBaseColorEnabled: liveryPreviewBodyBaseColorEnabled(liveryProject),
    bodyBaseColorMode: liveryProject.bodyBaseColorMode || "",
    bodyBaseColorCapability: liveryProject.bodyBaseColorCapability || {},
    officialMaterialChannels: liveryProject.officialMaterialChannels || null,
    officialPreviewMaterialSurfaces: Array.isArray(liveryProject.officialPreviewMaterialSurfaces) ? liveryProject.officialPreviewMaterialSurfaces : [],
    materialFinish: normalizeLiveryMaterialFinish(state.liveryPreviewMaterialFinish),
    previewMode,
    selectedTextureKey: selectedTexture?.key || state.liveryPreviewTextureKey || "ext_skin_c",
    selectedTextureLabel: selectedTexture?.label || liveryPreviewTextureLabel(state.liveryPreviewTextureKey),
    selectedTexturePath: selectedTexture?.path || "",
  };
}

async function attachPreviewWheelMaterials(projectPayload) {
  const auxCount = (projectPayload?.auxiliaryModels || []).length
    + (projectPayload?.auxiliaryModelPaths || []).length;
  if (!auxCount || !window.pywebview?.api?.inspect_preview3d_materials) {
    return projectPayload;
  }
  try {
    const inspectionResult = await window.pywebview.api.inspect_preview3d_materials(projectPayload);
    if (!inspectionResult?.ok) return projectPayload;
    const inspection = inspectionResult.data || {};
    state.liveryPreviewMaterialInspection = inspection;
    if (!$("liveryPreviewMaterialPanel")?.hidden) renderPreviewMaterialPanel(inspection);
    return {
      ...projectPayload,
      wheelMaterials: inspection.wheelMaterials || [],
    };
  } catch (_error) {
    return projectPayload;
  }
}

async function applyPreviewWheelMaterialsAfterLoad(projectPayload, viewer = {}) {
  if (!window.LiveryLabPreview3d?.applyWheelMaterials) {
    return { wheelMaterials: 0, wheelTexturedMaterials: 0, wheelFallbackMaterials: 0, wheelDetailMaps: 0 };
  }
  setLiveryPreviewWatchBadge("Inspecting wheel materials", "loading");
  const inspectedPayload = await attachPreviewWheelMaterials(projectPayload);
  const wheelMaterials = inspectedPayload.wheelMaterials || [];
  if (!wheelMaterials.length) {
    return { wheelMaterials: 0, wheelTexturedMaterials: 0, wheelFallbackMaterials: 0, wheelDetailMaps: 0 };
  }
  try {
    setLiveryPreviewWatchBadge("Loading wheel textures", "loading");
    return await window.LiveryLabPreview3d.applyWheelMaterials(wheelMaterials, {
      loadId: viewer.loadId,
      materialLook: currentLiveryPreviewMaterialLook(),
    });
  } catch (_error) {
    return { wheelMaterials: 0, wheelTexturedMaterials: 0, wheelFallbackMaterials: 0, wheelDetailMaps: 0 };
  }
}

function normalizeLiveryPreviewHexColor(value, fallback = "#ffffff") {
  const text = String(value || "").trim();
  if (/^#[0-9a-fA-F]{6}$/.test(text)) return text.toLowerCase();
  if (/^[0-9a-fA-F]{6}$/.test(text)) return `#${text.toLowerCase()}`;
  return fallback;
}

function applyLiveryPreviewBodyBaseColorValue(value, source = "default") {
  const color = normalizeLiveryPreviewHexColor(value);
  state.liveryPreviewBodyBaseColor = color;
  state.liveryPreviewBodyBaseColorSource = source;
  if ($("liveryPreviewBodyBaseColor")) $("liveryPreviewBodyBaseColor").value = color;
  return color;
}

function renderLiveryPreviewTextureSlotMenu(project = state.liveryPreviewProject || {}) {
  const list = $("liveryPreviewTextureSlotList");
  if (!list) return;
  const entries = liveryPreviewTextureEntries(project);
  const selected = liveryPreviewTextureEntryForKey(state.liveryPreviewTextureKey, project);
  if (selected && selected.key !== state.liveryPreviewTextureKey) state.liveryPreviewTextureKey = selected.key;
  if (!entries.length) {
    list.innerHTML = `<button class="active" type="button" role="radio" aria-checked="true" data-livery-preview-texture-key="ext_skin_c">EXT_SKIN_C</button>`;
    syncLiveryPreviewSplatLegend();
    return;
  }
  list.innerHTML = entries.map((entry) => {
    const active = entry.key === (selected?.key || "ext_skin_c");
    return `<button class="${active ? "active" : ""}" type="button" role="radio" aria-checked="${active ? "true" : "false"}" data-livery-preview-texture-key="${escapeHtml(entry.key)}" title="${escapeHtml(entry.path)}">
      <span>${escapeHtml(entry.label)}</span>
      <small>${escapeHtml(entry.role)}</small>
    </button>`;
  }).join("");
  syncLiveryPreviewSplatLegend();
}

function syncLiveryPreviewModeButtons() {
  const mode = normalizeLiveryPreviewMode(state.liveryPreviewPreviewMode);
  document.querySelectorAll("[data-livery-preview-mode]").forEach((button) => {
    const active = button.dataset.liveryPreviewMode === mode;
    button.classList.toggle("active", active);
    button.setAttribute("aria-checked", active ? "true" : "false");
  });
  syncLiveryPreviewSplatLegend();
}

function syncLiveryPreviewSplatLegend() {
  const legend = $("liveryPreviewSplatLegend");
  const workspace = $("liveryPreviewWorkspace");
  if (!legend || !workspace) return;
  const visible = normalizeLiveryPreviewMode(state.liveryPreviewPreviewMode) === "materialMask"
    && normalizeLiveryPreviewTextureKey(state.liveryPreviewTextureKey) === "ext_skin_d";
  legend.hidden = !visible;
  workspace.classList.toggle("has-splat-legend", visible);
}

function applyPreview3dProject(project) {
  const data = project?.project || project || {};
  state.liveryPreviewProject = data;
  state.liveryPreviewNumberplateSupport = normalizeNumberplateSupport(data);
  if ($("preview3dModelPath")) $("preview3dModelPath").value = data.modelPath || data.modelCachePath || "";
  if ($("preview3dTexturePath")) $("preview3dTexturePath").value = data.texturePath || data.liveTexturePath || "";
  state.preview3dAoTexturePath = preview3dAoTexturePathForProject(data);
  if ($("preview3dAoTexturePath")) $("preview3dAoTexturePath").value = state.preview3dAoTexturePath;
  state.preview3dDetailTexturePath = data.detailTexturePath || data.liveDetailTexturePath || "";
  state.preview3dBannerTexturePath = data.bannerTexturePath || data.liveBannerTexturePath || "";
  if ($("preview3dMaterialName")) $("preview3dMaterialName").value = data.materialName || "EXT_SKIN";
  if ($("preview3dAutoRefresh")) $("preview3dAutoRefresh").checked = data.autoRefresh !== false;
  if ($("preview3dWatchInterval")) $("preview3dWatchInterval").value = data.watchIntervalMs || 1000;
  state.preview3dLastMtimeMs = data.lastTextureMtimeMs || project?.texture?.mtime_ms || null;
  const hasProjectColor = /^#?[0-9a-fA-F]{6}$/.test(String(data.bodyBaseColor || "").trim());
  applyLiveryPreviewBodyBaseColorValue(hasProjectColor ? data.bodyBaseColor : "#ffffff", hasProjectColor ? "project" : "default");
  state.liveryPreviewMaterialFinish = normalizeLiveryMaterialFinish(data.materialFinish || data.material_finish || state.liveryPreviewMaterialFinish);
  syncLiveryPreviewMaterialFinishPanel();
  notifyLiveryMaterialFinishSubscribers(state.liveryPreviewMaterialFinish, { source: "previewProjectApply" });
  renderLiveryPreviewTextureSlotMenu(data);
  syncLiveryPreviewModeButtons();
  renderWorkflowCompatibility();
}

function renderPreview3dStatus(data) {
  const status = $("preview3dStatus");
  if (!status) return;
  const model = data?.model || {};
  const auxiliaryModels = data?.auxiliaryModels || [];
  const texture = data?.texture || {};
  const warnings = data?.warnings || [];
  const lines = [
    `Model: ${model.path || "-"} ${model.exists ? "(found)" : "(missing)"}`,
    `Aux models: ${auxiliaryModels.filter((row) => row.exists).length}/${auxiliaryModels.length}`,
    `Texture: ${texture.path || "-"} ${texture.exists ? "(found)" : "(missing)"}`,
    `AO: ${data?.aoTexture?.path || data?.project?.aoTexturePath || data?.project?.liveAoTexturePath || "-"} ${data?.aoTexture?.exists ? "(found)" : "(optional)"}`,
    `Detail/Splat: ${data?.detailTexture?.path || data?.project?.detailTexturePath || data?.project?.liveDetailTexturePath || "-"} ${data?.detailTexture?.exists ? "(found)" : "(optional)"}`,
    `Banner: ${data?.bannerTexture?.path || data?.project?.bannerTexturePath || data?.project?.liveBannerTexturePath || "-"} ${data?.bannerTexture?.exists ? "(found)" : "(optional)"}`,
    `Material: ${data?.project?.materialName || "EXT_SKIN"}`,
    `Texture mtime: ${texture.mtime ? new Date(texture.mtime * 1000).toLocaleString() : "-"}`,
    `Ready for renderer: ${data?.ready ? "yes" : "no"}`,
  ];
  if (warnings.length) {
    lines.push("", "Warnings:", ...warnings.map((row) => `- ${row}`));
  }
  status.textContent = lines.join("\n");
}

function nextPreview3dViewerLoadToken() {
  const loadToken = (state.preview3dViewerLoadToken || 0) + 1;
  state.preview3dViewerLoadToken = loadToken;
  return loadToken;
}

function cancelPreview3dViewerLoad(options = {}) {
  nextPreview3dViewerLoadToken();
  window.LiveryLabPreview3d?.clear?.({
    emptyId: options.emptyId || "liveryPreviewEmpty",
  });
}

function liveryPreviewWorkspaceActive() {
  return Boolean(state.liveryPreviewOpening || $("liveryPreviewWorkspace")?.hidden === false);
}

async function resetPreview3dViewerForRetry(options = {}) {
  window.LiveryLabPreview3d?.clear?.({
    emptyId: options.emptyId || "liveryPreviewEmpty",
  });
  window.LiveryLabPreview3d?.resize?.();
  for (let index = 0; index < 3; index += 1) {
    await new Promise((resolve) => window.requestAnimationFrame(resolve));
    window.LiveryLabPreview3d?.resize?.();
  }
}

function preview3dStepLabel(options = {}) {
  return options.stepLabel || options.statusPrefix || "3D preview";
}

function setPreview3dStepStatus(status, options, message) {
  if (!status) return;
  status.textContent = `${preview3dStepLabel(options)}\n${message}`;
}

function isVariantPartAuxiliaryModel(row) {
  if (!row || typeof row !== "object") return false;
  const kind = String(row.kind || "").trim().toLowerCase();
  const label = String(row.label || "").trim().toLowerCase();
  return kind === "variantpart" || label.startsWith("part_");
}

function stripPreviewAuxiliaryModels(project = {}, options = {}) {
  const auxiliaryModels = Array.isArray(project.auxiliaryModels) ? project.auxiliaryModels : [];
  const keptModels = options.keepVariantParts
    ? auxiliaryModels.filter(isVariantPartAuxiliaryModel)
    : [];
  return {
    ...project,
    auxiliaryModelPaths: keptModels.map((row) => String(row.path || "").trim()).filter(Boolean),
    auxiliaryModels: keptModels,
    wheelMaterials: [],
  };
}

function layerStudioPreviewProjectPayload(project = {}) {
  const surfaceKey = String(project.layerStudioSurfaceKey || "").trim().toLowerCase();
  const specParts = surfaceKey === "ext_skin_c_spec_parts";
  const policy = layerStudioSurfacePolicy(project);
  const modelPolicy = String(policy?.modelPolicy || "").trim().toLowerCase();
  const variantModelSet = modelPolicy === "variant_model_set";
  return {
    ...project,
    hideExteriorModel: specParts || variantModelSet ? true : false,
  };
}

function layerStudioSurfacePolicy(project = {}) {
  const surfaceKey = String(project.layerStudioSurfaceKey || "ext_skin_c").trim().toLowerCase();
  const policies = Array.isArray(project.surfacePolicies) ? project.surfacePolicies : [];
  return policies.find((row) => {
    const key = String(row?.key || "").trim().toLowerCase();
    return key && key === surfaceKey;
  }) || null;
}

function layerStudioShouldShowVariantPartAuxiliaries(project = {}) {
  const surfaceKey = String(project.layerStudioSurfaceKey || "").trim().toLowerCase();
  if (surfaceKey === "ext_skin_c_spec_parts") return true;
  const classification = String(project.variantPolicy?.classification || project.variantClassification || "").trim().toLowerCase();
  if (classification === "hybrid_replacement_parts") return true;
  const variantPreview = project.variantPartPreview && typeof project.variantPartPreview === "object"
    ? project.variantPartPreview
    : {};
  const skinMeshRefs = Array.isArray(variantPreview.skinMeshRefs) ? variantPreview.skinMeshRefs : [];
  if (skinMeshRefs.some((row) => row && typeof row === "object" && row.previewRelevant !== false)) return true;
  const policy = layerStudioSurfacePolicy(project);
  const modelPolicy = String(policy?.modelPolicy || "").trim().toLowerCase();
  return modelPolicy === "base_plus_additive_parts" || modelPolicy === "variant_model_set";
}

async function withPreview3dTimeout(promise, timeoutMs, message) {
  const ms = Number(timeoutMs || 0);
  if (!Number.isFinite(ms) || ms <= 0) return await promise;
  let timeoutId = 0;
  try {
    return await Promise.race([
      promise,
      new Promise((_, reject) => {
        timeoutId = window.setTimeout(() => reject(new Error(message)), ms);
      }),
    ]);
  } finally {
    if (timeoutId) window.clearTimeout(timeoutId);
  }
}

async function loadPreview3dViewer(options = {}) {
  const status = $(options.statusId || "preview3dStatus");
  const canvasId = options.canvasId || "preview3dCanvas";
  const minCanvasWidth = Number(options.minCanvasWidth || 20);
  const minCanvasHeight = Number(options.minCanvasHeight || 20);
  const loadToken = nextPreview3dViewerLoadToken();
  const isCurrentLoad = () => state.preview3dViewerLoadToken === loadToken;
  const ready = await waitForPreview3dViewerReady(canvasId, status, {
    minWidth: minCanvasWidth,
    minHeight: minCanvasHeight,
    stableFramesRequired: Number(options.stableFramesRequired || 4),
  });
  if (!ready || !isCurrentLoad()) return false;
  setLiveryPreviewWatchBadge("Preparing 3D view", "loading");
  setPreview3dStepStatus(status, options, "Loading 3D preview...");
  try {
    const baseProjectPayload = payloadPreview3dProject();
    let projectPayload = options.projectOverride && typeof options.projectOverride === "object"
      ? { ...baseProjectPayload, ...options.projectOverride }
      : baseProjectPayload;
    const initialSkinKey = projectPayload.skinKey || state.liveryPreviewSkinKey || "";
    if (window.pywebview?.api?.inspect_preview3d_project) {
      setLiveryPreviewWatchBadge("Inspecting preview project", "loading");
      setPreview3dStepStatus(status, options, "Inspecting preview project...");
      const inspectResult = await window.pywebview.api.inspect_preview3d_project(projectPayload);
      if (inspectResult?.ok && inspectResult.data?.project) {
        projectPayload = { ...projectPayload, ...inspectResult.data.project };
        state.liveryPreviewProject = { ...(state.liveryPreviewProject || {}), ...inspectResult.data.project };
        renderLiveryPreviewTextureSlotMenu(state.liveryPreviewProject);
      }
    }
    const buildSkinKey = projectPayload.skinKey || state.liveryPreviewSkinKey || initialSkinKey;
    if (options.buildPreviewModel !== false && buildSkinKey && window.pywebview?.api?.build_livery_preview_model) {
      setLiveryPreviewWatchBadge("Building model and wheels", "loading");
      setPreview3dStepStatus(status, options, "Building model and wheel preview data...");
      const buildResult = await window.pywebview.api.build_livery_preview_model({
        skinKey: buildSkinKey,
        root: state.root || $("rootPath")?.value.trim() || "",
        force: Boolean(options.forceWheelBuild),
        wheelPlacementOverrides: $("liveryPreviewUseWheelPlacementOverrides")?.checked !== false,
      });
      const builtProject = buildResult?.data?.project || {};
      if (buildResult?.ok) {
        state.liveryPreviewWheelPreview = buildResult.data?.wheelPreview || null;
        renderLiveryPreviewWheelDiagnostics();
      }
      if (buildResult?.ok) {
        projectPayload = { ...projectPayload, ...builtProject };
        state.liveryPreviewProject = { ...(state.liveryPreviewProject || {}), ...builtProject };
        renderLiveryPreviewTextureSlotMenu(state.liveryPreviewProject);
      }
    }
    setPreview3dStepStatus(status, options, "Detecting material/base color...");
    const detectedBaseColor = await detectDefaultLiveryPreviewBodyBaseColor(projectPayload);
    if (detectedBaseColor?.color) {
      projectPayload = {
        ...projectPayload,
        bodyBaseColor: detectedBaseColor.color,
      };
    }
    const keepVariantPartsOnly = options.includeAuxiliaryModels === "variantParts";
    if (options.includeAuxiliaryModels === false || keepVariantPartsOnly) {
      projectPayload = stripPreviewAuxiliaryModels(projectPayload, { keepVariantParts: keepVariantPartsOnly });
      setPreview3dStepStatus(
        status,
        options,
        keepVariantPartsOnly
          ? "Loading renderer, model and spec parts without wheels/tyres..."
          : "Loading renderer, model and texture without wheels/tyres...",
      );
    }
    setLiveryPreviewWatchBadge("Loading renderer", "loading");
    if (options.includeAuxiliaryModels !== false) {
      setPreview3dStepStatus(status, options, "Loading renderer, model and textures...");
    }
    const viewer = await withPreview3dTimeout(
      window.LiveryLabPreview3d.load({
        ...projectPayload,
        canvasId,
      emptyId: options.emptyId || "preview3dViewerEmpty",
      statusId: options.statusId || "",
      statusPrefix: preview3dStepLabel(options),
      auxiliaryTimeoutMs: options.auxiliaryTimeoutMs || 8000,
      cameraPreset: options.cameraPreset || state.liveryPreviewCameraPreset || "free",
    }),
      options.loadTimeoutMs || 30000,
      "3D renderer load timed out while loading model or textures",
    );
    if (!isCurrentLoad()) return false;
    await settlePreview3dRender(canvasId);
    if (!isCurrentLoad()) return false;
    const stability = preview3dLoadStability(viewer, canvasId, {
      minWidth: minCanvasWidth,
      minHeight: minCanvasHeight,
    });
    if (!stability.ok) {
      if (options.retryOnUnstable !== false) {
        setLiveryPreviewWatchBadge("Retrying preview", "loading");
        if (status) status.textContent = `Retrying 3D preview load...\n${stability.reason}`;
        await resetPreview3dViewerForRetry({ emptyId: options.emptyId || "preview3dViewerEmpty" });
        await new Promise((resolve) => window.setTimeout(resolve, 320));
        if (!isCurrentLoad()) return false;
        return await loadPreview3dViewer({
          ...options,
          retryOnUnstable: false,
        });
      }
      throw new Error(stability.reason || "3D preview did not finish loading a visible model");
    }
    const exteriorCheckbox = $("liveryPreviewShowExterior");
    if (projectPayload.hideExteriorModel === true && exteriorCheckbox && options.syncExteriorCheckbox !== false) {
      exteriorCheckbox.checked = false;
    }
    const exteriorVisible = projectPayload.hideExteriorModel === true
      ? false
      : exteriorCheckbox?.checked !== false;
    window.LiveryLabPreview3d?.setModelVisible?.(exteriorVisible);
    window.LiveryLabPreview3d?.setAuxiliaryVisible?.($("liveryPreviewShowRims")?.checked !== false);
    window.LiveryLabPreview3d?.setFloorVisible?.($("liveryPreviewShowFloor")?.checked !== false);
    const renderLoadedStatus = (wheelResult = null) => {
      if (!isCurrentLoad()) return;
      if (!status) return;
      const wheelPreview = state.liveryPreviewWheelPreview || {};
      const calibration = wheelPreview.layout?.calibration || {};
      const wheelSource = wheelPreview.cardesignSource ? `Wheel cardesign source: ${wheelPreview.cardesignSource}` : "";
      const calibrationLine = calibration.car
        ? `Wheel calibration: ${calibration.car} (${Object.keys(calibration.wheelOverrides || {}).length} overrides)`
        : "Wheel calibration: none";
      status.textContent = [
        "3D preview loaded",
        `Model: ${viewer.modelPath || "-"}`,
        `Preview mode: ${liveryPreviewModeLabel()}`,
        `Texture: ${viewer.texturePath || "-"}`,
        ...liveryPreviewAoStatusLines(viewer),
        `Banner: ${viewer.bannerTexturePath || "-"}`,
        `Material: ${viewer.materialName || "EXT_SKIN"}`,
        `Wheels/tyres: ${viewer.auxiliaryModelCount ?? 0}`,
        wheelResult
          ? `Wheel materials: ${wheelResult.wheelTexturedMaterials ?? 0}/${wheelResult.wheelMaterials ?? 0} (${wheelResult.wheelDetailMaps ?? 0} detail maps)`
          : "Wheel materials: loading after exterior...",
        wheelSource,
        calibrationLine,
        `Textured materials: ${viewer.appliedMaterials}`,
        `Glass/lights materials: ${viewer.specialMaterials ?? 0}`,
        `Meshes: ${viewer.meshCount ?? "-"}`,
        `Canvas: ${stability.health?.canvasWidth || viewer.canvasSize?.width || "-"} x ${stability.health?.canvasHeight || viewer.canvasSize?.height || "-"}`,
      ].filter(Boolean).join("\n");
    };
    renderLoadedStatus(null);
    const wheelResult = await applyPreviewWheelMaterialsAfterLoad(projectPayload, viewer);
    if (!isCurrentLoad()) return false;
    await syncLiveryPreviewSceneMaterialLookMode({ quiet: true });
    if (!isCurrentLoad()) return false;
    renderLoadedStatus(wheelResult);
    setLiveryPreviewWatchBadge("Starting watcher", "loading");
    renderLiveryPreviewWheelDiagnostics();
    return true;
  } catch (error) {
    if (/Preview load was superseded/i.test(String(error?.message || error))) {
      return false;
    }
    if (isCurrentLoad()) {
      setLiveryPreviewWatchBadge("Preview failed", "error");
      if (status) status.textContent = `3D preview failed:\n${error?.message || error}`;
      log(error?.message || String(error), true);
    }
    return false;
  }
}

function previewCanvasMetrics(canvasId) {
  const canvas = $(canvasId);
  const rect = canvas?.getBoundingClientRect?.();
  const width = rect ? Math.floor(rect.width) : 0;
  const height = rect ? Math.floor(rect.height) : 0;
  return {
    canvas,
    width,
    height,
    ready: Boolean(canvas && width >= 20 && height >= 20),
  };
}

async function settlePreview3dRender(canvasId) {
  let metrics = previewCanvasMetrics(canvasId);
  for (let index = 0; index < 5; index += 1) {
    await new Promise((resolve) => window.requestAnimationFrame(resolve));
    window.LiveryLabPreview3d?.resize?.();
    metrics = previewCanvasMetrics(canvasId);
  }
  return metrics.ready;
}

function preview3dLoadStability(viewer, canvasId, options = {}) {
  const minWidth = Number(options.minWidth || 20);
  const minHeight = Number(options.minHeight || 20);
  const metrics = previewCanvasMetrics(canvasId);
  if (!metrics.ready || metrics.width < minWidth || metrics.height < minHeight) {
    return { ok: false, reason: "3D canvas did not keep a usable size after loading" };
  }
  const health = window.LiveryLabPreview3d?.getHealth?.() || window.LiveryLabPreview3d?.hasLoadedModel?.() || {};
  if (health.loaded !== true) {
    return { ok: false, reason: "3D viewer did not report a loaded model", health };
  }
  const meshCount = Number(health.meshCount ?? viewer?.meshCount ?? 0);
  if (!Number.isFinite(meshCount) || meshCount <= 0) {
    return { ok: false, reason: "3D preview model loaded without mesh data", health };
  }
  const visibleMeshCount = Number(health.visibleMeshCount ?? meshCount);
  if (!Number.isFinite(visibleMeshCount) || visibleMeshCount <= 0) {
    return { ok: false, reason: "3D preview model loaded without visible mesh data", health };
  }
  if (health.boundsValid === false) {
    return { ok: false, reason: "3D preview model bounds are not ready", health };
  }
  if (health.frustumIntersects === false) {
    return { ok: false, reason: "3D preview camera is not framing the model", health };
  }
  if (Number(health.drawingBufferWidth || 0) < minWidth || Number(health.drawingBufferHeight || 0) < minHeight) {
    return { ok: false, reason: "3D renderer drawing buffer is not ready", health };
  }
  return { ok: true, reason: "", health };
}

async function waitForPreview3dViewerReady(canvasId, status, options = {}) {
  const started = performance.now();
  let lastReason = "";
  let stableFrames = 0;
  let lastSize = null;
  const minWidth = Number(options.minWidth || 20);
  const minHeight = Number(options.minHeight || 20);
  const stableFramesRequired = Number(options.stableFramesRequired || 4);
  while (performance.now() - started < 5000) {
    const metrics = previewCanvasMetrics(canvasId);
    if (!window.LiveryLabPreview3d?.load) {
      lastReason = "3D viewer module is not ready yet";
      stableFrames = 0;
    } else if (!metrics.canvas) {
      lastReason = "3D canvas is missing";
      stableFrames = 0;
    } else if (!metrics.ready || metrics.width < minWidth || metrics.height < minHeight) {
      lastReason = "3D canvas is not ready yet";
      stableFrames = 0;
    } else {
      const stableSize = lastSize
        && Math.abs(metrics.width - lastSize.width) <= 2
        && Math.abs(metrics.height - lastSize.height) <= 2;
      stableFrames = stableSize ? stableFrames + 1 : 1;
      lastSize = { width: metrics.width, height: metrics.height };
      if (stableFrames >= stableFramesRequired) return true;
      lastReason = "3D canvas is stabilizing";
    }
    if (status) status.textContent = `${lastReason}...\nWaiting for preview startup.`;
    await new Promise((resolve) => window.requestAnimationFrame(resolve));
  }
  if (status) status.textContent = `3D preview failed:\n${lastReason}`;
  log(lastReason, true);
  return false;
}

function setLiveryPreviewCameraPreset(preset, options = {}) {
  const next = String(preset || "free");
  state.liveryPreviewCameraPreset = next;
  document.querySelectorAll("[data-camera-preset]").forEach((button) => {
    button.classList.toggle("active", button.dataset.cameraPreset === next);
  });
  const result = window.LiveryLabPreview3d?.setCameraPreset?.(next);
  const status = $("liveryPreviewStatus");
  if (!options.quiet && status && result) {
    status.textContent = [
      `Camera: ${buttonLabelForCameraPreset(result.preset)}`,
      result.locked ? "Rotation locked. Pan and zoom are enabled." : "Free orbit enabled.",
    ].join("\n");
  }
}

function clonePreviewJson(value) {
  return JSON.parse(JSON.stringify(value || {}));
}

function previewSceneName(value) {
  const scene = String(value || "").toLowerCase();
  return ["light", "dark", "studio"].includes(scene) ? scene : "light";
}

function liveryPreviewUserSceneName(value) {
  const scene = String(value || "").toLowerCase();
  return ["light", "dark", "studio"].includes(scene) ? scene : "light";
}

function liveryPreviewSceneLabel(sceneName) {
  const scene = previewSceneName(sceneName);
  if (scene === "studio") return "Studio";
  return scene === "dark" ? "Dark" : "Light";
}

function mergePreviewSceneConfig(base = {}, override = {}) {
  const result = { ...(base || {}) };
  Object.entries(override || {}).forEach(([key, value]) => {
    if (value && typeof value === "object" && !Array.isArray(value) && result[key] && typeof result[key] === "object" && !Array.isArray(result[key])) {
      result[key] = mergePreviewSceneConfig(result[key], value);
    } else {
      result[key] = Array.isArray(value) ? [...value] : value;
    }
  });
  return result;
}

function normalizePreviewSceneSettings(data = {}) {
  const source = data?.settings && typeof data.settings === "object" ? data.settings : data;
  const scenes = source?.scenes && typeof source.scenes === "object" ? source.scenes : source || {};
  return {
    default: previewSceneName(source?.default || "light"),
    scenes: {
      light: clonePreviewJson(scenes.light || {}),
      dark: clonePreviewJson(scenes.dark || {}),
      studio: clonePreviewJson(scenes.studio || {}),
    },
  };
}

function previewSceneSettingsFor(sceneName) {
  const scene = previewSceneName(sceneName);
  const settings = state.liveryPreviewSceneSettings || normalizePreviewSceneSettings(window.LiveryLabPreview3d?.getSceneConfigs?.() || {});
  return settings.scenes?.[scene] || {};
}

function previewSceneDefaultsFor(sceneName) {
  const scene = previewSceneName(sceneName);
  const defaults = state.liveryPreviewSceneDefaults || normalizePreviewSceneSettings(window.LiveryLabPreview3d?.getDefaultSceneConfigs?.() || {});
  return defaults.scenes?.[scene] || {};
}

function previewScenePanelScene() {
  return previewSceneName($("liveryPreviewSceneSettingsScene")?.value || state.liveryPreviewScene || "light");
}

function currentLiveryPreviewMaterialLook(sceneName = state.liveryPreviewScene || "light") {
  const scene = previewSceneName(sceneName);
  const config = mergePreviewSceneConfig(previewSceneDefaultsFor(scene), previewSceneSettingsFor(scene));
  return config.materialLook || {};
}

function setPreviewSceneNumber(id, value) {
  const input = $(id);
  if (input) input.value = Number.isFinite(Number(value)) ? String(Math.round(Number(value) * 100) / 100) : "0";
}

function setPreviewScenePreciseNumber(id, value, digits = 4) {
  const input = $(id);
  if (!input) return;
  const number = Number(value);
  input.value = Number.isFinite(number) ? String(Math.round(number * (10 ** digits)) / (10 ** digits)) : "0";
}

function previewSceneVector(config, key, fallback = [0, 0, 0]) {
  const value = config?.[key]?.position;
  return Array.isArray(value) && value.length >= 3 ? value : fallback;
}

function renderLiveryPreviewSceneSettingsPanel() {
  const scene = previewScenePanelScene();
  const config = mergePreviewSceneConfig(previewSceneDefaultsFor(scene), previewSceneSettingsFor(scene));
  if ($("liveryPreviewSceneSettingsScene")) $("liveryPreviewSceneSettingsScene").value = scene;
  if ($("liveryPreviewSceneBackground")) $("liveryPreviewSceneBackground").value = typeof config.background === "string" ? config.background : "#101113";
  if ($("liveryPreviewSceneFloorColor")) $("liveryPreviewSceneFloorColor").value = config.floor?.color || "#101113";
  if ($("liveryPreviewSceneFloorVisible")) $("liveryPreviewSceneFloorVisible").checked = config.floor?.visible !== false;
  setPreviewSceneNumber("liveryPreviewSceneExposure", config.renderer?.exposure ?? 1);
  setPreviewSceneNumber("liveryPreviewSceneHemiIntensity", config.hemi?.intensity ?? 1);
  setPreviewSceneNumber("liveryPreviewSceneKeyIntensity", config.key?.intensity ?? 1);
  setPreviewSceneNumber("liveryPreviewSceneFillIntensity", config.fill?.intensity ?? 0.5);
  setPreviewSceneNumber("liveryPreviewSceneRimIntensity", config.rim?.intensity ?? 1);
  if ($("liveryPreviewSceneSoftShadowVisible")) $("liveryPreviewSceneSoftShadowVisible").checked = config.contactShadow?.visible !== false;
  setPreviewSceneNumber("liveryPreviewSceneContactShadow", config.contactShadow?.opacity ?? 0.4);
  const realShadow = config.realShadow || {};
  if ($("liveryPreviewSceneRealShadowEnabled")) $("liveryPreviewSceneRealShadowEnabled").checked = Boolean(realShadow.enabled);
  setPreviewSceneNumber("liveryPreviewSceneRealShadowOpacity", realShadow.opacity ?? 0.34);
  setPreviewSceneNumber("liveryPreviewSceneRealShadowRadius", realShadow.radius ?? 5);
  setPreviewScenePreciseNumber("liveryPreviewSceneRealShadowBias", realShadow.bias ?? -0.0002, 4);
  setPreviewScenePreciseNumber("liveryPreviewSceneRealShadowNormalBias", realShadow.normalBias ?? 0.02, 3);
  setPreviewSceneNumber("liveryPreviewSceneRealShadowMapSize", realShadow.mapSize ?? 2048);
  setPreviewSceneNumber("liveryPreviewSceneRealShadowCameraSize", realShadow.cameraSize ?? 8);
  const environment = config.environment || {};
  if ($("liveryPreviewSceneEnvironmentEnabled")) $("liveryPreviewSceneEnvironmentEnabled").checked = environment.enabled !== false;
  setPreviewSceneNumber("liveryPreviewSceneEnvironmentIntensity", environment.intensity ?? config.materialLook?.environmentIntensity ?? 0);
  const keyPosition = previewSceneVector(config, "key", [4.8, 6.5, 5.2]);
  const fillPosition = previewSceneVector(config, "fill", [-5.2, 3.1, -3.8]);
  const rimPosition = previewSceneVector(config, "rim", [-3.8, 4.8, 6.5]);
  setPreviewSceneNumber("liveryPreviewSceneKeyX", keyPosition[0]);
  setPreviewSceneNumber("liveryPreviewSceneKeyY", keyPosition[1]);
  setPreviewSceneNumber("liveryPreviewSceneKeyZ", keyPosition[2]);
  setPreviewSceneNumber("liveryPreviewSceneFillX", fillPosition[0]);
  setPreviewSceneNumber("liveryPreviewSceneFillY", fillPosition[1]);
  setPreviewSceneNumber("liveryPreviewSceneFillZ", fillPosition[2]);
  setPreviewSceneNumber("liveryPreviewSceneRimX", rimPosition[0]);
  setPreviewSceneNumber("liveryPreviewSceneRimY", rimPosition[1]);
  setPreviewSceneNumber("liveryPreviewSceneRimZ", rimPosition[2]);
  const look = config.materialLook || {};
  if ($("liveryPreviewSceneEvoApprox")) $("liveryPreviewSceneEvoApprox").checked = Boolean(look.evoApproxEnabled);
  setPreviewSceneNumber("liveryPreviewSceneMaskThreshold", look.maskThreshold ?? 0.08);
  setPreviewSceneNumber("liveryPreviewScenePaintBrightness", look.paintBrightness ?? 1);
  setPreviewSceneNumber("liveryPreviewScenePaintSpecular", look.paintSpecular ?? 0.35);
  setPreviewSceneNumber("liveryPreviewScenePaintLightInfluence", look.paintLightInfluence ?? 0);
  setPreviewSceneNumber("liveryPreviewScenePaintRimStrength", look.paintRimStrength ?? 0.012);
  setPreviewSceneNumber("liveryPreviewScenePaintHighlightStrength", look.paintHighlightStrength ?? 0.025);
  setPreviewSceneNumber("liveryPreviewScenePaintReflectionStrength", look.paintReflectionStrength ?? 0.32);
  setPreviewSceneNumber("liveryPreviewScenePaintStripStrength", look.paintStripStrength ?? 0.72);
  setPreviewSceneNumber("liveryPreviewScenePaintStripSharpness", look.paintStripSharpness ?? 24);
  if ($("liveryPreviewScenePaintToneMapped")) $("liveryPreviewScenePaintToneMapped").checked = Boolean(look.paintToneMapped);
  setPreviewSceneNumber("liveryPreviewSceneCarbonScale", look.carbonScale ?? 18);
  setPreviewSceneNumber("liveryPreviewSceneCarbonBrightness", look.carbonBrightness ?? 1);
  if ($("liveryPreviewSceneMatteColor")) $("liveryPreviewSceneMatteColor").value = look.matteColor || "#15181b";
  setPreviewSceneNumber("liveryPreviewSceneGlassOpacity", look.glassOpacity ?? 0.82);
  setPreviewSceneNumber("liveryPreviewSceneLightEmissive", look.lightEmissive ?? 1);
  setPreviewSceneNumber("liveryPreviewSceneRimMetalness", look.rimMetalness ?? 0.58);
  setPreviewSceneNumber("liveryPreviewSceneTyreRoughness", look.tyreRoughness ?? 0.7);
  const path = $("liveryPreviewSceneSettingsPath");
  if (path) path.textContent = state.liveryPreviewSceneSettingsPath || "LiveryLibrary/Settings/preview3d_scenes.json";
}

function liveryPreviewSceneConfigFromInputs(sceneName) {
  const scene = previewSceneName(sceneName);
  const base = mergePreviewSceneConfig(previewSceneDefaultsFor(scene), previewSceneSettingsFor(scene));
  const number = (id, fallback, lo, hi) => {
    const raw = Number($(id)?.value);
    const value = Number.isFinite(raw) ? raw : fallback;
    return Math.max(lo, Math.min(hi, value));
  };
  return mergePreviewSceneConfig(base, {
    renderer: { exposure: number("liveryPreviewSceneExposure", base.renderer?.exposure ?? 1, 0.1, 4) },
    background: $("liveryPreviewSceneBackground")?.value || base.background || "#101113",
    floor: {
      color: $("liveryPreviewSceneFloorColor")?.value || base.floor?.color || "#101113",
      visible: $("liveryPreviewSceneFloorVisible")?.checked !== false,
    },
    contactShadow: {
      visible: $("liveryPreviewSceneSoftShadowVisible")?.checked !== false,
      opacity: number("liveryPreviewSceneContactShadow", base.contactShadow?.opacity ?? 0.4, 0, 1),
      scaleX: base.contactShadow?.scaleX ?? 1,
      scaleZ: base.contactShadow?.scaleZ ?? 1,
    },
    realShadow: {
      enabled: $("liveryPreviewSceneRealShadowEnabled")?.checked === true,
      opacity: number("liveryPreviewSceneRealShadowOpacity", base.realShadow?.opacity ?? 0.34, 0, 1),
      radius: number("liveryPreviewSceneRealShadowRadius", base.realShadow?.radius ?? 5, 0, 20),
      bias: number("liveryPreviewSceneRealShadowBias", base.realShadow?.bias ?? -0.0002, -0.01, 0.01),
      normalBias: number("liveryPreviewSceneRealShadowNormalBias", base.realShadow?.normalBias ?? 0.02, 0, 0.2),
      mapSize: number("liveryPreviewSceneRealShadowMapSize", base.realShadow?.mapSize ?? 2048, 256, 4096),
      cameraSize: number("liveryPreviewSceneRealShadowCameraSize", base.realShadow?.cameraSize ?? 8, 1, 40),
    },
    environment: {
      enabled: $("liveryPreviewSceneEnvironmentEnabled")?.checked === true,
      intensity: number("liveryPreviewSceneEnvironmentIntensity", base.environment?.intensity ?? base.materialLook?.environmentIntensity ?? 0, 0, 4),
    },
    hemi: { intensity: number("liveryPreviewSceneHemiIntensity", base.hemi?.intensity ?? 1, 0, 4) },
    key: {
      intensity: number("liveryPreviewSceneKeyIntensity", base.key?.intensity ?? 1, 0, 6),
      position: [
        number("liveryPreviewSceneKeyX", previewSceneVector(base, "key", [4.8, 6.5, 5.2])[0], -20, 20),
        number("liveryPreviewSceneKeyY", previewSceneVector(base, "key", [4.8, 6.5, 5.2])[1], -20, 20),
        number("liveryPreviewSceneKeyZ", previewSceneVector(base, "key", [4.8, 6.5, 5.2])[2], -20, 20),
      ],
    },
    fill: {
      intensity: number("liveryPreviewSceneFillIntensity", base.fill?.intensity ?? 0.5, 0, 6),
      position: [
        number("liveryPreviewSceneFillX", previewSceneVector(base, "fill", [-5.2, 3.1, -3.8])[0], -20, 20),
        number("liveryPreviewSceneFillY", previewSceneVector(base, "fill", [-5.2, 3.1, -3.8])[1], -20, 20),
        number("liveryPreviewSceneFillZ", previewSceneVector(base, "fill", [-5.2, 3.1, -3.8])[2], -20, 20),
      ],
    },
    rim: {
      intensity: number("liveryPreviewSceneRimIntensity", base.rim?.intensity ?? 1, 0, 6),
      position: [
        number("liveryPreviewSceneRimX", previewSceneVector(base, "rim", [-3.8, 4.8, 6.5])[0], -20, 20),
        number("liveryPreviewSceneRimY", previewSceneVector(base, "rim", [-3.8, 4.8, 6.5])[1], -20, 20),
        number("liveryPreviewSceneRimZ", previewSceneVector(base, "rim", [-3.8, 4.8, 6.5])[2], -20, 20),
      ],
    },
    materialLook: {
      evoApproxEnabled: $("liveryPreviewSceneEvoApprox")?.checked === true,
      maskThreshold: number("liveryPreviewSceneMaskThreshold", base.materialLook?.maskThreshold ?? 0.08, 0, 0.5),
      paintBrightness: number("liveryPreviewScenePaintBrightness", base.materialLook?.paintBrightness ?? 1, 0.2, 2.5),
      paintSpecular: number("liveryPreviewScenePaintSpecular", base.materialLook?.paintSpecular ?? 0.35, 0, 3),
      paintLightInfluence: number("liveryPreviewScenePaintLightInfluence", base.materialLook?.paintLightInfluence ?? 0, 0, 1),
      paintRimStrength: number("liveryPreviewScenePaintRimStrength", base.materialLook?.paintRimStrength ?? 0.012, 0, 0.25),
      paintHighlightStrength: number("liveryPreviewScenePaintHighlightStrength", base.materialLook?.paintHighlightStrength ?? 0.025, 0, 0.5),
      environmentIntensity: number("liveryPreviewSceneEnvironmentIntensity", base.materialLook?.environmentIntensity ?? base.environment?.intensity ?? 0, 0, 4),
      paintReflectionStrength: number("liveryPreviewScenePaintReflectionStrength", base.materialLook?.paintReflectionStrength ?? 0.32, 0, 2),
      paintStripStrength: number("liveryPreviewScenePaintStripStrength", base.materialLook?.paintStripStrength ?? 0.72, 0, 3),
      paintStripSharpness: number("liveryPreviewScenePaintStripSharpness", base.materialLook?.paintStripSharpness ?? 24, 2, 96),
      paintToneMapped: $("liveryPreviewScenePaintToneMapped")?.checked === true,
      carbonScale: number("liveryPreviewSceneCarbonScale", base.materialLook?.carbonScale ?? 18, 1, 80),
      carbonBrightness: number("liveryPreviewSceneCarbonBrightness", base.materialLook?.carbonBrightness ?? 1, 0.2, 2.5),
      matteColor: $("liveryPreviewSceneMatteColor")?.value || base.materialLook?.matteColor || "#15181b",
      glassOpacity: number("liveryPreviewSceneGlassOpacity", base.materialLook?.glassOpacity ?? 0.82, 0.05, 1),
      lightEmissive: number("liveryPreviewSceneLightEmissive", base.materialLook?.lightEmissive ?? 1, 0, 4),
      rimMetalness: number("liveryPreviewSceneRimMetalness", base.materialLook?.rimMetalness ?? 0.58, 0, 1),
      tyreRoughness: number("liveryPreviewSceneTyreRoughness", base.materialLook?.tyreRoughness ?? 0.7, 0.05, 1),
    },
  });
}

async function loadLiveryPreviewSceneSettings(force = false) {
  if (state.liveryPreviewSceneSettings && !force) {
    window.LiveryLabPreview3d?.setSceneConfigs?.(state.liveryPreviewSceneSettings.scenes || {});
    return state.liveryPreviewSceneSettings;
  }
  let settings = null;
  let defaults = null;
  let path = "";
  if (window.pywebview?.api?.load_preview3d_scene_settings) {
    const result = await window.pywebview.api.load_preview3d_scene_settings();
    if (result?.ok) {
      settings = normalizePreviewSceneSettings(result.data?.settings || {});
      defaults = normalizePreviewSceneSettings(result.data?.defaults || {});
      path = result.data?.path || "";
    }
  }
  if (!settings) settings = normalizePreviewSceneSettings(window.LiveryLabPreview3d?.getSceneConfigs?.() || {});
  if (!defaults) defaults = normalizePreviewSceneSettings(window.LiveryLabPreview3d?.getDefaultSceneConfigs?.() || settings);
  state.liveryPreviewSceneSettings = settings;
  state.liveryPreviewSceneDefaults = defaults;
  state.liveryPreviewSceneSettingsPath = path;
  window.LiveryLabPreview3d?.setSceneConfigs?.(settings.scenes || {});
  return settings;
}

function applyLiveryPreviewSceneSettingsFromInputs(options = {}) {
  const scene = previewScenePanelScene();
  const settings = state.liveryPreviewSceneSettings || normalizePreviewSceneSettings(window.LiveryLabPreview3d?.getSceneConfigs?.() || {});
  settings.scenes[scene] = liveryPreviewSceneConfigFromInputs(scene);
  state.liveryPreviewSceneSettings = settings;
  state.liveryPreviewSceneSettingsDirty = options.dirty !== false;
  window.LiveryLabPreview3d?.setSceneConfig?.(scene, settings.scenes[scene]);
  if (state.liveryPreviewScene !== scene) {
    setLiveryPreviewScene(scene, { quiet: true });
  }
  syncLiveryPreviewSceneMaterialLookMode({ quiet: true, sceneName: scene });
  const info = $("liveryPreviewSceneSettingsInfo");
  if (info) info.textContent = state.liveryPreviewSceneSettingsDirty ? "Unsaved scene settings. Save to write the scene entries into the JSON." : "Scene settings are up to date.";
}

async function syncLiveryPreviewSceneMaterialLookMode(options = {}) {
  if ($("liveryPreviewWorkspace")?.hidden || !window.LiveryLabPreview3d?.setApproxMaterialOverlay) return false;
  if (window.LiveryLabPreview3d?.hasLoadedModel && !window.LiveryLabPreview3d.hasLoadedModel().loaded) return false;
  if (normalizeLiveryPreviewMode(state.liveryPreviewPreviewMode) !== "evoApprox") {
    await setLiveryPreviewApproxMaterials(false, { quiet: options.quiet !== false });
    return false;
  }
  return await setLiveryPreviewApproxMaterials(true, { quiet: options.quiet !== false, sceneName: options.sceneName });
}

async function saveLiveryPreviewSceneSettings() {
  applyLiveryPreviewSceneSettingsFromInputs({ dirty: true });
  if (!window.pywebview?.api?.save_preview3d_scene_settings) return;
  const result = await window.pywebview.api.save_preview3d_scene_settings(state.liveryPreviewSceneSettings || {});
  const info = $("liveryPreviewSceneSettingsInfo");
  if (!result?.ok) {
    if (info) info.textContent = result?.message || "Scene settings could not be saved.";
    return;
  }
  state.liveryPreviewSceneSettings = normalizePreviewSceneSettings(result.data?.settings || {});
  state.liveryPreviewSceneDefaults = normalizePreviewSceneSettings(result.data?.defaults || {});
  state.liveryPreviewSceneSettingsPath = result.data?.path || state.liveryPreviewSceneSettingsPath;
  state.liveryPreviewSceneSettingsDirty = false;
  window.LiveryLabPreview3d?.setSceneConfigs?.(state.liveryPreviewSceneSettings.scenes || {});
  renderLiveryPreviewSceneSettingsPanel();
  if (info) info.textContent = result.message || "Scene settings saved.";
}

function resetLiveryPreviewSceneSettingsScene() {
  const scene = previewScenePanelScene();
  const settings = state.liveryPreviewSceneSettings || normalizePreviewSceneSettings(window.LiveryLabPreview3d?.getSceneConfigs?.() || {});
  settings.scenes[scene] = clonePreviewJson(previewSceneDefaultsFor(scene));
  state.liveryPreviewSceneSettings = settings;
  state.liveryPreviewSceneSettingsDirty = true;
  window.LiveryLabPreview3d?.setSceneConfig?.(scene, settings.scenes[scene]);
  setLiveryPreviewScene(scene, { quiet: true });
  renderLiveryPreviewSceneSettingsPanel();
  const info = $("liveryPreviewSceneSettingsInfo");
  if (info) info.textContent = "Scene reset to bundled defaults. Save to update the JSON.";
}

async function setLiveryPreviewSceneSettingsPanelVisible(visible) {
  const panel = $("liveryPreviewSceneSettingsPanel");
  const workspace = $("liveryPreviewWorkspace");
  const button = $("liveryPreviewToggleSceneSettings");
  const next = Boolean(visible);
  const wasOpen = panel?.hidden === false;
  if (next) {
    setLiveryPreviewNumberplatePanelVisible(false);
    setLiveryPreviewNumberplateDevPanelVisible(false);
    await loadLiveryPreviewSceneSettings();
    if ($("liveryPreviewSceneSettingsScene")) $("liveryPreviewSceneSettingsScene").value = state.liveryPreviewScene || "light";
    setLiveryPreviewScene(state.liveryPreviewScene || "light", { quiet: true });
    await syncLiveryPreviewSceneMaterialLookMode({ quiet: true, sceneName: state.liveryPreviewScene || "light" });
    renderLiveryPreviewSceneSettingsPanel();
  }
  if (panel) panel.hidden = !next;
  if (!next && wasOpen) setLiveryPreviewScene(state.liveryPreviewScene || "light", { quiet: true });
  workspace?.classList.toggle("is-scene-settings-panel-open", next);
  button?.classList.toggle("is-active-danger", next);
  refreshLiveryPreviewLayout();
}

function setLiveryPreviewCameraCalibrationPanelVisible(visible) {
  const panel = $("liveryPreviewCameraCalibrationPanel");
  const workspace = $("liveryPreviewWorkspace");
  const button = $("liveryPreviewToggleCameraCalibration");
  const next = Boolean(visible);
  if (panel) panel.hidden = !next;
  workspace?.classList.toggle("is-camera-calibration-panel-open", next);
  button?.classList.toggle("is-active-danger", next);
  refreshLiveryPreviewLayout();
}

function setLiveryPreviewScene(sceneName, options = {}) {
  const next = liveryPreviewUserSceneName(sceneName);
  state.liveryPreviewScene = next;
  document.querySelectorAll("[data-livery-preview-scene]").forEach((button) => {
    const active = button.dataset.liveryPreviewScene === next;
    button.classList.toggle("active", active);
    button.setAttribute("aria-checked", active ? "true" : "false");
  });
  const result = window.LiveryLabPreview3d?.setSceneMode?.(next);
  const status = $("liveryPreviewStatus");
  if (!options.quiet && status) {
    status.textContent = `Scene: ${liveryPreviewSceneLabel(next)}`;
  }
  if ($("liveryPreviewSceneSettingsPanel")?.hidden === false) {
    if ($("liveryPreviewSceneSettingsScene")) $("liveryPreviewSceneSettingsScene").value = next;
    renderLiveryPreviewSceneSettingsPanel();
  }
  syncLiveryPreviewSceneMaterialLookMode({ quiet: true });
  return result || { sceneMode: next };
}

function setLiveryPreviewWatchBadge(text, mode = "idle") {
  const badge = $("liveryPreviewWatchBadge");
  if (!badge) return;
  const normalizedMode = ["idle", "loading", "watching", "reloading", "error", "stopped", "success"].includes(mode) ? mode : "idle";
  badge.textContent = text || "Preview idle";
  badge.className = `livery-preview-watch-badge is-${normalizedMode}`;
  badge.dataset.mode = normalizedMode;
}

function syncLiveryPreviewStatusCollapse() {
  const wrap = $("liveryPreviewStatusWrap");
  const button = $("liveryPreviewStatusToggle");
  const collapsed = Boolean(state.liveryPreviewStatusCollapsed);
  wrap?.classList.toggle("is-collapsed", collapsed);
  if (button) {
    button.textContent = collapsed ? "Show Output" : "Hide Output";
    button.setAttribute("aria-expanded", collapsed ? "false" : "true");
  }
}

function toggleLiveryPreviewStatusOutput() {
  state.liveryPreviewStatusCollapsed = !state.liveryPreviewStatusCollapsed;
  syncLiveryPreviewStatusCollapse();
  refreshLiveryPreviewLayout();
}

function liveryPreviewAoStatusLines(viewer = {}) {
  const applied = Array.isArray(viewer.appliedTextures)
    ? viewer.appliedTextures.find((item) => item?.aoTexturePath || item?.texturePath === viewer.texturePath)
    : null;
  if (!liveryPreviewModeUsesAo()) return ["AO texture: off in this preview mode"];
  if (!state.liveryPreviewAoEnabled) return ["AO texture: off"];
  const path = viewer.aoTexturePath || applied?.aoTexturePath || "";
  if (!path) return ["AO texture: none"];
  return [
    `AO texture: ${path}`,
    `AO materials: ${applied?.aoAppliedMaterials ?? 0}`,
  ];
}

async function setLiveryPreviewAoEnabled(enabled) {
  state.liveryPreviewAoEnabled = Boolean(enabled);
  const checkbox = $("liveryPreviewUseAoTexture");
  if (checkbox) checkbox.checked = state.liveryPreviewAoEnabled;
  if ($("liveryPreviewWorkspace")?.hidden || !window.LiveryLabPreview3d?.reloadTexture) return;
  setLiveryPreviewWatchBadge("Reloading texture", "reloading");
  $("liveryPreviewStatus").textContent = `AO texture ${state.liveryPreviewAoEnabled ? "enabled" : "disabled"}. Reloading preview...`;
  try {
    const { viewer, materialMode } = await reloadLiveryPreviewViewerTextureWithMaterialMode();
    setLiveryPreviewWatchBadge("Watching livery PNGs", "watching");
    $("liveryPreviewStatus").textContent = [
      `AO texture ${state.liveryPreviewAoEnabled ? "enabled" : "disabled"}`,
      `Texture: ${viewer.texturePath || "-"}`,
      ...liveryPreviewAoStatusLines(viewer),
      `Textured materials: ${viewer.appliedMaterials}`,
      ...(materialMode ? liveryPreviewMaterialModeLegend(materialMode) : []),
    ].join("\n");
  } catch (error) {
    setLiveryPreviewWatchBadge("Reload failed", "error");
    $("liveryPreviewStatus").textContent = `AO texture toggle failed:\n${error?.message || error}`;
  }
}

async function generateLiveryPreviewAoTexture() {
  const project = state.liveryPreviewProject || {};
  const carModel = project.carModel || "";
  if (!carModel) {
    $("liveryPreviewStatus").textContent = "Car AO generation needs a loaded car model.";
    return;
  }
  setLiveryPreviewWatchBadge("Generating AO texture", "reloading");
  $("liveryPreviewStatus").textContent = "Generating car AO preview texture...";
  try {
    const generateAoTexture = window.pywebview?.api?.generate_preview3d_car_ao_texture;
    if (typeof generateAoTexture !== "function") {
      setLiveryPreviewWatchBadge("AO generation unavailable", "error");
      $("liveryPreviewStatus").textContent = "AO texture generation needs an updated LiveryLab.exe build.";
      return;
    }
    const result = await generateAoTexture({
      root: state.root || $("rootPath")?.value.trim() || project.gameRoot || "",
      carModel,
      overwrite: false,
    });
    if (!result.ok) {
      setLiveryPreviewWatchBadge("AO generation failed", "error");
      $("liveryPreviewStatus").textContent = `AO texture generation failed:\n${result.message || ""}`;
      return;
    }
    const aoPath = result.data?.targetPngPath || "";
    state.preview3dAoTexturePath = aoPath;
    state.liveryPreviewProject = {
      ...(state.liveryPreviewProject || {}),
      aoTexturePath: aoPath,
      liveAoTexturePath: aoPath,
    };
    if ($("preview3dAoTexturePath")) $("preview3dAoTexturePath").value = aoPath;
    if ($("liveryPreviewUseAoTexture")) $("liveryPreviewUseAoTexture").checked = true;
    state.liveryPreviewAoEnabled = true;
    renderLiveryPreviewTextureSlotMenu();
    const { viewer, materialMode } = await reloadLiveryPreviewViewerTextureWithMaterialMode();
    setLiveryPreviewWatchBadge("Watching livery PNGs", "watching");
    $("liveryPreviewStatus").textContent = [
      result.message || "Car AO texture generated.",
      `AO texture: ${aoPath || "-"}`,
      ...liveryPreviewAoStatusLines(viewer),
      `Textured materials: ${viewer.appliedMaterials}`,
      ...(materialMode ? liveryPreviewMaterialModeLegend(materialMode) : []),
    ].join("\n");
  } catch (error) {
    setLiveryPreviewWatchBadge("AO generation failed", "error");
    $("liveryPreviewStatus").textContent = `AO texture generation failed:\n${error?.message || error}`;
  }
}

async function setLiveryPreviewTextureKey(key, options = {}) {
  const entry = liveryPreviewTextureEntryForKey(key);
  const next = entry?.key || "ext_skin_c";
  state.liveryPreviewTextureKey = next;
  state.liveryPreviewSkinTextureMode = next === "ext_skin_d" ? "detail" : "color";
  const previewMode = normalizeLiveryPreviewMode(state.liveryPreviewPreviewMode);
  if (next === "ext_skin_c" && previewMode === "materialMask") {
    await setLiveryPreviewPreviewMode("materialView", { reload: false });
  } else if (next !== "ext_skin_c" && previewMode !== "materialMask") {
    await setLiveryPreviewPreviewMode("materialMask", { reload: false, preserveTexture: true });
  }
  renderLiveryPreviewTextureSlotMenu();
  if (options.reload === false || $("liveryPreviewWorkspace")?.hidden || !window.LiveryLabPreview3d?.reloadTexture) return;
  setLiveryPreviewWatchBadge("Reloading texture", "reloading");
  $("liveryPreviewStatus").textContent = `Showing ${entry?.label || liveryPreviewTextureLabel(next)} on skin materials...`;
  try {
    const { viewer, materialMode } = await reloadLiveryPreviewViewerTextureWithMaterialMode();
    setLiveryPreviewWatchBadge("Watching livery PNGs", "watching");
    $("liveryPreviewStatus").textContent = [
      `Showing ${entry?.label || liveryPreviewTextureLabel(next)}`,
      `Preview mode: ${liveryPreviewModeLabel()}`,
      `Texture: ${viewer.texturePath || "-"}`,
      ...liveryPreviewAoStatusLines(viewer),
      `Textured materials: ${viewer.appliedMaterials}`,
      ...(materialMode ? liveryPreviewMaterialModeLegend(materialMode) : []),
    ].join("\n");
  } catch (error) {
    setLiveryPreviewWatchBadge("Reload failed", "error");
    $("liveryPreviewStatus").textContent = `Texture mode failed:\n${error?.message || error}`;
  }
}

async function setLiveryPreviewSkinTextureMode(mode, options = {}) {
  return setLiveryPreviewTextureKey(mode === "detail" ? "ext_skin_d" : "ext_skin_c", options);
}

async function setLiveryPreviewPreviewMode(mode, options = {}) {
  const next = normalizeLiveryPreviewMode(mode);
  state.liveryPreviewPreviewMode = next;
  syncLiveryPreviewModeButtons();
  if (next !== "evoApprox" && state.liveryPreviewApproxMaterialsActive) {
    await setLiveryPreviewApproxMaterials(false, { quiet: true });
  }
  if (next !== "materialMask" && state.liveryPreviewMaterialOverlayActive) {
    await setLiveryPreviewMaterialOverlay(false, { quiet: true });
  }
  if (next === "evoApprox") {
    state.liveryPreviewTextureKey = "ext_skin_c";
    state.liveryPreviewSkinTextureMode = "color";
    renderLiveryPreviewTextureSlotMenu();
  }
  if (next === "materialView") {
    state.liveryPreviewTextureKey = "ext_skin_c";
    state.liveryPreviewSkinTextureMode = "color";
    renderLiveryPreviewTextureSlotMenu();
  }
  if (next === "materialMask") {
    if (!options.preserveTexture && state.liveryPreviewTextureKey === "ext_skin_c") {
      state.liveryPreviewTextureKey = "ext_skin_d";
      state.liveryPreviewSkinTextureMode = "detail";
    }
    renderLiveryPreviewTextureSlotMenu();
  }
  if (options.reload === false || $("liveryPreviewWorkspace")?.hidden || !window.LiveryLabPreview3d?.reloadTexture) return;
  setLiveryPreviewWatchBadge("Reloading preview mode", "reloading");
  $("liveryPreviewStatus").textContent = `Switching preview mode to ${liveryPreviewModeLabel(next)}...`;
  try {
    const { viewer, materialMode } = await reloadLiveryPreviewViewerTextureWithMaterialMode();
    setLiveryPreviewWatchBadge("Watching livery PNGs", "watching");
    $("liveryPreviewStatus").textContent = [
      `Preview mode: ${liveryPreviewModeLabel(next)}`,
      `Texture: ${viewer.texturePath || "-"}`,
      ...liveryPreviewAoStatusLines(viewer),
      `Textured materials: ${viewer.appliedMaterials}`,
      ...(materialMode ? liveryPreviewMaterialModeLegend(materialMode) : []),
    ].join("\n");
  } catch (error) {
    setLiveryPreviewWatchBadge("Reload failed", "error");
    $("liveryPreviewStatus").textContent = `Preview mode failed:\n${error?.message || error}`;
  }
}

function buttonLabelForCameraPreset(preset) {
  const labels = {
    free: "Free Cam",
    front: "Front",
    rear: "Rear",
    left: "Left",
    right: "Right",
    top: "Top",
    "front-three-quarter": "Front 3/4",
    "rear-three-quarter": "Rear 3/4",
  };
  return labels[preset] || labels.free;
}

function syncLiveryPreviewReadOnlyMenus() {
  const readOnly = state.liveryPreviewReadOnly === true;
  document.querySelectorAll("#liveryPreviewWorkspace .livery-preview-menu-group").forEach((group) => {
    if (readOnly) {
      if (!group.dataset.readOnlyPreviousHidden) {
        group.dataset.readOnlyPreviousHidden = group.hidden ? "1" : "0";
      }
      group.hidden = true;
    } else if (group.dataset.readOnlyPreviousHidden) {
      group.hidden = group.dataset.readOnlyPreviousHidden === "1";
      delete group.dataset.readOnlyPreviousHidden;
    }
  });
  [document.querySelector("#liveryPreviewWorkspace .livery-preview-bottombar"), $("liveryPreviewStatusWrap")].forEach((item) => {
    if (!item) return;
    if (readOnly) {
      if (!item.dataset.readOnlyPreviousHidden) {
        item.dataset.readOnlyPreviousHidden = item.hidden ? "1" : "0";
      }
      item.hidden = true;
    } else if (item.dataset.readOnlyPreviousHidden) {
      item.hidden = item.dataset.readOnlyPreviousHidden === "1";
      delete item.dataset.readOnlyPreviousHidden;
    }
  });
  const reloadInView = $("liveryPreviewReloadTextureView");
  if (reloadInView) reloadInView.hidden = true;
}

async function openLiveryPreviewWorkspace(data) {
  const workspace = $("liveryPreviewWorkspace");
  if (!workspace) return false;
  window.clearTimeout(state.preview3dTimer);
  state.preview3dTimer = 0;
  cancelPreview3dViewerLoad({ emptyId: "liveryPreviewEmpty" });
  const project = data?.project || {};
  state.liveryPreviewProject = project;
  state.liveryPreviewReadOnly = project.readOnly === true || project.policy?.readOnly === true;
  syncLiveryPreviewReadOnlyMenus();
  state.liveryPreviewNumberplateSupport = normalizeNumberplateSupport(data?.numberplateSupport ? data : project);
  state.liveryPreviewProjectFolder = project.projectFolder || "";
  state.liveryPreviewSkinKey = project.skinKey || "";
  syncLiveryPreviewGameUpdateAction();
  const hasProjectColor = /^#?[0-9a-fA-F]{6}$/.test(String(project.bodyBaseColor || "").trim());
  applyLiveryPreviewBodyBaseColorValue(hasProjectColor ? project.bodyBaseColor : "#ffffff", hasProjectColor ? "project" : "default");
  state.liveryPreviewMaterialFinish = normalizeLiveryMaterialFinish(project.materialFinish || project.material_finish || LIVERY_MATERIAL_FINISH_DEFAULT);
  syncLiveryPreviewMaterialFinishPanel();
  notifyLiveryMaterialFinishSubscribers(state.liveryPreviewMaterialFinish, { source: "liveryPreviewOpen" });
  state.preview3dAoTexturePath = preview3dAoTexturePathForProject(project);
  state.liveryPreviewMaterialInspection = null;
  state.liveryPreviewWheelPreview = data?.wheelPreview || null;
  state.liveryPreviewWheelTunerEnabled = false;
  state.liveryPreviewWheelUnsavedChanges = false;
  state.liveryPreviewNumberplateGizmoEnabled = false;
  state.liveryPreviewNumberplateUnsavedChanges = false;
  state.liveryPreviewNumberplateSavedNeedsApply = false;
  state.liveryPreviewNumberplateSelectedIndex = 0;
  state.liveryPreviewNumberplateFit = normalizeNumberplateFitConfig(NUMBERPLATE_FIT_DEFAULT);
  state.liveryPreviewNumberplateDebugLabels = true;
  window.clearTimeout(state.liveryPreviewNumberplateFitTimer);
  setNumberplateFitInputs(state.liveryPreviewNumberplateFit);
  setLiveryPreviewNumberplatesVisible(false);
  state.liveryPreviewMaterialOverlayActive = false;
  state.liveryPreviewApproxMaterialsActive = false;
  setLiveryPreviewMaterialPanelVisible(false);
  setLiveryPreviewMaterialFinishPanelVisible(false);
  setLiveryPreviewWheelPanelVisible(false);
  setLiveryPreviewMaterialOverlayButton(false);
  setLiveryPreviewApproxMaterialsButton(false);
  state.liveryPreviewAoEnabled = true;
  state.liveryPreviewTextureKey = "ext_skin_c";
  state.liveryPreviewPreviewMode = "materialView";
  renderLiveryPreviewTextureSlotMenu(project);
  syncLiveryPreviewModeButtons();
  setLiveryPreviewScene("dark", { quiet: true });
  state.liveryPreviewRimsVisible = true;
  state.liveryPreviewWheelPlacementOverrides = true;
  $("liveryPreviewTitle").textContent = project.displayName || project.skinId || "3D Livery Preview";
  $("liveryPreviewMeta").textContent = [
    project.carModel || "car",
    state.liveryPreviewReadOnly ? "read-only preview" : "",
    project.projectFolder || "",
  ].filter(Boolean).join(" | ");
  if ($("liveryPreviewShowExterior")) $("liveryPreviewShowExterior").checked = true;
  if ($("liveryPreviewUseAoTexture")) $("liveryPreviewUseAoTexture").checked = state.liveryPreviewAoEnabled;
  if ($("preview3dAoTexturePath")) $("preview3dAoTexturePath").value = state.preview3dAoTexturePath;
  if ($("liveryPreviewShowRims")) $("liveryPreviewShowRims").checked = true;
  if ($("liveryPreviewShowFloor")) $("liveryPreviewShowFloor").checked = true;
  [
    "liveryPreviewImportTextures",
    "liveryPreviewImportOverwrite",
    "liveryPreviewToggleMaterialFinish",
    "liveryPreviewMaterialFinishSave",
    "liveryPreviewSaveBodyBaseColor",
  ].forEach((id) => {
    const node = $(id);
    if (node) node.disabled = state.liveryPreviewReadOnly;
  });
  syncLiveryPreviewBodyBaseColorControls(project);
  syncLiveryPreviewNumberplateSupportUi();
  if ($("liveryPreviewShowNumberplates")) $("liveryPreviewShowNumberplates").checked = !liveryPreviewNumberplatesBlocked() && !liveryPreviewNumberplatesHidden();
  if ($("liveryPreviewUseWheelPlacementOverrides")) $("liveryPreviewUseWheelPlacementOverrides").checked = true;
  if ($("liveryPreviewWheelTune")) $("liveryPreviewWheelTune").textContent = "Tune";
  updateLiveryPreviewWheelUnsavedIndicator({ dirty: false });
  updateNumberplateGizmoUnsavedIndicator({ dirty: false });
  setLiveryPreviewWatchBadge("Preparing preview", "loading");
  setLiveryPreviewSkinTextureMode("color", { reload: false });
  $("liveryPreviewStatus").textContent = "Preparing 3D livery preview...";
  setLiveryPreviewCameraPreset("free");
  workspace.hidden = false;
  refreshLiveryPreviewLayout();
  const canvasReady = await waitForPreviewCanvasVisible("liveryPreviewCanvas", $("liveryPreviewStatus"), 4200, {
    minWidth: 260,
    minHeight: 180,
    stableFramesRequired: 5,
  });
  if (!canvasReady) {
    $("liveryPreviewStatus").textContent = "3D preview failed:\nPreview canvas did not become stable. Close and reopen the preview.";
    setLiveryPreviewWatchBadge("Preview failed", "error");
    return false;
  }
  const loaded = await loadPreview3dViewer({
    canvasId: "liveryPreviewCanvas",
    emptyId: "liveryPreviewEmpty",
    statusId: "liveryPreviewStatus",
    minCanvasWidth: 260,
    minCanvasHeight: 180,
    stableFramesRequired: 5,
  });
  if (!loaded) return false;
  if (!state.liveryPreviewReadOnly && NUMBERPLATE_OVERLAY_WORKFLOW_ENABLED) await applyBuiltinNumberplateFitForCurrentCar();
  if (!state.liveryPreviewReadOnly) await startLiveryPreviewWatcher();
  if (!state.liveryPreviewReadOnly && !liveryPreviewNumberplatesBlocked() && !liveryPreviewNumberplatesHidden()) {
    try {
      await prepareNumberplateOverlayPanel({ openPanel: false, loadSelected: false, removeOverlay: false, overwrite: false });
      await showMappedNumberplates();
    } catch (error) {
      setLiveryPreviewNumberplatesVisible(false);
      $("liveryPreviewStatus").textContent = `Registered numberplates failed:\n${error?.message || error}`;
    }
  }
  return loaded;
}

async function waitForPreviewCanvasVisible(canvasId, status, timeoutMs = 1800, options = {}) {
  const started = performance.now();
  const minWidth = Number(options.minWidth || 20);
  const minHeight = Number(options.minHeight || 20);
  const stableFramesRequired = Number(options.stableFramesRequired || 2);
  let stableFrames = 0;
  let lastSize = null;
  while (performance.now() - started < timeoutMs) {
    const metrics = previewCanvasMetrics(canvasId);
    const sizeReady = metrics.ready && metrics.width >= minWidth && metrics.height >= minHeight;
    if (sizeReady) {
      const stableSize = lastSize
        && Math.abs(metrics.width - lastSize.width) <= 2
        && Math.abs(metrics.height - lastSize.height) <= 2;
      stableFrames = stableSize ? stableFrames + 1 : 1;
      lastSize = { width: metrics.width, height: metrics.height };
      if (stableFrames >= stableFramesRequired) return true;
    } else {
      stableFrames = 0;
      lastSize = null;
      if (status) status.textContent = "Preparing 3D livery preview...\nWaiting for preview canvas.";
    }
    await new Promise((resolve) => window.requestAnimationFrame(resolve));
  }
  return false;
}

function closeLiveryPreviewWorkspace() {
  cancelPreview3dViewerLoad({ emptyId: "liveryPreviewEmpty" });
  const workspace = $("liveryPreviewWorkspace");
  if (workspace) workspace.hidden = true;
  refreshLiveryPreviewLayout();
  state.liveryPreviewProjectFolder = "";
  state.liveryPreviewSkinKey = "";
  state.liveryPreviewReadOnly = false;
  syncLiveryPreviewReadOnlyMenus();
  state.liveryPreviewProject = null;
  state.liveryPreviewNumberplateSupport = null;
  state.liveryPreviewTextureKey = "ext_skin_c";
  state.liveryPreviewPreviewMode = "materialView";
  state.liveryPreviewMaterialInspection = null;
  state.liveryPreviewWheelPreview = null;
  state.liveryPreviewWheelTunerEnabled = false;
  state.liveryPreviewWheelUnsavedChanges = false;
  window.LiveryLabPreview3d?.setWheelTunerEnabled?.(false);
  state.liveryPreviewNumberplateGizmoEnabled = false;
  state.liveryPreviewNumberplateUnsavedChanges = false;
  setLiveryPreviewNumberplatesVisible(false);
  window.LiveryLabPreview3d?.setNumberplateTunerEnabled?.(false);
  state.liveryPreviewMaterialOverlayActive = false;
  state.liveryPreviewApproxMaterialsActive = false;
  setLiveryPreviewMaterialPanelVisible(false);
  setLiveryPreviewMaterialFinishPanelVisible(false);
  setLiveryPreviewWheelPanelVisible(false);
  setLiveryPreviewSceneSettingsPanelVisible(false);
  setLiveryPreviewCameraCalibrationPanelVisible(false);
  updateLiveryPreviewWheelUnsavedIndicator({ dirty: false });
  setLiveryPreviewMaterialOverlayButton(false);
  setLiveryPreviewApproxMaterialsButton(false);
  state.liveryPreviewNumberplate = null;
  setLiveryPreviewNumberplatePanelVisible(false);
  updateNumberplateGizmoUnsavedIndicator({ dirty: false });
  window.LiveryLabPreview3d?.removeNumberplateOverlay?.();
  setLiveryPreviewWatchBadge("Watcher stopped", "stopped");
  stopLiveryPreviewWatcher();
}

window.LiveryLabLayerStudioApp = {
  root() {
    return state.root || $("rootPath")?.value.trim() || "";
  },
  setButtonWorking,
  showResult,
  log,
  setInspectOutputText,
  async handleTextureUpdated(data) {
    const skinKey = String(data?.skinKey || state.liveryPreviewSkinKey || "").trim();
    if (skinKey) state.mySkinSelectedKey = skinKey;
    await loadMySkins();
    syncLiveryPreviewGameUpdateAction();
  },
  async preparePreview(data) {
    closeLiveryPreviewWorkspace();
    applyPreview3dProject(data?.project || {});
    await detectDefaultLiveryPreviewBodyBaseColor();
    const saveResult = await window.pywebview.api.save_preview3d_project(payloadPreview3dProject());
    if (!saveResult.ok) {
      return { ok: false, message: saveResult.message || "Could not save 3D preview setup" };
    }
    return { ok: true };
  },
  async loadPreview(data) {
    const project = layerStudioPreviewProjectPayload(data?.project || {});
    const showVariantPartAuxiliaries = layerStudioShouldShowVariantPartAuxiliaries(project);
    state.liveryPreviewProject = project;
    state.liveryPreviewProjectFolder = project.projectFolder || "";
    state.liveryPreviewSkinKey = project.skinKey || "";
    state.liveryPreviewReadOnly = false;
    state.liveryPreviewAoEnabled = true;
    state.liveryPreviewTextureKey = "ext_skin_c";
    state.liveryPreviewPreviewMode = "materialView";
    setLiveryPreviewSkinTextureMode("color", { reload: false });
    setLiveryPreviewCameraPreset("free");
    const loadOptions = {
      canvasId: "layerStudioPreviewCanvas",
      emptyId: "layerStudioPreviewEmpty",
      statusId: "layerStudioPreviewStatus",
      statusPrefix: "Layer Studio 3D preview",
      minCanvasWidth: 260,
      minCanvasHeight: 180,
      stableFramesRequired: 5,
      buildPreviewModel: showVariantPartAuxiliaries && !(Array.isArray(project.auxiliaryModels) && project.auxiliaryModels.length),
      includeAuxiliaryModels: showVariantPartAuxiliaries ? "variantParts" : false,
      syncExteriorCheckbox: false,
      projectOverride: project,
      cameraPreset: project.cameraPreset || "free",
      loadTimeoutMs: 30000,
      auxiliaryTimeoutMs: 8000,
    };
    const status = $("layerStudioPreviewStatus");
    await waitForPreviewCanvasVisible(loadOptions.canvasId, status, 3500, {
      minWidth: loadOptions.minCanvasWidth,
      minHeight: loadOptions.minCanvasHeight,
      stableFramesRequired: loadOptions.stableFramesRequired,
    });
    let loaded = await loadPreview3dViewer(loadOptions);
    if (!loaded) {
      if (status) status.textContent = "Retrying Layer Studio 3D preview...\nWaiting for renderer startup.";
      await resetPreview3dViewerForRetry({ emptyId: loadOptions.emptyId });
      await waitForPreviewCanvasVisible(loadOptions.canvasId, status, 2500, {
        minWidth: loadOptions.minCanvasWidth,
        minHeight: loadOptions.minCanvasHeight,
        stableFramesRequired: loadOptions.stableFramesRequired,
      });
      loaded = await loadPreview3dViewer({
        ...loadOptions,
        retryOnUnstable: false,
      });
    }
    return {
      ok: Boolean(loaded),
      message: $("layerStudioPreviewStatus")?.textContent || "",
    };
  },
  async saveMaterialFinish(options = {}) {
    return confirmAndSaveLiveryPreviewMaterialFinish({
      buttonId: options.buttonId || "",
      source: "layerStudioSave",
      statusId: "layerStudioPreviewStatus",
      showSuccessModal: false,
    });
  },
  closePreview() {
    cancelPreview3dViewerLoad({ emptyId: "layerStudioPreviewEmpty" });
    window.LiveryLabPreview3d?.clear?.({ emptyId: "layerStudioPreviewEmpty" });
    window.LiveryLabPreview3d?.resize?.();
  },
  async openProjectFolder(path) {
    const folder = String(path || state.liveryPreviewProjectFolder || "").trim();
    if (!folder) return { ok: false, message: "No project folder is available." };
    return window.pywebview.api.open_folder(folder);
  },
};

function renderPreviewTextureImportStatus(data) {
  const items = data?.items || [];
  const lines = [
    "Preview texture import finished",
    `Imported: ${data?.imported ?? 0}`,
    `Skipped: ${data?.skipped ?? 0}`,
    `Missing: ${data?.missing ?? 0}`,
    "",
    ...items.map((item) => `${item.label}: ${item.status}${item.reason ? ` (${item.reason})` : ""}`),
  ];
  $("liveryPreviewStatus").textContent = lines.join("\n");
}

function renderGameTextureUpdateStatus(data) {
  const items = data?.items || [];
  const lines = [
    "Game texture update finished",
    `Updated: ${data?.updated ?? 0}`,
    `Skipped: ${data?.skipped ?? 0}`,
    `Missing: ${data?.missing ?? 0}`,
    "",
    ...items.map((item) => {
      const suffix = [
        item.status,
        item.format ? `format ${item.format}` : "",
        item.width && item.height ? `${item.width}x${item.height}` : "",
        item.mips ? `${item.mips} mips` : "",
        item.reason ? item.reason : "",
      ].filter(Boolean).join(" | ");
      return `${item.label}: ${suffix}`;
    }),
  ];
  $("liveryPreviewStatus").textContent = lines.join("\n");
}

function numberValue(id, fallback = 0) {
  const value = Number($(id)?.value ?? fallback);
  return Number.isFinite(value) ? value : fallback;
}

function numberplateScaleRatioFromTransform(transform = {}) {
  const sx = Math.abs(Number(transform.sx));
  const sy = Math.abs(Number(transform.sy));
  if (Number.isFinite(sx) && sx > 0.0001 && Number.isFinite(sy) && sy > 0.0001) {
    return sy / sx;
  }
  return state.liveryPreviewNumberplateScaleRatio || 0.28;
}

function setNumberplateScaleInputs(transform = {}) {
  const sx = Math.abs(Number(transform.sx));
  const sy = Math.abs(Number(transform.sy));
  const safeSx = Number.isFinite(sx) && sx > 0 ? sx : 1;
  const safeSy = Number.isFinite(sy) && sy > 0 ? sy : safeSx * (state.liveryPreviewNumberplateScaleRatio || 0.28);
  state.liveryPreviewNumberplateScaleRatio = numberplateScaleRatioFromTransform({ sx: safeSx, sy: safeSy });
  setNumberInput("liveryPreviewPlateScale", safeSx);
  setNumberInput("liveryPreviewPlateScaleX", safeSx);
  setNumberInput("liveryPreviewPlateScaleY", safeSy);
}

function syncNumberplateProportionalScaleFromInput() {
  const scale = Math.max(0.001, Math.abs(numberValue("liveryPreviewPlateScale", numberValue("liveryPreviewPlateScaleX", 1))));
  const ratio = state.liveryPreviewNumberplateScaleRatio || numberplateScaleRatioFromTransform({
    sx: numberValue("liveryPreviewPlateScaleX", scale),
    sy: numberValue("liveryPreviewPlateScaleY", scale * 0.28),
  });
  const sy = scale * ratio;
  setNumberInput("liveryPreviewPlateScaleX", scale);
  setNumberInput("liveryPreviewPlateScaleY", sy);
  return { sx: scale, sy };
}

function currentNumberplateTransform() {
  const scale = syncNumberplateProportionalScaleFromInput();
  return {
    x: numberValue("liveryPreviewPlateX"),
    y: numberValue("liveryPreviewPlateY"),
    z: numberValue("liveryPreviewPlateZ"),
    rx: numberValue("liveryPreviewPlateRotX"),
    ry: numberValue("liveryPreviewPlateRotY"),
    rz: numberValue("liveryPreviewPlateRotZ"),
    sx: scale.sx,
    sy: scale.sy,
  };
}

function selectedNumberplatePlacement() {
  const placements = state.liveryPreviewNumberplate?.placements || [];
  const index = Number($("liveryPreviewNumberplatePlacement")?.value || 0);
  return { placement: placements[index] || {}, index };
}

function selectedNumberplateBackground(data = state.liveryPreviewNumberplate || {}) {
  const { placement } = selectedNumberplatePlacement();
  return {
    path: placement?.backgroundPng || data?.backgroundPng || "",
    rel: placement?.backgroundRel || data?.backgroundRel || "",
    status: placement?.backgroundStatus || data?.backgroundStatus || "",
  };
}

function selectedNumberplateFont(data = state.liveryPreviewNumberplate || {}) {
  const { placement } = selectedNumberplatePlacement();
  return {
    path: placement?.fontPng || data?.fontPng || "",
    rel: placement?.fontRel || data?.fontRel || "",
    status: placement?.fontStatus || data?.fontStatus || "",
  };
}

function selectedNumberplateTextColor(data = state.liveryPreviewNumberplate || {}) {
  const { placement } = selectedNumberplatePlacement();
  const color = placement?.textColor || placement?.overlay || data?.textColor || data?.overlay || null;
  return Array.isArray(color) && color.length >= 4 ? color.slice(0, 4).map(Number) : null;
}

function numberplateTextColorLabel(color) {
  if (!Array.isArray(color) || color.length < 4) return "texture";
  return color.slice(0, 4).map((value) => Number.isFinite(Number(value)) ? Math.round(Number(value) * 1000) / 1000 : 0).join(", ");
}

function linearChannelToSrgb(value) {
  const clamped = Math.max(0, Math.min(1, Number(value) || 0));
  return clamped <= 0.0031308 ? clamped * 12.92 : 1.055 * Math.pow(clamped, 1 / 2.4) - 0.055;
}

function srgbChannelToLinear(value) {
  const clamped = Math.max(0, Math.min(1, Number(value) || 0));
  return clamped <= 0.04045 ? clamped / 12.92 : Math.pow((clamped + 0.055) / 1.055, 2.4);
}

function linearRgbaToHex(color) {
  const rgba = Array.isArray(color) && color.length >= 3 ? color : [1, 1, 1, 1];
  const channels = rgba.slice(0, 3).map((value) => Math.round(linearChannelToSrgb(value) * 255));
  return `#${channels.map((value) => Math.max(0, Math.min(255, value)).toString(16).padStart(2, "0")).join("")}`;
}

function hexAlphaToLinearRgba(hex, alpha = 1) {
  const normalized = String(hex || "").trim().replace(/^#/, "");
  if (!/^[0-9a-fA-F]{6}$/.test(normalized)) return null;
  const channels = [0, 2, 4].map((index) => parseInt(normalized.slice(index, index + 2), 16) / 255);
  const numericAlpha = Number(alpha);
  return [
    srgbChannelToLinear(channels[0]),
    srgbChannelToLinear(channels[1]),
    srgbChannelToLinear(channels[2]),
    Number.isFinite(numericAlpha) ? Math.max(0, Math.min(1, numericAlpha)) : 1,
  ];
}

const NUMBERPLATE_TEXT_LAYER_DEFAULT = { x: 0, y: 0, scale: 0.3, spacing: 0.02 };

function coerceNumberplateTextLayerValues(values = {}, fallback = NUMBERPLATE_TEXT_LAYER_DEFAULT) {
  const source = values && typeof values === "object" ? values : {};
  const base = fallback && typeof fallback === "object" ? fallback : NUMBERPLATE_TEXT_LAYER_DEFAULT;
  const numeric = (key) => {
    const value = Number(source[key]);
    if (Number.isFinite(value)) return value;
    const fallbackValue = Number(base[key]);
    return Number.isFinite(fallbackValue) ? fallbackValue : NUMBERPLATE_TEXT_LAYER_DEFAULT[key];
  };
  return {
    x: Math.max(-1, Math.min(1, numeric("x"))),
    y: Math.max(-1, Math.min(1, numeric("y"))),
    scale: Math.max(0.05, Math.min(1, numeric("scale"))),
    spacing: Math.max(-0.1, Math.min(0.5, numeric("spacing"))),
  };
}

function numberplateTextLayerLookupKeys(value) {
  const raw = String(value || "").trim();
  if (!raw) return [];
  const normalized = raw.replace(/\\/g, "/").toLowerCase();
  const keys = new Set([normalized]);
  const backgroundTail = normalized.includes("/numberplates/backgrounds/")
    ? normalized.split("/numberplates/backgrounds/").pop()
    : "";
  if (backgroundTail) keys.add(backgroundTail);
  const fileName = normalized.split("/").pop() || "";
  if (fileName) keys.add(fileName);
  [backgroundTail, fileName, normalized].forEach((item) => {
    if (!item) return;
    keys.add(item.replace(/\.(texture|texturemips|dds|png)$/i, ""));
  });
  return Array.from(keys).filter(Boolean);
}

function selectedNumberplateTextLayerKey(background = selectedNumberplateBackground()) {
  const preferred = (value) => {
    const keys = numberplateTextLayerLookupKeys(value);
    return keys.find((key) => !key.includes("/") && !key.includes(".")) || keys.find((key) => !key.includes("/")) || keys[0] || "";
  };
  return preferred(background?.rel) || preferred(background?.path);
}

function numberplateTemplateRefMatches(ref = {}, lookup = new Set()) {
  const values = [ref.rel, ref.path, ref.assetRel, ref.label, ref.assetId].filter(Boolean);
  return values.some((value) => numberplateTextLayerLookupKeys(value).some((key) => lookup.has(key)));
}

function numberplateTemplateTextLayerDefault(background = selectedNumberplateBackground()) {
  const templates = Array.isArray(state.numberplateStudioTemplates) ? state.numberplateStudioTemplates : [];
  if (!templates.length) return null;
  const backgroundLookup = new Set([
    ...numberplateTextLayerLookupKeys(background?.rel),
    ...numberplateTextLayerLookupKeys(background?.path),
  ]);
  if (!backgroundLookup.size) return null;
  const font = selectedNumberplateFont();
  const fontLookup = new Set([
    ...numberplateTextLayerLookupKeys(font?.rel),
    ...numberplateTextLayerLookupKeys(font?.path),
  ]);
  for (const template of templates) {
    if (fontLookup.size && template.font && !numberplateTemplateRefMatches(template.font, fontLookup)) continue;
    const backgrounds = template.backgrounds && typeof template.backgrounds === "object" ? template.backgrounds : {};
    for (const surfaceKey of ["leftDoor", "rightDoor", "hood"]) {
      if (!numberplateTemplateRefMatches(backgrounds[surfaceKey], backgroundLookup)) continue;
      const surfaceLayers = template.surfaceTextLayers && typeof template.surfaceTextLayers === "object" ? template.surfaceTextLayers : {};
      return coerceNumberplateTextLayerValues(surfaceLayers[surfaceKey] || template.textLayer || {});
    }
  }
  return null;
}

function numberplateTextLayerDefault(background = selectedNumberplateBackground()) {
  const templateDefault = numberplateTemplateTextLayerDefault(background);
  if (templateDefault) return templateDefault;
  const defaults = state.numberplateTextLayerDefaults && typeof state.numberplateTextLayerDefaults === "object"
    ? state.numberplateTextLayerDefaults
    : {};
  const fallback = coerceNumberplateTextLayerValues(defaults.__default || defaults.default || NUMBERPLATE_TEXT_LAYER_DEFAULT);
  const backgrounds = defaults.backgrounds && typeof defaults.backgrounds === "object" ? defaults.backgrounds : defaults;
  const lookup = new Set([
    ...numberplateTextLayerLookupKeys(background?.rel),
    ...numberplateTextLayerLookupKeys(background?.path),
  ]);
  for (const [key, rawLayer] of Object.entries(backgrounds || {})) {
    if (String(key).startsWith("__") || ["default", "backgrounds"].includes(String(key))) continue;
    const candidateKeys = numberplateTextLayerLookupKeys(key);
    if (candidateKeys.some((candidate) => lookup.has(candidate))) {
      return coerceNumberplateTextLayerValues(rawLayer, fallback);
    }
  }
  return fallback;
}

function liveryPreviewCarId() {
  return String(state.liveryPreviewSkinKey || "").split(":")[0].trim().toLowerCase();
}

function selectedNumberplateTunerKey() {
  const { placement, index } = selectedNumberplatePlacement();
  return `${placement?.label || "placement"}:${placement?.index ?? index}`;
}

function numberplateCalibrationMap() {
  const overlay = state.liveryPreviewNumberplate?.numberplateOverlay || state.liveryPreviewNumberplate?.project?.numberplateOverlay || {};
  return overlay?.placements && typeof overlay.placements === "object" ? overlay.placements : {};
}

const NUMBERPLATE_FIT_DEFAULT = Object.freeze({
  boundsScale: 1,
  widthScale: 1,
  lengthScale: 1,
  heightScale: 1,
  plateScale: 0.85,
  lengthDirection: 1,
  xOffset: 0,
  yOffset: 0,
  zOffset: 0,
  hoodYOffset: 0,
  hoodZOffset: 0,
  hoodScale: 1,
  hoodPitch: 0,
  hoodYaw: 0,
  hoodRoll: 0,
  doorPitch: 0,
  doorYaw: 0,
  doorRoll: 0,
  doorPlacementMode: "actor",
});

const NUMBERPLATE_ACTOR_PROJECTION_OFFSET_DEFAULT = Object.freeze({ x: 0, y: 0, z: 0 });

function numberplateProjectionOffsetNumber(raw, fallback = 0) {
  const number = Number(raw);
  if (!Number.isFinite(number)) return fallback;
  return Math.max(-2, Math.min(2, number));
}

function normalizeNumberplateActorProjectionOffset(raw = {}) {
  const offset = raw && typeof raw === "object" ? raw : {};
  return {
    x: numberplateProjectionOffsetNumber(offset.x, NUMBERPLATE_ACTOR_PROJECTION_OFFSET_DEFAULT.x),
    y: numberplateProjectionOffsetNumber(offset.y, NUMBERPLATE_ACTOR_PROJECTION_OFFSET_DEFAULT.y),
    z: numberplateProjectionOffsetNumber(offset.z, NUMBERPLATE_ACTOR_PROJECTION_OFFSET_DEFAULT.z),
  };
}

function setNumberplateActorProjectionOffsetInput(id, value) {
  const input = $(id);
  if (!input) return;
  input.value = Number(value ?? 0).toFixed(3).replace(/0+$/, "").replace(/\.$/, "");
}

function setNumberplateActorProjectionOffsetInputs(offset = state.liveryPreviewNumberplateActorProjectionOffset) {
  const normalized = normalizeNumberplateActorProjectionOffset(offset);
  state.liveryPreviewNumberplateActorProjectionOffset = normalized;
  setNumberplateActorProjectionOffsetInput("liveryPreviewActorProjectionOffsetX", normalized.x);
  setNumberplateActorProjectionOffsetInput("liveryPreviewActorProjectionOffsetY", normalized.y);
  setNumberplateActorProjectionOffsetInput("liveryPreviewActorProjectionOffsetZ", normalized.z);
  return normalized;
}

function readNumberplateActorProjectionOffsetInputs() {
  const next = normalizeNumberplateActorProjectionOffset({
    x: $("liveryPreviewActorProjectionOffsetX")?.value ?? state.liveryPreviewNumberplateActorProjectionOffset?.x,
    y: $("liveryPreviewActorProjectionOffsetY")?.value ?? state.liveryPreviewNumberplateActorProjectionOffset?.y,
    z: $("liveryPreviewActorProjectionOffsetZ")?.value ?? state.liveryPreviewNumberplateActorProjectionOffset?.z,
  });
  state.liveryPreviewNumberplateActorProjectionOffset = next;
  return next;
}

function numberplateActorProjectionOffsetSummary(offset = state.liveryPreviewNumberplateActorProjectionOffset) {
  const normalized = normalizeNumberplateActorProjectionOffset(offset);
  return `Actor projection offset: X ${formatActorNudgeValue(normalized.x)}, Y ${formatActorNudgeValue(normalized.y)}, Z ${formatActorNudgeValue(normalized.z)}`;
}

const NUMBERPLATE_ACTOR_GHOST_OFFSET_DEFAULT = Object.freeze({
  x: 0,
  y: 0,
  z: 0,
  rx: 0,
  ry: 0,
  rz: 0,
  sx: 0,
  sy: 0,
});

function numberplateActorGhostOffsetNumber(raw, fallback = 0, min = -10, max = 10) {
  const number = Number(raw);
  if (!Number.isFinite(number)) return fallback;
  return Math.max(min, Math.min(max, number));
}

function normalizeNumberplateActorGhostOffset(raw = {}) {
  const offset = raw && typeof raw === "object" ? raw : {};
  return {
    x: numberplateActorGhostOffsetNumber(offset.x, NUMBERPLATE_ACTOR_GHOST_OFFSET_DEFAULT.x, -5, 5),
    y: numberplateActorGhostOffsetNumber(offset.y, NUMBERPLATE_ACTOR_GHOST_OFFSET_DEFAULT.y, -5, 5),
    z: numberplateActorGhostOffsetNumber(offset.z, NUMBERPLATE_ACTOR_GHOST_OFFSET_DEFAULT.z, -5, 5),
    rx: numberplateActorGhostOffsetNumber(offset.rx, NUMBERPLATE_ACTOR_GHOST_OFFSET_DEFAULT.rx, -180, 180),
    ry: numberplateActorGhostOffsetNumber(offset.ry, NUMBERPLATE_ACTOR_GHOST_OFFSET_DEFAULT.ry, -180, 180),
    rz: numberplateActorGhostOffsetNumber(offset.rz, NUMBERPLATE_ACTOR_GHOST_OFFSET_DEFAULT.rz, -180, 180),
    sx: numberplateActorGhostOffsetNumber(offset.sx, NUMBERPLATE_ACTOR_GHOST_OFFSET_DEFAULT.sx, -2, 2),
    sy: numberplateActorGhostOffsetNumber(offset.sy, NUMBERPLATE_ACTOR_GHOST_OFFSET_DEFAULT.sy, -2, 2),
  };
}

function numberplateActorGhostOffsetHasValue(offset = {}) {
  const normalized = normalizeNumberplateActorGhostOffset(offset);
  return Object.values(normalized).some((value) => Math.abs(Number(value) || 0) > 0.000001);
}

function numberplateActorGhostOffsetKey(placement = selectedNumberplatePlacement().placement, fallbackIndex = selectedNumberplatePlacement().index) {
  const label = String(placement?.label || "").trim();
  const actorIndex = placement?.index ?? fallbackIndex;
  return label && actorIndex !== "" && actorIndex != null ? `${label}:${actorIndex}` : "";
}

function selectedNumberplateActorGhostOffsetKey() {
  const { placement, index } = selectedNumberplatePlacement();
  return numberplateActorGhostOffsetKey(placement, index);
}

function selectedNumberplateActorGhostOffset() {
  const key = selectedNumberplateActorGhostOffsetKey();
  return normalizeNumberplateActorGhostOffset(key ? state.liveryPreviewActorGhostOffsets[key] : {});
}

function numberplateActorGhostOffsetsForViewer() {
  return Object.fromEntries(
    Object.entries(state.liveryPreviewActorGhostOffsets || {})
      .map(([key, value]) => [key, normalizeNumberplateActorGhostOffset(value)])
      .filter(([, value]) => numberplateActorGhostOffsetHasValue(value)),
  );
}

function actorGhostOffsetSummaryText(offset = selectedNumberplateActorGhostOffset()) {
  const normalized = normalizeNumberplateActorGhostOffset(offset);
  return `Pos ${formatActorNudgeValue(normalized.x)}, ${formatActorNudgeValue(normalized.y)}, ${formatActorNudgeValue(normalized.z)} | Rot ${formatActorNudgeValue(normalized.rx)}, ${formatActorNudgeValue(normalized.ry)}, ${formatActorNudgeValue(normalized.rz)} | Scale ${formatActorNudgeValue(normalized.sx)}, ${formatActorNudgeValue(normalized.sy)}`;
}

function renderNumberplateActorGhostOffsetSummary() {
  const node = $("liveryPreviewActorGhostOffsetSummary");
  if (!node) return;
  const key = selectedNumberplateActorGhostOffsetKey();
  const offset = selectedNumberplateActorGhostOffset();
  const activeCount = Object.values(state.liveryPreviewActorGhostOffsets || {}).filter((value) => numberplateActorGhostOffsetHasValue(value)).length;
  node.hidden = false;
  node.textContent = [
    `Selected: ${key || "-"}`,
    `Ghost offset: ${actorGhostOffsetSummaryText(offset)}`,
    `Active placement offsets: ${activeCount}`,
    "Preview only. No actor or game files are written.",
  ].join("\n");
}

function numberplateFitNumber(raw, fallback, min = -10, max = 10) {
  const number = Number(raw);
  if (!Number.isFinite(number)) return fallback;
  return Math.max(min, Math.min(max, number));
}

function numberplateFitDoorPlacementMode(raw) {
  return String(raw || "").trim().toLowerCase() === "fallback" ? "fallback" : "actor";
}

function normalizeNumberplateFitConfig(raw = {}) {
  const fit = raw && typeof raw === "object" ? raw : {};
  return {
    boundsScale: numberplateFitNumber(fit.boundsScale, NUMBERPLATE_FIT_DEFAULT.boundsScale, -5, 5),
    widthScale: numberplateFitNumber(fit.widthScale, NUMBERPLATE_FIT_DEFAULT.widthScale, -5, 5),
    lengthScale: numberplateFitNumber(fit.lengthScale, NUMBERPLATE_FIT_DEFAULT.lengthScale, -5, 5),
    heightScale: numberplateFitNumber(fit.heightScale, NUMBERPLATE_FIT_DEFAULT.heightScale, -5, 5),
    plateScale: numberplateFitNumber(fit.plateScale, NUMBERPLATE_FIT_DEFAULT.plateScale, 0.1, 3),
    lengthDirection: numberplateFitNumber(fit.lengthDirection, NUMBERPLATE_FIT_DEFAULT.lengthDirection, -1, 1) < 0 ? -1 : 1,
    xOffset: numberplateFitNumber(fit.xOffset, NUMBERPLATE_FIT_DEFAULT.xOffset, -10, 10),
    yOffset: numberplateFitNumber(fit.yOffset, NUMBERPLATE_FIT_DEFAULT.yOffset, -10, 10),
    zOffset: numberplateFitNumber(fit.zOffset, NUMBERPLATE_FIT_DEFAULT.zOffset, -10, 10),
    hoodYOffset: numberplateFitNumber(fit.hoodYOffset, NUMBERPLATE_FIT_DEFAULT.hoodYOffset, -10, 10),
    hoodZOffset: numberplateFitNumber(fit.hoodZOffset, NUMBERPLATE_FIT_DEFAULT.hoodZOffset, -10, 10),
    hoodScale: numberplateFitNumber(fit.hoodScale, NUMBERPLATE_FIT_DEFAULT.hoodScale, 0.1, 3),
    hoodPitch: numberplateFitNumber(fit.hoodPitch, NUMBERPLATE_FIT_DEFAULT.hoodPitch, -180, 180),
    hoodYaw: numberplateFitNumber(fit.hoodYaw, NUMBERPLATE_FIT_DEFAULT.hoodYaw, -180, 180),
    hoodRoll: numberplateFitNumber(fit.hoodRoll, NUMBERPLATE_FIT_DEFAULT.hoodRoll, -180, 180),
    doorPitch: numberplateFitNumber(fit.doorPitch, NUMBERPLATE_FIT_DEFAULT.doorPitch, -180, 180),
    doorYaw: numberplateFitNumber(fit.doorYaw, NUMBERPLATE_FIT_DEFAULT.doorYaw, -180, 180),
    doorRoll: numberplateFitNumber(fit.doorRoll, NUMBERPLATE_FIT_DEFAULT.doorRoll, -180, 180),
    doorPlacementMode: numberplateFitDoorPlacementMode(fit.doorPlacementMode || NUMBERPLATE_FIT_DEFAULT.doorPlacementMode),
  };
}

function setNumberplateFitInput(id, value, digits = 3) {
  const input = $(id);
  if (!input) return;
  input.value = Number(value ?? 0).toFixed(digits).replace(/0+$/, "").replace(/\.$/, "");
}

function setNumberplateFitInputs(fit = {}) {
  const normalized = normalizeNumberplateFitConfig(fit);
  setNumberplateFitInput("liveryPreviewNumberplateFitBounds", normalized.boundsScale);
  setNumberplateFitInput("liveryPreviewNumberplateFitWidth", normalized.widthScale);
  setNumberplateFitInput("liveryPreviewNumberplateFitLength", normalized.lengthScale);
  setNumberplateFitInput("liveryPreviewNumberplateFitHeight", normalized.heightScale);
  setNumberplateFitInput("liveryPreviewNumberplateFitPlateScale", normalized.plateScale);
  const flip = $("liveryPreviewNumberplateFitFlipLength");
  if (flip) flip.checked = normalized.lengthDirection < 0;
  setNumberplateFitInput("liveryPreviewNumberplateFitX", normalized.xOffset);
  setNumberplateFitInput("liveryPreviewNumberplateFitY", normalized.yOffset);
  setNumberplateFitInput("liveryPreviewNumberplateFitZ", normalized.zOffset);
  setNumberplateFitInput("liveryPreviewNumberplateFitHoodY", normalized.hoodYOffset);
  setNumberplateFitInput("liveryPreviewNumberplateFitHoodZ", normalized.hoodZOffset);
  setNumberplateFitInput("liveryPreviewNumberplateFitHoodScale", normalized.hoodScale);
  setNumberplateFitInput("liveryPreviewNumberplateFitHoodPitch", normalized.hoodPitch, 1);
  setNumberplateFitInput("liveryPreviewNumberplateFitHoodYaw", normalized.hoodYaw, 1);
  setNumberplateFitInput("liveryPreviewNumberplateFitHoodRoll", normalized.hoodRoll, 1);
  setNumberplateFitInput("liveryPreviewNumberplateFitDoorPitch", normalized.doorPitch, 1);
  setNumberplateFitInput("liveryPreviewNumberplateFitDoorYaw", normalized.doorYaw, 1);
  setNumberplateFitInput("liveryPreviewNumberplateFitDoorRoll", normalized.doorRoll, 1);
  const doorMode = $("liveryPreviewNumberplateFitDoorMode");
  if (doorMode) doorMode.value = normalized.doorPlacementMode;
  const debugLabels = $("liveryPreviewNumberplateDebugLabels");
  if (debugLabels) debugLabels.checked = state.liveryPreviewNumberplateDebugLabels !== false;
}

function numberplateFitFromInputs() {
  return normalizeNumberplateFitConfig({
    boundsScale: $("liveryPreviewNumberplateFitBounds")?.value,
    widthScale: $("liveryPreviewNumberplateFitWidth")?.value,
    lengthScale: $("liveryPreviewNumberplateFitLength")?.value,
    heightScale: $("liveryPreviewNumberplateFitHeight")?.value,
    plateScale: $("liveryPreviewNumberplateFitPlateScale")?.value,
    lengthDirection: $("liveryPreviewNumberplateFitFlipLength")?.checked ? -1 : 1,
    xOffset: $("liveryPreviewNumberplateFitX")?.value,
    yOffset: $("liveryPreviewNumberplateFitY")?.value,
    zOffset: $("liveryPreviewNumberplateFitZ")?.value,
    hoodYOffset: $("liveryPreviewNumberplateFitHoodY")?.value,
    hoodZOffset: $("liveryPreviewNumberplateFitHoodZ")?.value,
    hoodScale: $("liveryPreviewNumberplateFitHoodScale")?.value,
    hoodPitch: $("liveryPreviewNumberplateFitHoodPitch")?.value,
    hoodYaw: $("liveryPreviewNumberplateFitHoodYaw")?.value,
    hoodRoll: $("liveryPreviewNumberplateFitHoodRoll")?.value,
    doorPitch: $("liveryPreviewNumberplateFitDoorPitch")?.value,
    doorYaw: $("liveryPreviewNumberplateFitDoorYaw")?.value,
    doorRoll: $("liveryPreviewNumberplateFitDoorRoll")?.value,
    doorPlacementMode: $("liveryPreviewNumberplateFitDoorMode")?.value || state.liveryPreviewNumberplateFit?.doorPlacementMode,
  });
}

function numberplateFitSummary(fit = state.liveryPreviewNumberplateFit || NUMBERPLATE_FIT_DEFAULT) {
  const normalized = normalizeNumberplateFitConfig(fit);
  return [
    `bounds ${normalized.boundsScale}`,
    `width ${normalized.widthScale}`,
    `length ${normalized.lengthScale}`,
    `direction ${normalized.lengthDirection < 0 ? "flipped" : "normal"}`,
    `height ${normalized.heightScale}`,
    `plate ${normalized.plateScale}`,
    `offset ${normalized.xOffset} / ${normalized.yOffset} / ${normalized.zOffset}`,
    `hood ${normalized.hoodYOffset} / ${normalized.hoodZOffset} / ${normalized.hoodScale}`,
    `hood rot ${normalized.hoodPitch} / ${normalized.hoodYaw} / ${normalized.hoodRoll}`,
    `door rot ${normalized.doorPitch} / ${normalized.doorYaw} / ${normalized.doorRoll}`,
    `door mode ${normalized.doorPlacementMode}`,
    `labels ${state.liveryPreviewNumberplateDebugLabels !== false ? "on" : "off"}`,
  ].join(" | ");
}

function applyNumberplateFitToViewer(fit = state.liveryPreviewNumberplateFit || NUMBERPLATE_FIT_DEFAULT) {
  state.liveryPreviewNumberplateFit = normalizeNumberplateFitConfig(fit);
  return window.LiveryLabPreview3d?.setNumberplateFit?.(state.liveryPreviewNumberplateFit) || state.liveryPreviewNumberplateFit;
}

async function refreshNumberplateFitPreview() {
  window.clearTimeout(state.liveryPreviewNumberplateFitTimer);
  applyNumberplateFitToViewer(numberplateFitFromInputs());
  if ($("liveryPreviewShowNumberplates")?.checked !== false) {
    await showMappedNumberplates();
  } else {
    $("liveryPreviewNumberplateInfo").textContent = `Numberplate fit applied: ${numberplateFitSummary()}`;
  }
}

function scheduleNumberplateFitPreviewRefresh() {
  window.clearTimeout(state.liveryPreviewNumberplateFitTimer);
  state.liveryPreviewNumberplateFit = normalizeNumberplateFitConfig(numberplateFitFromInputs());
  applyNumberplateFitToViewer(state.liveryPreviewNumberplateFit);
  state.liveryPreviewNumberplateFitTimer = window.setTimeout(() => {
    refreshNumberplateFitPreview().catch((error) => {
      $("liveryPreviewNumberplateInfo").textContent = `Numberplate fit refresh failed: ${error?.message || error}`;
    });
  }, 120);
}

function resetNumberplateFitControls() {
  const fit = window.LiveryLabPreview3d?.resetNumberplateFit?.() || NUMBERPLATE_FIT_DEFAULT;
  state.liveryPreviewNumberplateFit = normalizeNumberplateFitConfig(fit);
  setNumberplateFitInputs(state.liveryPreviewNumberplateFit);
  refreshNumberplateFitPreview().catch((error) => {
    $("liveryPreviewNumberplateInfo").textContent = `Numberplate fit reset failed: ${error?.message || error}`;
  });
}

async function copyNumberplateFitValues() {
  const fit = normalizeNumberplateFitConfig(state.liveryPreviewNumberplateFit || numberplateFitFromInputs());
  const payload = {
    carId: liveryPreviewCarId(),
    numberplateFit: fit,
  };
  const text = JSON.stringify(payload, null, 2);
  try {
    await navigator.clipboard.writeText(text);
    $("liveryPreviewNumberplateInfo").textContent = `Copied numberplate fit:\n${text}`;
  } catch (_error) {
    $("liveryPreviewNumberplateInfo").textContent = `Numberplate fit:\n${text}`;
  }
}

function formatPreviewMaterialInspection(data) {
  const materials = data?.materials || [];
  const lines = [
    "3D Preview material inspection",
    `Car: ${data?.carModel || "-"}`,
    `Mesh: ${data?.meshPath || "-"}`,
    `Materials: ${materials.length}`,
    `Texture slots: ${data?.textureSlotCount ?? 0}`,
  ];
  if (data?.warnings?.length) {
    lines.push("", "Warnings:", ...data.warnings.map((row) => `- ${row}`));
  }
  materials.forEach((material) => {
    lines.push(
      "",
      `# ${material.name || "-"}`,
      `Path: ${material.materialPath || "-"}`,
      `Shader: ${material.shader || "-"}`,
      `Parent: ${material.parentMaterialPath || "-"}`,
      `Indices: ${material.indexCount ?? "-"}`,
    );
    const slots = material.textureSlots || [];
    if (slots.length) {
      lines.push("Texture slots:");
      slots.slice(0, 18).forEach((slot) => {
        const size = slot.width && slot.height ? ` ${slot.width}x${slot.height}` : "";
        const channel = slot.previewChannel ? ` [${slot.previewChannel}]` : "";
        lines.push(`- ${slot.name || "-"}${channel}: ${slot.value || "-"}${size}`);
      });
      if (slots.length > 18) lines.push(`- ... ${slots.length - 18} more`);
    }
    const params = (material.parameters || []).filter((param) => param.value !== null && param.value !== undefined && param.value !== "");
    if (params.length) {
      lines.push("Parameters:");
      params.slice(0, 8).forEach((param) => lines.push(`- ${param.name || "-"}: ${Array.isArray(param.value) ? param.value.join(", ") : param.value}`));
      if (params.length > 8) lines.push(`- ... ${params.length - 8} more`);
    }
  });
  return lines.join("\n");
}

function setLiveryPreviewMaterialPanelVisible(visible) {
  const workspace = $("liveryPreviewWorkspace");
  const panel = $("liveryPreviewMaterialPanel");
  const button = $("liveryPreviewInspectMaterials");
  if (!panel) return;
  if (visible) {
    setLiveryPreviewWheelPanelVisible(false);
    const finishPanel = $("liveryPreviewMaterialFinishPanel");
    if (finishPanel) finishPanel.hidden = true;
    workspace?.classList.remove("is-material-finish-open");
    $("liveryPreviewToggleMaterialFinish")?.classList.remove("is-active-danger");
  }
  panel.hidden = !visible;
  workspace?.classList.toggle("is-material-inspector-open", Boolean(visible));
  button?.classList.toggle("is-active-danger", Boolean(visible));
}

function setLiveryPreviewWheelPanelVisible(visible) {
  const workspace = $("liveryPreviewWorkspace");
  const panel = $("liveryPreviewWheelPanel");
  const button = $("liveryPreviewWheelDiagnostics");
  if (!panel) return;
  visible = Boolean(visible) && debugToolEnabled("wheelCalibration");
  if (visible) {
    setLiveryPreviewMaterialPanelVisible(false);
    setLiveryPreviewMaterialFinishPanelVisible(false);
  }
  panel.hidden = !visible;
  workspace?.classList.toggle("is-wheel-diagnostics-open", Boolean(visible));
  button?.classList.toggle("is-active-danger", Boolean(visible));
  if (visible) renderLiveryPreviewWheelDiagnostics();
  refreshLiveryPreviewLayout();
}

function wheelDiagValue(value, unit = "") {
  if (value === null || value === undefined || value === "") return "-";
  const number = Number(value);
  if (Number.isFinite(number)) return `${number.toFixed(3).replace(/0+$/, "").replace(/\.$/, "")}${unit}`;
  return String(value);
}

function wheelDiagTransformText(transform = {}) {
  const parts = [
    `x ${wheelDiagValue(transform.x, "m")}`,
    `y ${wheelDiagValue(transform.y, "m")}`,
    `z ${wheelDiagValue(transform.z, "m")}`,
  ];
  const rotation = [transform.rx, transform.ry, transform.rz].some((value) => Number(value || 0) !== 0)
    ? ` | rot ${wheelDiagValue(transform.rx, "deg")} / ${wheelDiagValue(transform.ry, "deg")} / ${wheelDiagValue(transform.rz, "deg")}`
    : "";
  return `${parts.join(" | ")}${rotation}`;
}

function renderWheelDiagStat(label, value) {
  return `<div><span>${escapeHtml(label)}</span><strong>${escapeHtml(value)}</strong></div>`;
}

function debugToolEnabled(toolName) {
  if (!state.debugMode) return false;
  if (!state.debugTools || typeof state.debugTools !== "object") return true;
  if (!Object.prototype.hasOwnProperty.call(state.debugTools, toolName)) return true;
  return Boolean(state.debugTools[toolName]);
}

function updateDebugUi() {
  document.documentElement.classList.toggle("is-debug-mode", Boolean(state.debugMode));
  document.querySelectorAll("[data-debug-tool]").forEach((element) => {
    const toolName = element.getAttribute("data-debug-tool") || "";
    element.hidden = !debugToolEnabled(toolName);
  });
  if (!debugToolEnabled("wheelCalibration")) {
    window.LiveryLabPreview3d?.setWheelTunerEnabled?.(false);
    state.liveryPreviewWheelTunerEnabled = false;
    state.liveryPreviewWheelUnsavedChanges = false;
    setLiveryPreviewWheelPanelVisible(false);
  }
  if (!debugToolEnabled("sceneSettings")) {
    setLiveryPreviewSceneSettingsPanelVisible(false);
  }
  syncLiveryPreviewNumberplateSupportUi();
  updateLiveryPreviewWheelUnsavedIndicator();
  updateNumberplateGizmoUnsavedIndicator();
}

function applyDebugSettings(settings = {}) {
  const debug = settings.debug && typeof settings.debug === "object" ? settings.debug : {};
  state.debugMode = Boolean(debug.enabled);
  state.debugTools = debug.tools && typeof debug.tools === "object" ? debug.tools : {};
  updateDebugUi();
}

function updateLiveryPreviewWheelUnsavedIndicator(tunerState = null) {
  const tuner = tunerState || window.LiveryLabPreview3d?.getWheelTunerState?.() || {};
  state.liveryPreviewWheelUnsavedChanges = Boolean(tuner.dirty);
  const badge = $("liveryPreviewWheelUnsaved");
  if (badge) badge.hidden = !(debugToolEnabled("wheelCalibration") && state.liveryPreviewWheelUnsavedChanges);
}

function liveryPreviewWheelDiagnosticsPayload() {
  const tunerState = window.LiveryLabPreview3d?.getWheelTunerState?.() || {};
  return {
    carModel: state.liveryPreviewProject?.carModel || liveryPreviewCarId(),
    skinKey: state.liveryPreviewSkinKey || "",
    wheelPreview: state.liveryPreviewWheelPreview || {},
    tuner: tunerState,
  };
}

async function copyLiveryPreviewWheelDiagnostics() {
  if (!debugToolEnabled("wheelCalibration")) {
    $("liveryPreviewStatus").textContent = "Wheel diagnostics require debug mode.";
    return;
  }
  const text = JSON.stringify(liveryPreviewWheelDiagnosticsPayload(), null, 2);
  try {
    await navigator.clipboard.writeText(text);
    $("liveryPreviewStatus").textContent = "Wheel diagnostics copied to clipboard.";
  } catch (_error) {
    $("liveryPreviewStatus").textContent = `Wheel diagnostics:\n${text}`;
  }
}

function setLiveryPreviewWheelTuner(enabled = true) {
  if (enabled && !debugToolEnabled("wheelCalibration")) {
    $("liveryPreviewStatus").textContent = "Wheel tuning requires debug mode.";
    return;
  }
  const target = $("liveryPreviewWheelTuneTarget")?.value || "all";
  const result = window.LiveryLabPreview3d?.setWheelTunerEnabled?.(enabled, target) || {};
  state.liveryPreviewWheelTunerEnabled = Boolean(result.enabled);
  const button = $("liveryPreviewWheelTune");
  if (button) button.textContent = state.liveryPreviewWheelTunerEnabled ? "Stop tuning" : "Tune";
  $("liveryPreviewStatus").textContent = state.liveryPreviewWheelTunerEnabled
    ? `Wheel tuner enabled for ${target} (${result.selectedCount || 0} objects). Drag the X/Y/Z handles in the preview.`
    : "Wheel tuner disabled.";
  updateLiveryPreviewWheelUnsavedIndicator(result);
  renderLiveryPreviewWheelDiagnostics();
}

function updateLiveryPreviewWheelTunerTarget() {
  const target = $("liveryPreviewWheelTuneTarget")?.value || "all";
  const result = window.LiveryLabPreview3d?.setWheelTunerTarget?.(target) || {};
  if (state.liveryPreviewWheelTunerEnabled) {
    $("liveryPreviewStatus").textContent = `Wheel tuner target: ${target} (${result.selectedCount || 0} objects).`;
  }
  updateLiveryPreviewWheelUnsavedIndicator(result);
  renderLiveryPreviewWheelDiagnostics();
}

async function saveLiveryPreviewWheelCalibration() {
  if (!debugToolEnabled("wheelCalibration")) {
    $("liveryPreviewStatus").textContent = "Wheel calibration save requires debug mode.";
    return;
  }
  const carModel = state.liveryPreviewProject?.carModel || liveryPreviewCarId();
  const overrides = window.LiveryLabPreview3d?.getWheelCalibrationOverrides?.() || {};
  if (!carModel) {
    $("liveryPreviewStatus").textContent = "Wheel calibration save failed: no car model is attached to this preview.";
    return;
  }
  const result = await window.pywebview.api.save_wheel_preview_calibration({
    carModel,
    wheelOverrides: overrides,
  });
  if (!result.ok) {
    $("liveryPreviewStatus").textContent = `Wheel calibration save failed:\n${result.message || ""}`;
    return;
  }
  $("liveryPreviewStatus").textContent = [
    result.message || "Wheel preview calibration saved.",
    `File: ${result.data?.path || "-"}`,
    result.data?.backup ? `Backup: ${result.data.backup}` : "",
    result.data?.sourceMirrorPath ? `Source mirror: ${result.data.sourceMirrorPath}` : "",
    result.data?.sourceMirrorBackup ? `Source mirror backup: ${result.data.sourceMirrorBackup}` : "",
    "Reloading the preview will apply this calibration automatically.",
  ].filter(Boolean).join("\n");
  updateLiveryPreviewWheelUnsavedIndicator(window.LiveryLabPreview3d?.markWheelCalibrationSaved?.() || {});
  renderLiveryPreviewWheelDiagnostics();
}

function renderLiveryPreviewWheelDiagnostics() {
  const summary = $("liveryPreviewWheelSummary");
  const content = $("liveryPreviewWheelContent");
  if (!summary || !content) return;
  const data = state.liveryPreviewWheelPreview || {};
  const auxModels = data.auxiliaryModels || [];
  const meshRefs = data.meshRefs || [];
  const layout = data.layout || {};
  const warnings = data.warnings || [];
  const tuner = window.LiveryLabPreview3d?.getWheelTunerState?.() || {};
  updateLiveryPreviewWheelUnsavedIndicator(tuner);
  if (!Object.keys(data).length) {
    summary.textContent = "No wheel preview data yet";
    content.innerHTML = "<p>Load a livery preview to inspect rim and tyre placement data.</p>";
    return;
  }

  const foundMeshes = meshRefs.filter((row) => row.exists).length;
  const calibration = layout.calibration || {};
  const calibrationValues = calibration.values || {};
  summary.textContent = [
    `${auxModels.length} auxiliary GLBs`,
    `${foundMeshes}/${meshRefs.length} source meshes`,
    warnings.length ? `${warnings.length} warnings` : "no warnings",
  ].join(" | ");

  const rimInfo = data.rimmeshInfo || {};
  const frontInfo = rimInfo.front || {};
  const rearInfo = rimInfo.rear || {};
  const stats = [
    renderWheelDiagStat("compatiblerim", data.compatiblerim || "-"),
    renderWheelDiagStat("front rimmesh", data.rimmesh || "-"),
    renderWheelDiagStat("rear rimmesh", data.rearRimmesh || "-"),
    renderWheelDiagStat("tyre texture set", data.tyreTextureSet || "-"),
    renderWheelDiagStat("wheelbase", wheelDiagValue(layout.wheelbase, "m")),
    renderWheelDiagStat("front track", wheelDiagValue(layout.frontTrack, "m")),
    renderWheelDiagStat("rear track", wheelDiagValue(layout.rearTrack, "m")),
    renderWheelDiagStat("calibration", calibration.car ? `${calibration.car} (${Object.keys(calibrationValues).length} values)` : "none"),
    renderWheelDiagStat("tuner", tuner.enabled ? `${tuner.target || "all"} (${tuner.selectedCount || 0} objects)` : "off"),
    renderWheelDiagStat("unsaved", tuner.dirty ? "yes" : "no"),
  ].join("");

  const calibrationRows = Object.entries(calibrationValues)
    .map(([key, value]) => `<li><strong>${escapeHtml(key)}</strong><span>${escapeHtml(wheelDiagValue(value, "m"))}</span></li>`)
    .join("");
  const rimRows = [
    ["front", frontInfo],
    ["rear", rearInfo],
  ].map(([label, info]) => `<li>
    <strong>${escapeHtml(label)}</strong>
    <span>${escapeHtml([info.brand, info.name].filter(Boolean).join(" ") || "-")}</span>
    <span>diameter ${escapeHtml(wheelDiagValue(info.diameter))} | width ${escapeHtml(wheelDiagValue(info.width))} | offset ${escapeHtml(wheelDiagValue(info.offset))}</span>
  </li>`).join("");

  const meshRows = meshRefs.length
    ? meshRefs.map((row) => `<tr>
      <td>${escapeHtml(row.label || "-")}</td>
      <td>${row.exists ? "yes" : "no"}</td>
      <td>${escapeHtml(row.sourceRelPath || row.relPath || "-")}</td>
      <td>${escapeHtml(wheelDiagTransformText(row.transform || {}))}</td>
    </tr>`).join("")
    : `<tr><td colspan="4">No wheel or tyre mesh references were found.</td></tr>`;

  const auxRows = auxModels.length
    ? auxModels.map((row) => `<tr>
      <td>${escapeHtml(row.label || "-")}</td>
      <td>${row.cached ? "cache" : "built"}</td>
      <td>${escapeHtml(row.meshRelPath || row.sourceRelPath || "-")}</td>
      <td>${escapeHtml(row.path || "-")}</td>
    </tr>`).join("")
    : `<tr><td colspan="4">No auxiliary GLBs were exported.</td></tr>`;

  content.innerHTML = `
    <section class="livery-preview-wheel-section">
      <h3>Resolved Rim Data</h3>
      <div class="livery-preview-wheel-stats">${stats}</div>
    </section>
    <section class="livery-preview-wheel-section">
      <h3>Rim Metadata</h3>
      <ul class="livery-preview-material-list">${rimRows}</ul>
    </section>
    ${calibrationRows ? `<section class="livery-preview-wheel-section">
      <h3>Applied Calibration</h3>
      <ul class="livery-preview-material-list">${calibrationRows}</ul>
    </section>` : ""}
    ${Object.keys(tuner.overrides || {}).length ? `<section class="livery-preview-wheel-section">
      <h3>Unsaved Tuner Offsets</h3>
      <ul class="livery-preview-material-list">${Object.entries(tuner.overrides).map(([label, values]) => `<li><strong>${escapeHtml(label)}</strong><span>${escapeHtml(wheelDiagTransformText(values))}</span></li>`).join("")}</ul>
    </section>` : ""}
    <section class="livery-preview-wheel-section">
      <h3>Source Mesh Placement</h3>
      <div class="livery-preview-wheel-table-wrap">
        <table class="livery-preview-wheel-table">
          <thead><tr><th>Label</th><th>Found</th><th>Source</th><th>Transform</th></tr></thead>
          <tbody>${meshRows}</tbody>
        </table>
      </div>
    </section>
    <section class="livery-preview-wheel-section">
      <h3>Auxiliary GLB Cache</h3>
      <div class="livery-preview-wheel-table-wrap">
        <table class="livery-preview-wheel-table">
          <thead><tr><th>Label</th><th>State</th><th>Source</th><th>GLB</th></tr></thead>
          <tbody>${auxRows}</tbody>
        </table>
      </div>
    </section>
    ${warnings.length ? `<section class="livery-preview-wheel-section is-warning">
      <h3>Warnings</h3>
      <ul class="livery-preview-material-list">${warnings.map((row) => `<li><span>${escapeHtml(row)}</span></li>`).join("")}</ul>
    </section>` : ""}
  `;
}

function closeLiveryPreviewMenus(except = null) {
  document.querySelectorAll("#liveryPreviewWorkspace .livery-preview-menu-group.is-open").forEach((group) => {
    if (except && group === except) return;
    group.classList.remove("is-open");
    group.querySelector(".livery-preview-menu-trigger")?.setAttribute("aria-expanded", "false");
  });
}

function refreshLiveryPreviewLayout() {
  window.requestAnimationFrame(() => {
    window.requestAnimationFrame(() => {
      window.LiveryLabPreview3d?.resize?.();
      window.dispatchEvent(new Event("resize"));
    });
  });
}

function placeNumberplateInfo(slotId = "liveryPreviewNumberplateMainInfoSlot") {
  const info = $("liveryPreviewNumberplateInfo");
  const slot = $(slotId);
  if (info && slot && info.parentElement !== slot) slot.append(info);
}

function setLiveryPreviewNumberplatePanelVisible(visible) {
  if (!NUMBERPLATE_OVERLAY_WORKFLOW_ENABLED && visible) return;
  const panel = $("liveryPreviewNumberplatePanel");
  const workspace = $("liveryPreviewWorkspace");
  const button = $("liveryPreviewToggleNumberplate");
  const next = Boolean(visible);
  if (next) {
    placeNumberplateInfo("liveryPreviewNumberplateMainInfoSlot");
    setLiveryPreviewNumberplateDevPanelVisible(false);
    const scenePanel = $("liveryPreviewSceneSettingsPanel");
    const sceneButton = $("liveryPreviewToggleSceneSettings");
    if (scenePanel) scenePanel.hidden = true;
    workspace?.classList.remove("is-scene-settings-panel-open");
    sceneButton?.classList.remove("is-active-danger");
  }
  if (panel) panel.hidden = !next;
  workspace?.classList.toggle("is-numberplate-panel-open", next);
  button?.classList.toggle("is-active-danger", next);
  refreshLiveryPreviewLayout();
}

function setLiveryPreviewNumberplateDevPanelVisible(visible) {
  if (!NUMBERPLATE_OVERLAY_WORKFLOW_ENABLED && visible) return;
  const panel = $("liveryPreviewNumberplateDevPanel");
  const workspace = $("liveryPreviewWorkspace");
  const button = $("liveryPreviewToggleNumberplateDev");
  const next = Boolean(visible);
  if (next && !debugToolEnabled("numberplateCalibration")) {
    $("liveryPreviewStatus").textContent = "Numberplate dev tools require dev mode.";
    return;
  }
  if (next) {
    placeNumberplateInfo("liveryPreviewNumberplateDevInfoSlot");
    setNumberplateActorProjectionOffsetInputs();
    setLiveryPreviewNumberplatePanelVisible(false);
    setLiveryPreviewSceneSettingsPanelVisible(false);
    setLiveryPreviewCameraCalibrationPanelVisible(false);
  }
  if (panel) panel.hidden = !next;
  workspace?.classList.toggle("is-numberplate-dev-panel-open", next);
  button?.classList.toggle("is-active-danger", next);
  refreshLiveryPreviewLayout();
}

function setLiveryPreviewNumberplatesVisible(visible) {
  state.liveryPreviewNumberplatesVisible = Boolean(visible);
  const checkbox = $("liveryPreviewShowNumberplates");
  if (checkbox) checkbox.checked = state.liveryPreviewNumberplatesVisible;
}

function liveryPreviewNumberplatesBlocked() {
  return !NUMBERPLATE_OVERLAY_WORKFLOW_ENABLED || numberplatesBlockedForNormalMode(state.liveryPreviewNumberplateSupport || state.liveryPreviewProject || {});
}

function liveryPreviewNumberplatesHidden() {
  return numberplateVariantHidden(state.liveryPreviewProject || {});
}

function syncLiveryPreviewNumberplateSupportUi() {
  const support = normalizeNumberplateSupport(state.liveryPreviewNumberplateSupport || state.liveryPreviewProject || {});
  const workflowDisabled = !NUMBERPLATE_OVERLAY_WORKFLOW_ENABLED;
  const blocked = workflowDisabled || support.supported === false;
  const hidden = liveryPreviewNumberplatesHidden();
  const message = workflowDisabled ? NUMBERPLATE_WORKFLOW_DISABLED_MESSAGE : (support.message || "This car does not support numberplates yet");
  const hiddenMessage = "Numberplates are disabled for this skin.";
  const group = $("liveryPreviewNumberplateMenuGroup");
  const trigger = $("liveryPreviewNumberplateMenuTrigger");
  const editButton = $("liveryPreviewToggleNumberplate");
  const checkbox = $("liveryPreviewShowNumberplates");
  if (workflowDisabled && group) group.hidden = true;
  group?.classList.toggle("is-disabled", blocked || hidden);
  if (trigger) {
    trigger.disabled = blocked || hidden;
    trigger.title = blocked ? message : (hidden ? hiddenMessage : "");
  }
  if (editButton) {
    editButton.disabled = blocked || hidden;
    editButton.title = blocked ? message : (hidden ? hiddenMessage : "");
  }
  if (checkbox) {
    checkbox.disabled = blocked || hidden;
    checkbox.title = blocked ? message : (hidden ? hiddenMessage : "");
  }
  if (blocked) {
    setLiveryPreviewNumberplatePanelVisible(false);
    setLiveryPreviewNumberplateDevPanelVisible(false);
    setLiveryPreviewNumberplatesVisible(false);
    window.LiveryLabPreview3d?.removeNumberplateOverlay?.();
  } else if (hidden) {
    setLiveryPreviewNumberplatesVisible(false);
    window.LiveryLabPreview3d?.removeNumberplateOverlay?.();
  }
}

function openLiveryPreviewMenu(group) {
  if (!group) return;
  if (group.classList.contains("is-disabled")) return;
  closeLiveryPreviewMenus(group);
  group.classList.add("is-open");
  group.querySelector(".livery-preview-menu-trigger")?.setAttribute("aria-expanded", "true");
}

function initLiveryPreviewMenuController() {
  const menu = document.querySelector("#liveryPreviewWorkspace .livery-preview-menu");
  if (!menu || menu.dataset.controllerReady === "1") return;
  menu.dataset.controllerReady = "1";
  const groups = [...menu.querySelectorAll(".livery-preview-menu-group")];

  groups.forEach((group) => {
    const trigger = group.querySelector(".livery-preview-menu-trigger");
    const dropdown = group.querySelector(".livery-preview-menu-dropdown");
    trigger?.setAttribute("aria-haspopup", "menu");
    trigger?.setAttribute("aria-expanded", "false");
    group.addEventListener("pointerenter", () => openLiveryPreviewMenu(group));
    trigger?.addEventListener("focus", () => openLiveryPreviewMenu(group));
    trigger?.addEventListener("click", (event) => {
      event.preventDefault();
      if (group.classList.contains("is-open")) closeLiveryPreviewMenus();
      else openLiveryPreviewMenu(group);
    });
    dropdown?.addEventListener("click", (event) => {
      const target = event.target;
      if (target?.closest?.("input, select, label")) return;
      if (target?.closest?.("button")) closeLiveryPreviewMenus();
    });
  });

  [...menu.children].forEach((child) => {
    if (!(child instanceof HTMLElement)) return;
    if (child.classList.contains("livery-preview-menu-group")) return;
    child.addEventListener("pointerenter", () => closeLiveryPreviewMenus());
    child.addEventListener("focus", () => closeLiveryPreviewMenus());
    child.addEventListener("click", () => closeLiveryPreviewMenus());
  });

  menu.addEventListener("pointerleave", () => {
    window.setTimeout(() => {
      const focused = document.activeElement;
      if (focused && menu.contains(focused) && focused.matches("input, select")) return;
      closeLiveryPreviewMenus();
    }, 120);
  });
  document.addEventListener("pointerdown", (event) => {
    if (!menu.contains(event.target)) closeLiveryPreviewMenus();
  });
  document.addEventListener("keydown", (event) => {
    if (event.key === "Escape") closeLiveryPreviewMenus();
  });
}

function setLiveryPreviewMaterialOverlayButton(active) {
  const button = $("liveryPreviewMaterialOverlay");
  if (!button) return;
  button.classList.toggle("is-active-danger", Boolean(active));
}

function setLiveryPreviewApproxMaterialsButton(active) {
  const button = $("liveryPreviewApproxMaterials");
  if (!button) return;
  button.classList.toggle("is-active-danger", Boolean(active));
  button.setAttribute("aria-pressed", active ? "true" : "false");
}

function activeLiveryPreviewMaterialMode() {
  const previewMode = normalizeLiveryPreviewMode(state.liveryPreviewPreviewMode);
  if (previewMode === "materialView") return "finish";
  if (previewMode === "evoApprox" && state.liveryPreviewApproxMaterialsActive) return "approx";
  if (previewMode === "materialMask" && state.liveryPreviewMaterialOverlayActive) return "overlay";
  return "";
}

function liveryPreviewMaterialModeLegend(mode) {
  if (mode === "approx") {
    return [
      "EVO Approx:",
      `Body color: ${liveryPreviewBodyBaseColorStatus()} (${state.liveryPreviewBodyBaseColor || "#ffffff"})`,
      "Red/Yellow: authored EXT_SKIN_C overlap | Green: body/material channel | Blue: carbon",
    ];
  }
  if (mode === "overlay") {
    if (liveryPreviewMaterialMaskDisplayMode() === "channels") {
      return [
        "Material Mask:",
        "Red: body base color mask | Yellow: authored EXT_SKIN_C overlap | Green: green mask | Blue: blue mask",
      ];
    }
    return [
      "Material Mask:",
      `${liveryPreviewTextureLabel(state.liveryPreviewTextureKey)} shown as grayscale texture values.`,
    ];
  }
  if (mode === "finish") {
    const finish = normalizeLiveryMaterialFinish(state.liveryPreviewMaterialFinish);
    return [
      "Final Livery:",
      `Body color: ${liveryPreviewBodyBaseColorStatus()} (${state.liveryPreviewBodyBaseColor || "#ffffff"})`,
      `Base finish: M ${formatMaterialFinishValue(finish.metalness)} | R ${formatMaterialFinishValue(finish.roughness)} | CC ${formatMaterialFinishValue(finish.clearCoat)} | CCR ${formatMaterialFinishValue(finish.clearCoatRoughness)}`,
    ];
  }
  return [];
}

async function reloadLiveryPreviewViewerTextureWithMaterialMode() {
  const materialMode = activeLiveryPreviewMaterialMode();
  if (materialMode === "approx") await setLiveryPreviewApproxMaterials(false, { quiet: true });
  if (materialMode === "overlay") await setLiveryPreviewMaterialOverlay(false, { quiet: true });
  const viewer = await window.LiveryLabPreview3d.reloadTexture(payloadPreview3dProject());
  let reapplied = null;
  const previewMode = normalizeLiveryPreviewMode(state.liveryPreviewPreviewMode);
  let reappliedMode = "";
  if (previewMode === "evoApprox") {
    reapplied = await setLiveryPreviewApproxMaterials(true, { quiet: true });
    reappliedMode = "approx";
  }
  if (previewMode === "materialMask") {
    reapplied = await setLiveryPreviewMaterialOverlay(true, { quiet: true });
    reappliedMode = "overlay";
  }
  if (previewMode === "materialView") {
    reapplied = true;
    reappliedMode = "finish";
  }
  return { viewer, materialMode: reapplied ? reappliedMode : "" };
}

async function setLiveryPreviewMaterialOverlay(active, options = {}) {
  const enabled = Boolean(active);
  const payload = payloadPreview3dProject();
  const maskTexturePath = payload.selectedTexturePath || payload.detailTexturePath || "";
  if (enabled && !maskTexturePath) {
    $("liveryPreviewStatus").textContent = "Material overlay needs the selected material texture in the current preview project.";
    state.liveryPreviewMaterialOverlayActive = false;
    setLiveryPreviewMaterialOverlayButton(false);
    return false;
  }
  try {
    const result = await window.LiveryLabPreview3d?.setMaterialChannelOverlay?.({
      enabled,
      detailTexturePath: maskTexturePath,
      materialName: payload.materialName || "EXT_SKIN",
      hiddenMaterialNames: payload.hiddenMaterialNames || [],
      selectedTextureKey: payload.selectedTextureKey || state.liveryPreviewTextureKey || "ext_skin_d",
      displayMode: liveryPreviewMaterialMaskDisplayMode(payload.selectedTextureKey || state.liveryPreviewTextureKey),
    });
    state.liveryPreviewMaterialOverlayActive = Boolean(result?.enabled);
    if (state.liveryPreviewMaterialOverlayActive) {
      state.liveryPreviewApproxMaterialsActive = false;
      setLiveryPreviewApproxMaterialsButton(false);
    }
    setLiveryPreviewMaterialOverlayButton(state.liveryPreviewMaterialOverlayActive);
    if (!options.quiet) $("liveryPreviewStatus").textContent = result?.enabled
      ? [
          "Material channel overlay enabled.",
          ...liveryPreviewMaterialModeLegend("overlay"),
          `Textured materials: ${result.appliedMaterials ?? 0}`,
        ].join("\n")
      : "Material channel overlay disabled.";
    return state.liveryPreviewMaterialOverlayActive;
  } catch (error) {
    state.liveryPreviewMaterialOverlayActive = false;
    setLiveryPreviewMaterialOverlayButton(false);
    $("liveryPreviewStatus").textContent = `Material overlay failed:\n${error?.message || error}`;
    return false;
  }
}

async function setLiveryPreviewApproxMaterials(active, options = {}) {
  const enabled = Boolean(active);
  const payload = payloadPreview3dProject();
  if (enabled && normalizeLiveryPreviewMode(state.liveryPreviewPreviewMode) !== "evoApprox") {
    if (!options.quiet) $("liveryPreviewStatus").textContent = "EVO Approx is only available in EVO Approx preview mode.";
    state.liveryPreviewApproxMaterialsActive = false;
    setLiveryPreviewApproxMaterialsButton(false);
    return false;
  }
  if (enabled && (!payload.colorTexturePath || !payload.detailTexturePath)) {
    if (!options.quiet) $("liveryPreviewStatus").textContent = "EVO Approx needs EXT_SKIN_C and EXT_Skin_D in the current preview project.";
    state.liveryPreviewApproxMaterialsActive = false;
    setLiveryPreviewApproxMaterialsButton(false);
    return false;
  }
  try {
    const result = await window.LiveryLabPreview3d?.setApproxMaterialOverlay?.({
      enabled,
      colorTexturePath: payload.colorTexturePath,
      detailTexturePath: payload.detailTexturePath,
      aoTexturePath: payload.aoTexturePath,
      materialName: payload.materialName || "EXT_SKIN",
      hiddenMaterialNames: payload.hiddenMaterialNames || [],
      materialLook: {
        ...currentLiveryPreviewMaterialLook(options.sceneName || state.liveryPreviewScene || "light"),
        bodyBaseColor: state.liveryPreviewBodyBaseColor || "#ffffff",
        bodyBaseColorEnabled: liveryPreviewBodyBaseColorEnabled(payload),
      },
    });
    state.liveryPreviewApproxMaterialsActive = Boolean(result?.enabled);
    if (state.liveryPreviewApproxMaterialsActive) {
      state.liveryPreviewMaterialOverlayActive = false;
      setLiveryPreviewMaterialOverlayButton(false);
    }
    setLiveryPreviewApproxMaterialsButton(state.liveryPreviewApproxMaterialsActive);
    if (!options.quiet) $("liveryPreviewStatus").textContent = result?.enabled
      ? [
          "EVO Approx preview enabled.",
          ...liveryPreviewMaterialModeLegend("approx"),
          `AO texture: ${result.aoTextureLoaded ? "loaded" : "none"}`,
          `Carbon texture: ${result.carbonTextureLoaded ? "loaded" : "procedural fallback"}`,
          `Glass/lights materials: ${result.specialMaterials ?? 0}`,
          `Textured materials: ${result.appliedMaterials ?? 0}`,
        ].join("\n")
      : "EVO Approx preview disabled.";
    return state.liveryPreviewApproxMaterialsActive;
  } catch (error) {
    state.liveryPreviewApproxMaterialsActive = false;
    setLiveryPreviewApproxMaterialsButton(false);
    $("liveryPreviewStatus").textContent = `EVO Approx preview failed:\n${error?.message || error}`;
    return false;
  }
}

async function setLiveryPreviewBodyBaseColor(value, options = {}) {
  const color = applyLiveryPreviewBodyBaseColorValue(value, options.source || "user");
  if (state.liveryPreviewProject) {
    state.liveryPreviewProject = {
      ...state.liveryPreviewProject,
      bodyBaseColor: color,
    };
  }
  const previewMode = normalizeLiveryPreviewMode(state.liveryPreviewPreviewMode);
  if (previewMode === "materialView") {
    const update = window.LiveryLabPreview3d?.setMaterialViewBodyBaseColor?.(color);
    window.LiveryLabPreview3d?.setMaterialViewBodyBaseColorEnabled?.(liveryPreviewBodyBaseColorEnabled());
    if (!options.quiet && $("liveryPreviewStatus")) {
      $("liveryPreviewStatus").textContent = [
        `Body base color: ${color}`,
        `Body color: ${liveryPreviewBodyBaseColorStatus()}`,
        ...liveryPreviewMaterialModeLegend("finish"),
        `Updated materials: ${update?.updated ?? 0}`,
      ].join("\n");
    }
    return true;
  }
  if (!state.liveryPreviewApproxMaterialsActive) {
    if (!options.quiet && $("liveryPreviewStatus")) {
      $("liveryPreviewStatus").textContent = `Body base color: ${color}\nBody color: ${liveryPreviewBodyBaseColorStatus()}\nSwitch to Final Livery to preview supported EXT_Skin_D body-color areas.`;
    }
    return false;
  }
  const update = window.LiveryLabPreview3d?.setApproxBodyBaseColor?.(color);
  if (!options.quiet && $("liveryPreviewStatus")) {
    $("liveryPreviewStatus").textContent = [
      `Body base color: ${color}`,
      ...liveryPreviewMaterialModeLegend("approx"),
      `Updated materials: ${update?.updated ?? 0}`,
    ].join("\n");
  }
  return true;
}

async function detectLiveryPreviewBodyBaseColor(options = {}) {
  const payload = options.payload || payloadPreview3dProject();
  const api = window.pywebview?.api?.derive_preview3d_body_base_color;
  if (typeof api !== "function") {
    if (!options.quiet && $("liveryPreviewStatus")) $("liveryPreviewStatus").textContent = "Body base color detection needs an updated LiveryLab.exe build.";
    return null;
  }
  try {
    if (!options.quiet) $("liveryPreviewStatus").textContent = "Detecting body base color from EXT_SKIN_C and EXT_Skin_D...";
    const result = await api(payload);
    if (!result?.ok) {
      if (!options.quiet && $("liveryPreviewStatus")) $("liveryPreviewStatus").textContent = `Body base color detection failed:\n${result?.message || ""}`;
      return null;
    }
    const color = normalizeLiveryPreviewHexColor(result.data?.color);
    await setLiveryPreviewBodyBaseColor(color, { quiet: true, source: options.source || "derived" });
    if (!options.quiet && $("liveryPreviewStatus")) {
      $("liveryPreviewStatus").textContent = [
        `Body base color detected: ${color}`,
        `Sampled pixels: ${result.data?.sampleCount ?? "-"}`,
        "Use Save Base Color to keep this value for the current skin.",
      ].join("\n");
    }
    return { color, data: result.data || {} };
  } catch (error) {
    if (!options.quiet && $("liveryPreviewStatus")) $("liveryPreviewStatus").textContent = `Body base color detection failed:\n${error?.message || error}`;
    return null;
  }
}

async function detectDefaultLiveryPreviewBodyBaseColor(projectPayload = null) {
  if (state.liveryPreviewBodyBaseColorSource !== "default") return null;
  const payload = projectPayload || payloadPreview3dProject();
  if (!payload.colorTexturePath || !payload.detailTexturePath) return null;
  return detectLiveryPreviewBodyBaseColor({ quiet: true, source: "derived", payload });
}

async function saveLiveryPreviewBodyBaseColor(options = {}) {
  if (state.liveryPreviewReadOnly) {
    if (!options.quiet && $("liveryPreviewStatus")) $("liveryPreviewStatus").textContent = "Base color saving is disabled for read-only installed liveries.";
    return null;
  }
  const payload = payloadPreview3dProject();
  const officialProject = String(payload.projectType || "").trim().toLowerCase() === "official_external_livery";
  const result = await window.pywebview?.api?.save_preview3d_project?.(payload);
  if (!result?.ok) {
    if (!options.quiet && $("liveryPreviewStatus")) {
      $("liveryPreviewStatus").textContent = `Body base color save failed:\n${result?.message || ""}`;
    }
    return null;
  }
  state.liveryPreviewBodyBaseColorSource = "project";
  const saveStatus = officialProject ? (result.data?.project || {}) : (result.data || {});
  if (officialProject && result.data?.project) {
    state.liveryPreviewProject = { ...(state.liveryPreviewProject || {}), ...result.data.project };
    syncLiveryPreviewBodyBaseColorControls(state.liveryPreviewProject);
  }
  if (!options.quiet && $("liveryPreviewStatus")) {
    const synced = liveryPreviewBodyBaseColorMode(saveStatus) === "saved_car_synced";
    $("liveryPreviewStatus").textContent = synced
      ? `AC EVO Body Color applied: ${state.liveryPreviewBodyBaseColor || "#ffffff"}`
      : saveStatus.gameUpdateRequired
        ? `Fixed Livery Color saved: ${state.liveryPreviewBodyBaseColor || "#ffffff"}\nUpdate in Game is required.`
        : `Body base color saved: ${state.liveryPreviewBodyBaseColor || "#ffffff"}`;
  }
  return result.data || null;
}

function formatMaterialFinishValue(value) {
  const number = clampLiveryMaterialFinishValue(value, 0);
  return number.toFixed(2);
}

function syncLiveryPreviewMaterialFinishPanel() {
  const finish = normalizeLiveryMaterialFinish(state.liveryPreviewMaterialFinish);
  Object.entries(finish).forEach(([key, value]) => {
    if (key === "scope") return;
    document.querySelectorAll(`[data-material-finish-field="${key}"]`).forEach((input) => {
      if (input.value !== formatMaterialFinishValue(value)) input.value = formatMaterialFinishValue(value);
    });
    const output = $(`liveryPreviewFinish${key.charAt(0).toUpperCase()}${key.slice(1)}Value`);
    if (output) output.textContent = formatMaterialFinishValue(value);
  });
  const button = $("liveryPreviewToggleMaterialFinish");
  button?.classList.toggle("is-active-danger", !$("liveryPreviewMaterialFinishPanel")?.hidden);
}

function setLiveryPreviewMaterialFinishPanelVisible(visible) {
  const workspace = $("liveryPreviewWorkspace");
  const panel = $("liveryPreviewMaterialFinishPanel");
  const button = $("liveryPreviewToggleMaterialFinish");
  if (!panel) return;
  if (visible) {
    setLiveryPreviewMaterialPanelVisible(false);
    setLiveryPreviewWheelPanelVisible(false);
  }
  panel.hidden = !visible;
  workspace?.classList.toggle("is-material-finish-open", Boolean(visible));
  button?.classList.toggle("is-active-danger", Boolean(visible));
  syncLiveryPreviewMaterialFinishPanel();
  refreshLiveryPreviewLayout();
}

async function setLiveryPreviewMaterialFinish(values = {}, options = {}) {
  const finish = normalizeLiveryMaterialFinish({
    ...state.liveryPreviewMaterialFinish,
    ...(values || {}),
  });
  state.liveryPreviewMaterialFinish = finish;
  if (state.liveryPreviewProject) {
    state.liveryPreviewProject = {
      ...state.liveryPreviewProject,
      materialFinish: finish,
    };
  }
  syncLiveryPreviewMaterialFinishPanel();
  notifyLiveryMaterialFinishSubscribers(finish, { source: options.source || "liveryPreview" });
  if (normalizeLiveryPreviewMode(state.liveryPreviewPreviewMode) === "materialView") {
    window.LiveryLabPreview3d?.setMaterialViewFinish?.(finish);
  }
  if (!options.quiet && $("liveryPreviewStatus")) {
    $("liveryPreviewStatus").textContent = `Material finish updated: metalness ${formatMaterialFinishValue(finish.metalness)}, roughness ${formatMaterialFinishValue(finish.roughness)}`;
  }
  return finish;
}

async function openLiveryPreviewMaterialFinishPanel() {
  if (state.liveryPreviewReadOnly) {
    if ($("liveryPreviewStatus")) $("liveryPreviewStatus").textContent = "Paint finish editing is disabled for read-only installed liveries.";
    return;
  }
  setLiveryPreviewMaterialFinishPanelVisible(true);
  if (normalizeLiveryPreviewMode(state.liveryPreviewPreviewMode) !== "materialView") {
    await setLiveryPreviewPreviewMode("materialView");
  }
}

async function saveLiveryPreviewMaterialFinish(options = {}) {
  const status = $(options.statusId || "liveryPreviewStatus");
  if (state.liveryPreviewReadOnly) {
    if (!options.quiet && status) status.textContent = "Paint finish editing is disabled for read-only installed liveries.";
    return null;
  }
  const payload = payloadPreview3dProject();
  if (!payload.skinKey) {
    if (!options.quiet && status) status.textContent = "No livery is attached to this preview workspace.";
    return null;
  }
  const result = await window.pywebview?.api?.save_livery_material_finish?.(payload);
  if (!result?.ok) {
    const errorMessage = result?.message || "The paint finish could not be saved.";
    if (!options.quiet && status) {
      status.textContent = `Material finish save failed:\n${errorMessage}`;
    }
    showStatusModal({
      type: "error",
      title: "Paint Finish Save Failed",
      subtitle: state.liveryPreviewProject?.displayName || state.liveryPreviewProject?.skinId || "Livery material",
      message: "No saved paint-finish state was confirmed. Your preview slider values may be lost when the project is closed.",
      details: [
        `Skin: ${payload.skinKey || "-"}`,
        `Project: ${payload.projectPath || payload.projectFolder || "-"}`,
        "",
        errorMessage,
      ].join("\n"),
    });
    return null;
  }
  const finish = normalizeLiveryMaterialFinish(result.data?.materialFinish || payload.materialFinish);
  state.liveryPreviewMaterialFinish = finish;
  if (state.liveryPreviewProject) state.liveryPreviewProject = { ...state.liveryPreviewProject, materialFinish: finish };
  notifyLiveryMaterialFinishSubscribers(finish, { source: options.source || "liveryPreviewSave", saved: true });
  state.mySkinSelectedKey = payload.skinKey;
  if (window.pywebview?.api?.get_my_skins) {
    await loadMySkins();
  }
  syncLiveryPreviewGameUpdateAction();
  syncLiveryPreviewMaterialFinishPanel();
  if (!options.quiet && status) {
    const mapUpdate = result.data?.materialMapUpdate || {};
    const projectMapUpdate = result.data?.projectMaterialMapUpdate || {};
    const mapLine = mapUpdate.updated || mapUpdate.missing
      ? `Material maps: ${mapUpdate.updated || 0} updated, ${mapUpdate.missing || 0} missing.`
      : "";
    const projectMapLine = projectMapUpdate.updated || projectMapUpdate.missing
      ? `Project PNGs: ${projectMapUpdate.updated || 0} updated, ${projectMapUpdate.missing || 0} missing.`
      : "";
    status.textContent = [
      "Material finish saved.",
      `Material: ${result.data?.materialPath || "-"}`,
      mapLine,
      projectMapLine,
      result.data?.gameSync?.needs_update ? "Update in Game is required." : "",
    ].filter(Boolean).join("\n");
  }
  if (options.showSuccessModal) {
    const mapUpdate = result.data?.materialMapUpdate || {};
    const projectMapUpdate = result.data?.projectMaterialMapUpdate || {};
    const details = [
      result.data?.materialPath ? `Material: ${result.data.materialPath}` : "",
      mapUpdate.updated || mapUpdate.missing ? `Material maps: ${mapUpdate.updated || 0} updated, ${mapUpdate.missing || 0} missing.` : "",
      projectMapUpdate.updated || projectMapUpdate.missing ? `Project PNGs: ${projectMapUpdate.updated || 0} updated, ${projectMapUpdate.missing || 0} missing.` : "",
      result.data?.gameSync?.needs_update ? "Update in Game is required before the active in-game skin uses this finish." : "",
    ].filter(Boolean).join("\n");
    showStatusModal({
      type: "success",
      title: "Paint Finish Saved",
      subtitle: "Skin material updated",
      message: result.data?.gameSync?.needs_update
        ? "The paint finish was saved to the local skin material. Update the skin in game to publish it."
        : "The paint finish was saved to the skin material.",
      details,
    });
  }
  return result.data || null;
}

async function confirmAndSaveLiveryPreviewMaterialFinish(options = {}) {
  if (state.liveryPreviewReadOnly) {
    const statusId = options.statusId || "liveryPreviewStatus";
    if ($(statusId)) $(statusId).textContent = "Paint finish editing is disabled for read-only installed liveries.";
    return null;
  }
  const confirmed = await showConfirmModal({
    type: "warning",
    title: "Save Paint Finish",
    subtitle: "Existing texture files may be overwritten",
    message: "Saving this paint finish updates the skin material and regenerates material map textures. Existing user-edited material map PNGs and texturemips may be overwritten.",
    details: [
      "May update: EXT_Skin_M, EXT_Skin_R, EXT_Skin_CC and EXT_Skin_CR texturemips.",
      "May overwrite matching project PNG previews in the livery project folder.",
      "LiveryLab creates backups for local material/map writes where the writer path supports it.",
    ].join("\n"),
    confirmLabel: "Save and overwrite",
    cancelLabel: "Cancel",
  });
  if (!confirmed) return null;
  const saveOptions = {
    showSuccessModal: options.showSuccessModal !== false,
    source: options.source || "liveryPreviewSave",
    quiet: options.quiet === true,
    statusId: options.statusId || "liveryPreviewStatus",
  };
  const runner = () => saveLiveryPreviewMaterialFinish(saveOptions);
  if (options.buttonId) {
    return withButtonWorking(options.buttonId, "Saving...", runner);
  }
  return runner();
}

window.LiveryLabMaterialFinish = {
  default: { ...LIVERY_MATERIAL_FINISH_DEFAULT },
  presets: Object.fromEntries(Object.entries(LIVERY_MATERIAL_FINISH_PRESETS).map(([key, value]) => [key, { ...value }])),
  normalize: normalizeLiveryMaterialFinish,
  format: formatMaterialFinishValue,
  get() {
    return normalizeLiveryMaterialFinish(state.liveryPreviewMaterialFinish);
  },
  set(values = {}, options = {}) {
    return setLiveryPreviewMaterialFinish(values, { ...options, source: options.source || "shared" });
  },
  sync(values = {}, options = {}) {
    const finish = normalizeLiveryMaterialFinish(values);
    state.liveryPreviewMaterialFinish = finish;
    if (state.liveryPreviewProject) {
      state.liveryPreviewProject = { ...state.liveryPreviewProject, materialFinish: finish };
    }
    syncLiveryPreviewMaterialFinishPanel();
    notifyLiveryMaterialFinishSubscribers(finish, { source: options.source || "sharedSync" });
    return finish;
  },
  subscribe(callback) {
    if (typeof callback !== "function") return () => {};
    liveryMaterialFinishSubscribers.add(callback);
    callback(normalizeLiveryMaterialFinish(state.liveryPreviewMaterialFinish), { source: "subscribe" });
    return () => liveryMaterialFinishSubscribers.delete(callback);
  },
};

async function disableLiveryPreviewEvoPaintOverride(options = {}) {
  const payload = payloadPreview3dProject();
  if (!payload.skinKey) {
    if (!options.quiet && $("liveryPreviewStatus")) $("liveryPreviewStatus").textContent = "No livery is attached to this preview workspace.";
    return null;
  }
  if (!options.confirmed) {
    const confirmed = await showConfirmModal({
      type: "warning",
      title: "Disable EVO Paint Override",
      subtitle: "Use the skin material for EXT_SKIN finish values.",
      message: "This removes selected OEM paint finish entries from the local livery design, visual preset, and car final state. Backups are created before files are changed.",
      confirmLabel: "Disable Override",
      loadingLabel: "Disabling override...",
    });
    if (!confirmed) return null;
  }
  const result = await window.pywebview?.api?.disable_livery_evo_paint_override?.(payload);
  if (!result?.ok) {
    if (!options.quiet && $("liveryPreviewStatus")) {
      $("liveryPreviewStatus").textContent = `EVO paint override disable failed:\n${result?.message || ""}`;
    }
    return null;
  }
  state.mySkinSelectedKey = payload.skinKey;
  if (window.pywebview?.api?.get_my_skins) {
    await loadMySkins();
  }
  syncLiveryPreviewGameUpdateAction();
  const changedFiles = result.data?.changedFiles || [];
  if (!options.quiet && $("liveryPreviewStatus")) {
    $("liveryPreviewStatus").textContent = [
      result.data?.changed ? "EVO paint override disabled." : "EVO paint override was already disabled.",
      `Removed paint refs: ${result.data?.removedRefs ?? 0}`,
      changedFiles.length ? `Changed files: ${changedFiles.length}` : "",
      result.data?.gameSync?.needs_update ? "Update in Game is required." : "",
    ].filter(Boolean).join("\n");
  }
  return result.data || null;
}

function previewMaterialCategory(material = {}) {
  const name = String(material.name || "").toUpperCase();
  const shader = String(material.shader || "").toUpperCase();
  const source = String(material.source || "").toLowerCase();
  if (source === "rim" || source === "tyre" || source === "tire" || name.includes("TYRE") || name.includes("TIRE") || name.includes("RIM")) return "wheels";
  if (name.startsWith("EXT_SKIN")) return "skin";
  if (name.includes("DECAL")) return "decals";
  if (name.includes("WINDOW") || name.includes("GLASS") || shader.includes("GLASS")) return "glass";
  if (name.includes("LIGHT")) return "lights";
  if (
    name.includes("MECH") ||
    name.includes("DISC") ||
    name.includes("CALIPER") ||
    name.includes("RIM") ||
    name.includes("GRID") ||
    name.includes("EXHAUST") ||
    name.includes("CARBON") ||
    name.includes("METAL") ||
    name.includes("RUBBER")
  ) return "mechanics";
  return "other";
}

function previewMaterialMatchesQuery(material = {}, query = "") {
  const q = String(query || "").trim().toLowerCase();
  if (!q) return true;
  const haystack = [
    material.name,
    material.source,
    material.sourceLabel,
    ...(material.sourceLabels || []),
    material.meshName,
    material.meshPath,
    material.shader,
    material.materialPath,
    material.resolvedMaterialPath,
    material.parentMaterialPath,
    ...(material.textureSlots || []).flatMap((slot) => [slot.name, slot.previewChannel, slot.value]),
    ...(material.parameters || []).flatMap((param) => [param.name, Array.isArray(param.value) ? param.value.join(", ") : param.value]),
  ].join("\n").toLowerCase();
  return haystack.includes(q);
}

function filteredPreviewMaterials(data = {}) {
  const filter = state.liveryPreviewMaterialFilter || {};
  return (data.materials || []).filter((material) => {
    if (filter.category && filter.category !== "all" && previewMaterialCategory(material) !== filter.category) return false;
    if (!previewMaterialMatchesQuery(material, filter.query)) return false;
    if (filter.mappedOnly && !(material.textureSlots || []).some((slot) => slot.previewChannel)) return false;
    return true;
  });
}

function groupPreviewMaterialSlots(slots = []) {
  const order = ["baseColor", "detailSplat", "normal", "roughness", "metalness", "ao", "alpha", "clearcoat", "clearcoatRoughness", "clearcoatNormal", "scuff", "dirt", "damage", "other"];
  const groups = new Map(order.map((key) => [key, []]));
  slots.forEach((slot) => {
    const key = slot.previewChannel || "other";
    if (!groups.has(key)) groups.set(key, []);
    groups.get(key).push(slot);
  });
  return [...groups.entries()].filter(([, rows]) => rows.length);
}

function extSkinChannelKey(name = "") {
  const text = String(name || "");
  if (/^base_/i.test(text) || /^tx/i.test(text) || /^(normalmap|roughnessmap|spatmap)$/i.test(text)) return "base";
  if (/^red/i.test(text)) return "red";
  if (/^green/i.test(text)) return "green";
  if (/^blue/i.test(text)) return "blue";
  if (/red/i.test(text)) return "red";
  if (/green/i.test(text)) return "green";
  if (/blue/i.test(text)) return "blue";
  return "shared";
}

function renderExtSkinChannelAnalysis(data = {}) {
  const skinMaterials = (data.materials || []).filter((material) => previewMaterialCategory(material) === "skin");
  if (!skinMaterials.length) return "";
  const channels = [
    { key: "base", label: "Authored texture maps", note: "Black mask areas use the authored texture and PBR maps." },
    { key: "red", label: "Red material channel", note: "Red mask areas use the red JSON material channel." },
    { key: "green", label: "Green material channel", note: "Green mask areas use the green JSON material channel." },
    { key: "blue", label: "Carbon material", note: "Blue mask areas use the carbon material branch." },
    { key: "shared", label: "Shared / other", note: "Global skin material resources, damage, dirt, scuffs, editor defaults." },
  ];
  const material = skinMaterials.find((row) => String(row.name || "").toUpperCase() === "EXT_SKIN") || skinMaterials[0];
  const slots = material.textureSlots || [];
  const params = (material.parameters || []).filter((param) => param.value !== null && param.value !== undefined && param.value !== "");
  const detailSlot = slots.find((slot) => String(slot.name || "").toLowerCase() === "spatmap");
  const channelHtml = channels.map((channel) => {
    const channelSlots = slots.filter((slot) => extSkinChannelKey(slot.name) === channel.key);
    const channelParams = params.filter((param) => extSkinChannelKey(param.name) === channel.key);
    const slotRows = channelSlots.slice(0, 8).map((slot) => {
      const size = slot.width && slot.height ? `${slot.width} x ${slot.height}` : "";
      return `<li><strong>${escapeHtml(slot.name || "-")}</strong><span>${escapeHtml(slot.value || "-")}</span><span>${escapeHtml([slot.previewChannel || "", size].filter(Boolean).join(" | "))}</span></li>`;
    }).join("");
    const paramRows = channelParams.slice(0, 8).map((param) => `<li><strong>${escapeHtml(param.name || "-")}</strong><span>${escapeHtml(Array.isArray(param.value) ? param.value.join(", ") : param.value)}</span></li>`).join("");
    return `<section class="livery-preview-material-analysis-channel">
      <h3>${escapeHtml(channel.label)}</h3>
      <p>${escapeHtml(channel.note)}</p>
      ${channelSlots.length ? `<ul class="livery-preview-material-list">${slotRows}${channelSlots.length > 8 ? `<li><span>${channelSlots.length - 8} more slots</span></li>` : ""}</ul>` : "<p>No slots in this branch.</p>"}
      ${channelParams.length ? `<div class="livery-preview-material-subhead">Parameters</div><ul class="livery-preview-material-list">${paramRows}${channelParams.length > 8 ? `<li><span>${channelParams.length - 8} more parameters</span></li>` : ""}</ul>` : ""}
    </section>`;
  }).join("");
  return `<section class="livery-preview-material-analysis">
    <div class="livery-preview-material-analysis-head">
      <strong>EXT_SKIN Channel Analysis</strong>
      <span>${escapeHtml(material.name || "EXT_SKIN")} | ${slots.length} slots | ${params.length} parameters</span>
    </div>
    <p>Mask source: ${escapeHtml(detailSlot?.value || "SpatMap / EXT_Skin_D not found")}</p>
    <p>Note: yellow areas in EXT_Skin_D are usually overlapping red+green mask information and are previewed as authored EXT_SKIN_C areas.</p>
    ${channelHtml}
  </section>`;
}

function renderPreviewMaterialModeLegendPanel() {
  return `<section class="livery-preview-material-analysis">
    <div class="livery-preview-material-analysis-head">
      <strong>Viewer Material Modes</strong>
      <span>Experimental preview mapping</span>
    </div>
    <p>Debug overlay: red = body base color mask, green = green mask, blue = blue mask, yellow = authored EXT_SKIN_C overlap.</p>
    <p>EVO Approx: red-dominant areas use body base color, yellow keeps EXT_SKIN_C, blue uses carbon. Wheels are applied in the normal preview.</p>
  </section>`;
}

function renderWheelMaterialAnalysis(data = {}) {
  const wheelMaterials = (data.wheelMaterials || []).length
    ? data.wheelMaterials
    : (data.materials || []).filter((material) => previewMaterialCategory(material) === "wheels");
  if (!wheelMaterials.length) return "";
  const rimCount = wheelMaterials.filter((material) => String(material.source || "").toLowerCase() === "rim").length;
  const tyreCount = wheelMaterials.filter((material) => {
    const source = String(material.source || "").toLowerCase();
    return source === "tyre" || source === "tire";
  }).length;
  const slotCount = wheelMaterials.reduce((total, material) => total + (material.textureSlots || []).length, 0);
  const sourceLabels = [...new Set(wheelMaterials.flatMap((material) => material.sourceLabels || [material.sourceLabel]).filter(Boolean))];
  const candidateSlots = wheelMaterials
    .flatMap((material) => (material.textureSlots || []).map((slot) => ({ material, slot })))
    .filter((row) => row.slot?.value)
    .slice(0, 12);
  const slotRows = candidateSlots.map(({ material, slot }) => {
    const size = slot.width && slot.height ? `${slot.width} x ${slot.height}` : "";
    return `<li>
      <strong>${escapeHtml(material.name || material.source || "Wheel")}</strong>
      <span>${escapeHtml(slot.name || "-")}: ${escapeHtml(slot.value || "-")}</span>
      <span>${escapeHtml([slot.previewChannel || "", size, slot.suggestedFormat || ""].filter(Boolean).join(" | "))}</span>
    </li>`;
  }).join("");
  return `<section class="livery-preview-material-analysis">
    <div class="livery-preview-material-analysis-head">
      <strong>Wheel/Tyre Material Analysis</strong>
      <span>${rimCount} rim | ${tyreCount} tyre | ${slotCount} slots</span>
    </div>
    <p>Source models: ${escapeHtml(sourceLabels.slice(0, 8).join(", ") || "wheel auxiliaries")}${sourceLabels.length > 8 ? escapeHtml(` +${sourceLabels.length - 8} more`) : ""}</p>
    ${slotRows ? `<ul class="livery-preview-material-list">${slotRows}${candidateSlots.length < slotCount ? `<li><span>${slotCount - candidateSlots.length} more texture slots</span></li>` : ""}</ul>` : "<p>No texture slots found on wheel materials yet.</p>"}
  </section>`;
}

function syncPreviewMaterialFilterControls() {
  const filter = state.liveryPreviewMaterialFilter || {};
  const search = $("liveryPreviewMaterialSearch");
  if (search && search.value !== String(filter.query || "")) search.value = String(filter.query || "");
  const mappedOnly = $("liveryPreviewMaterialMappedOnly");
  if (mappedOnly) mappedOnly.checked = Boolean(filter.mappedOnly);
  document.querySelectorAll("[data-material-filter]").forEach((button) => {
    button.classList.toggle("active", button.dataset.materialFilter === (filter.category || "all"));
  });
}

function renderPreviewMaterialPanel(data) {
  const summary = $("liveryPreviewMaterialSummary");
  const content = $("liveryPreviewMaterialContent");
  if (!summary || !content) return;
  const materials = data?.materials || [];
  const visibleMaterials = filteredPreviewMaterials(data || {});
  const filter = state.liveryPreviewMaterialFilter || {};
  const visibleSlotCount = visibleMaterials.reduce((total, material) => {
    const slots = material.textureSlots || [];
    return total + (filter.mappedOnly ? slots.filter((slot) => slot.previewChannel).length : slots.length);
  }, 0);
  syncPreviewMaterialFilterControls();
  const wheelText = data?.wheelMaterialCount ? ` | wheels ${data.wheelMaterialCount}/${data.wheelTextureSlotCount ?? 0}` : "";
  summary.textContent = `${visibleMaterials.length}/${materials.length} materials | ${visibleSlotCount}/${data?.textureSlotCount ?? 0} slots${wheelText}`;
  const warningHtml = (data?.warnings || []).length
    ? `<div class="livery-preview-material-subhead">Warnings</div><ul class="livery-preview-material-list">${data.warnings.map((row) => `<li><span>${escapeHtml(row)}</span></li>`).join("")}</ul>`
    : "";
  const cards = visibleMaterials.map((material) => {
    const slots = filter.mappedOnly
      ? (material.textureSlots || []).filter((slot) => slot.previewChannel)
      : (material.textureSlots || []);
    const params = (material.parameters || []).filter((param) => param.value !== null && param.value !== undefined && param.value !== "");
    const slotHtml = groupPreviewMaterialSlots(slots).map(([channel, rows]) => (
      `<div class="livery-preview-material-subhead">${escapeHtml(channel)}</div><ul class="livery-preview-material-list">${rows.map((slot) => {
        const size = slot.width && slot.height ? `${slot.width} x ${slot.height}` : "";
        return `<li><strong>${escapeHtml(slot.name || "-")}</strong><span>${escapeHtml(slot.value || "-")}</span><span>${escapeHtml([size, slot.suggestedFormat || ""].filter(Boolean).join(" | "))}</span></li>`;
      }).join("")}</ul>`
    )).join("");
    const paramHtml = params.length
      ? `<div class="livery-preview-material-subhead">Parameters</div><ul class="livery-preview-material-list">${params.map((param) => `<li><strong>${escapeHtml(param.name || "-")}</strong><span>${escapeHtml(Array.isArray(param.value) ? param.value.join(", ") : param.value)}</span></li>`).join("")}</ul>`
      : "";
    const labels = (material.sourceLabels || [material.sourceLabel]).filter(Boolean);
    const labelHtml = labels.length ? `<dt>Labels</dt><dd>${escapeHtml(labels.join(", "))}</dd>` : "";
    const meshHtml = material.meshName || material.meshPath ? `<dt>Mesh</dt><dd>${escapeHtml(material.meshName || material.meshPath || "-")}</dd>` : "";
    return `<details class="livery-preview-material-card">
      <summary><span>${escapeHtml(material.name || "Material")}</span><small>${escapeHtml(previewMaterialCategory(material))} | ${escapeHtml(material.source || "-")} | ${slots.length} slots</small></summary>
      <dl>
        <dt>Source</dt><dd>${escapeHtml(material.source || "-")}</dd>
        ${labelHtml}
        ${meshHtml}
        <dt>Shader</dt><dd>${escapeHtml(material.shader || "-")}</dd>
        <dt>Indices</dt><dd>${escapeHtml(material.indexCount ?? "-")}</dd>
        <dt>Path</dt><dd>${escapeHtml(material.materialPath || "-")}</dd>
        <dt>Parent</dt><dd>${escapeHtml(material.parentMaterialPath || "-")}</dd>
      </dl>
      ${slotHtml}
      ${paramHtml}
    </details>`;
  }).join("");
  content.innerHTML = [
    `<p>${escapeHtml(data?.meshName || "Exterior mesh")} | ${escapeHtml(data?.triangleCount ?? "-")} triangles</p>`,
    renderPreviewMaterialModeLegendPanel(),
    renderWheelMaterialAnalysis(data || {}),
    renderExtSkinChannelAnalysis(data || {}),
    warningHtml,
    cards || "<p>No materials match the current filter.</p>",
  ].join("");
}

async function inspectLiveryPreviewMaterials() {
  const payload = payloadPreview3dProject();
  if (!payload.exteriorMeshPath && !payload.carContentPath) {
    $("liveryPreviewStatus").textContent = "No source exterior mesh is attached to this preview.";
    return null;
  }
  $("liveryPreviewStatus").textContent = "Inspecting EVO materials...";
  const result = await window.pywebview.api.inspect_preview3d_materials(payload);
  if (!result.ok) {
    $("liveryPreviewStatus").textContent = `Material inspection failed:\n${result.message || ""}`;
    showResult(result, "Material inspection failed");
    return null;
  }
  state.liveryPreviewMaterialInspection = result.data || {};
  renderPreviewMaterialPanel(state.liveryPreviewMaterialInspection);
  $("liveryPreviewStatus").textContent = result.message || "Material inspection finished.";
  return result.data || {};
}

async function exportLiveryPreviewWheelTextures() {
  const payload = payloadPreview3dProject();
  if (!payload.exteriorMeshPath && !payload.carContentPath) {
    $("liveryPreviewStatus").textContent = "No source exterior mesh is attached to this preview.";
    return null;
  }
  $("liveryPreviewStatus").textContent = "Exporting wheel and tyre textures...";
  const result = await window.pywebview.api.export_preview3d_wheel_textures(payload);
  if (!result.ok) {
    $("liveryPreviewStatus").textContent = `Wheel texture export failed:\n${result.message || ""}`;
    showResult(result, "Wheel texture export failed");
    return null;
  }
  $("liveryPreviewStatus").textContent = result.message || "Wheel texture export finished.";
  showResult(result, "Wheel texture export");
  return result.data || {};
}

async function toggleLiveryPreviewMaterialInspector() {
  const panel = $("liveryPreviewMaterialPanel");
  if (!panel) return;
  const opening = panel.hidden;
  setLiveryPreviewMaterialPanelVisible(opening);
  if (!opening) return;
  const content = $("liveryPreviewMaterialContent");
  const summary = $("liveryPreviewMaterialSummary");
  if (summary) summary.textContent = "Inspecting materials...";
  if (content) content.innerHTML = "<p>Reading EVO material data...</p>";
  await inspectLiveryPreviewMaterials();
}

function numberplateCalibrationSurfaceKind(rowKey = "", row = {}) {
  const keyLabel = String(rowKey || "").split(":", 1)[0] || "";
  return numberplateSurfaceKind({
    label: row?.placement || row?.label || keyLabel,
    surface: row?.surface ?? row?.outer_field_number ?? "",
  });
}

function normalizeNumberplateCalibrations(calibrations = {}) {
  return Object.fromEntries(
    Object.entries(calibrations || {})
      .filter(([rowKey]) => !String(rowKey).startsWith("__"))
      .map(([rowKey, row]) => {
        const key = String(rowKey).toUpperCase();
        const transform = row?.transform || row;
        if (!transform || typeof transform !== "object") return [key, transform];
        const surfaceKind = numberplateCalibrationSurfaceKind(key, row || {});
        const surfaceExport = calibrations.__export?.[surfaceKind] && typeof calibrations.__export[surfaceKind] === "object" ? calibrations.__export[surfaceKind] : {};
        const rowExport = row?.exportFactors && typeof row.exportFactors === "object" ? row.exportFactors : {};
        const transformExport = transform?.exportFactors && typeof transform.exportFactors === "object" ? transform.exportFactors : {};
        const exportFactors = { ...surfaceExport, ...rowExport, ...transformExport };
        return [key, Object.keys(exportFactors).length ? { ...transform, exportFactors } : transform];
      }),
  );
}

function normalizeNumberplateSavedViewerCalibrations(calibrations = {}, carBuiltins = {}) {
  return Object.fromEntries(
    Object.entries(calibrations || {})
      .filter(([rowKey]) => !String(rowKey).startsWith("__"))
      .map(([rowKey, row]) => {
        const key = String(rowKey).toUpperCase();
        const transform = row?.transform || row;
        if (!transform || typeof transform !== "object") return [key, transform];
        const placement = {
          label: row?.placement || key.split(":")[0] || "",
          index: row?.actorIndex ?? key.split(":")[1] ?? "",
          surface: row?.surface ?? "",
          numberplate_index: row?.numberplateIndex ?? "",
        };
        const builtin = numberplateBuiltinCalibrationFromCar(carBuiltins, placement, placement.index);
        return [key, previewTransformForNumberplate(placement, transform, builtin || transform)];
      }),
  );
}

async function loadNumberplateBuiltinCalibrations() {
  if (state.numberplateBuiltinCalibrations) return state.numberplateBuiltinCalibrations;
  if (!state.numberplateBuiltinCalibrationsPromise) {
    state.numberplateBuiltinCalibrationsPromise = (async () => {
      try {
        const result = await window.pywebview?.api?.get_numberplate_calibrations?.({});
        if (result?.ok && result.data?.cars && typeof result.data.cars === "object") {
          state.numberplateTextLayerDefaults = result.data?.textLayers && typeof result.data.textLayers === "object" ? result.data.textLayers : {};
          await loadNumberplateStudioTemplatesForPreview();
          return result.data.cars;
        }
      } catch (_error) {
        // Fall back to viewer-side JSON loading.
      }
      state.numberplateTextLayerDefaults = {};
      await loadNumberplateStudioTemplatesForPreview();
      return {};
    })();
  }
  state.numberplateBuiltinCalibrations = await state.numberplateBuiltinCalibrationsPromise;
  return state.numberplateBuiltinCalibrations;
}

async function loadNumberplateStudioTemplatesForPreview() {
  if (state.numberplateStudioTemplatesPromise) return state.numberplateStudioTemplatesPromise;
  state.numberplateStudioTemplatesPromise = (async () => {
    try {
      const result = await window.pywebview?.api?.list_numberplate_studio_templates?.();
      state.numberplateStudioTemplates = result?.ok && Array.isArray(result.data?.templates) ? result.data.templates : [];
    } catch (_error) {
      state.numberplateStudioTemplates = [];
    }
    return state.numberplateStudioTemplates;
  })();
  return state.numberplateStudioTemplatesPromise;
}

function invalidateNumberplateStudioTemplateCache() {
  state.numberplateStudioTemplates = [];
  state.numberplateStudioTemplatesPromise = null;
}

function numberplateStudioTemplateById(id = "") {
  const needle = String(id || "").trim().toLowerCase();
  if (!needle) return null;
  return (state.numberplateStudioTemplates || []).find((template) => String(template.id || "").trim().toLowerCase() === needle) || null;
}

function numberplateTemplateRel(ref = {}) {
  return String(ref?.rel || ref?.assetRel || ref?.path || "").trim().replace(/\//g, "\\");
}

function numberplateTemplatePayload(template = {}, options = {}) {
  const backgrounds = template.backgrounds && typeof template.backgrounds === "object" ? template.backgrounds : {};
  const includeHood = options.includeHood !== false;
  const left = numberplateTemplateRel(backgrounds.leftDoor || backgrounds.left || {});
  const right = numberplateTemplateRel(backgrounds.rightDoor || backgrounds.right || {});
  const hood = includeHood ? numberplateTemplateRel(backgrounds.hood || {}) : "";
  const font = numberplateTemplateRel(template.publishedFont || template.gameFont || template.font || {});
  return {
    templateId: String(template.id || "").trim(),
    mode: "surface",
    fontPath: font,
    backgroundPath: left || right || hood,
    hoodBackgroundPath: hood || left || right,
    leftBackgroundPath: left || hood || right,
    rightBackgroundPath: right || left || hood,
    textColor: Array.isArray(template.textColor || template.text_color) ? (template.textColor || template.text_color).slice(0, 4).map(Number) : null,
  };
}

function numberplateTemplateSurfaceSummary(template = {}, options = {}) {
  if (!template) return "No template selected.";
  const includeHood = options.includeHood !== false;
  const payload = numberplateTemplatePayload(template, { includeHood });
  const surfaces = [];
  if (payload.hoodBackgroundPath) surfaces.push("Hood");
  if (payload.leftBackgroundPath) surfaces.push("Left Door");
  if (payload.rightBackgroundPath) surfaces.push("Right Door");
  const suffix = !includeHood && numberplateTemplateRel(template.backgrounds?.hood || {})
    ? " This skin has no hood numberplate; the hood background is ignored."
    : "";
  return `Applies to: ${surfaces.join(", ") || "no available plate surface"}.${suffix}`;
}

function numberplateTemplateMatchesVariant(template = {}, variant = {}) {
  const payload = numberplateTemplatePayload(template);
  const same = (left, right) => String(left || "").replace(/\//g, "\\").toLowerCase() === String(right || "").replace(/\//g, "\\").toLowerCase();
  if (variant.template_id && same(variant.template_id, template.id)) return true;
  if (payload.fontPath && variant.font_path && !same(payload.fontPath, variant.font_path)) return false;
  const mode = String(variant.mode || "").toLowerCase();
  if (["surface", "surfaces", "per_surface", "split"].includes(mode)) {
    return (
      (!payload.hoodBackgroundPath || !variant.hood_background_path || same(payload.hoodBackgroundPath, variant.hood_background_path)) &&
      (!payload.leftBackgroundPath || !variant.left_background_path || same(payload.leftBackgroundPath, variant.left_background_path)) &&
      (!payload.rightBackgroundPath || !variant.right_background_path || same(payload.rightBackgroundPath, variant.right_background_path))
    );
  }
  return payload.backgroundPath && variant.background_path && same(payload.backgroundPath, variant.background_path);
}

function renderNumberplateStudioTemplateSelect(selectId, currentTemplateId = "", variant = null) {
  const select = $(selectId);
  if (!select) return;
  const current = String(currentTemplateId || select.dataset.current || "").trim();
  select.innerHTML = "";
  const placeholder = document.createElement("option");
  placeholder.value = "";
  placeholder.textContent = "Choose numberplate template";
  select.append(placeholder);
  (state.numberplateStudioTemplates || []).forEach((template) => {
    const option = document.createElement("option");
    option.value = template.id || "";
    option.textContent = template.name || template.id || "Template";
    option.title = [
      template.font?.label || template.font?.rel || "",
      template.backgrounds?.leftDoor?.label || template.backgrounds?.leftDoor?.rel || "",
      template.backgrounds?.rightDoor?.label || template.backgrounds?.rightDoor?.rel || "",
      template.backgrounds?.hood?.label || template.backgrounds?.hood?.rel || "",
    ].filter(Boolean).join(" | ");
    select.append(option);
  });
  let selectedValue = current;
  if (!selectedValue && variant && typeof variant === "object") {
    const matched = (state.numberplateStudioTemplates || []).find((template) => numberplateTemplateMatchesVariant(template, variant));
    selectedValue = matched?.id || "";
  }
  if (selectedValue && [...select.options].some((option) => option.value === selectedValue)) {
    select.value = selectedValue;
  } else if (selectedValue) {
    const missing = document.createElement("option");
    missing.value = selectedValue;
    missing.textContent = `${selectedValue} (template not installed)`;
    missing.disabled = true;
    select.append(missing);
    select.value = selectedValue;
  }
}

function selectedNumberplateTemplatePayload(selectId, options = {}) {
  const template = numberplateStudioTemplateById($(selectId)?.value || "");
  return template ? numberplateTemplatePayload(template, options) : null;
}

function updateMySkinNumberplateTemplateInfo(templateId = "", options = {}) {
  const template = numberplateStudioTemplateById(templateId);
  const info = $("mySkinNumberplateTemplateInfo");
  if (info) info.textContent = numberplateTemplateSurfaceSummary(template, options);
}

function applyNumberplateTemplateToSkinCreatorInputs(templateId = "") {
  const template = numberplateStudioTemplateById(templateId);
  if (!template) return;
  const payload = numberplateTemplatePayload(template);
  if ($("skinNumberplateFont")) $("skinNumberplateFont").dataset.current = payload.fontPath;
  if ($("skinNumberplateBackground")) $("skinNumberplateBackground").dataset.current = payload.backgroundPath;
}

async function numberplateViewerCalibrations() {
  const builtinCalibrations = await numberplateViewerBuiltinCalibrations();
  const builtins = await loadNumberplateBuiltinCalibrations();
  const carId = liveryPreviewCarId();
  const carBuiltins = builtins?.[carId] || builtins?.[carId.toLowerCase()] || {};
  return {
    ...builtinCalibrations,
    ...normalizeNumberplateSavedViewerCalibrations(numberplateCalibrationMap(), carBuiltins),
  };
}

async function numberplateViewerBuiltinCalibrations() {
  const builtins = await loadNumberplateBuiltinCalibrations();
  const carId = liveryPreviewCarId();
  const carBuiltins = builtins?.[carId] || builtins?.[carId.toLowerCase()] || {};
  return normalizeNumberplateCalibrations(carBuiltins);
}

async function applyBuiltinNumberplateFitForCurrentCar() {
  const builtins = await loadNumberplateBuiltinCalibrations();
  const carId = liveryPreviewCarId();
  const carBuiltins = builtins?.[carId] || builtins?.[carId.toLowerCase()] || {};
  const fit = carBuiltins?.__fit && typeof carBuiltins.__fit === "object" ? carBuiltins.__fit : NUMBERPLATE_FIT_DEFAULT;
  state.liveryPreviewNumberplateFit = normalizeNumberplateFitConfig(fit);
  setNumberplateFitInputs(state.liveryPreviewNumberplateFit);
  return applyNumberplateFitToViewer(state.liveryPreviewNumberplateFit);
}

function numberplateSurfaceKind(placement = {}) {
  const label = String(placement.label || "").toUpperCase();
  const surface = Number(placement.surface ?? placement.outer_field_number ?? 0);
  if (label.includes("HOOD") || surface === 1) return "hood";
  if (label.includes("DOOR") || surface === 3 || surface === 4) return "door";
  return "other";
}

function actorBaselineNumberClose(left, right, tolerance = 0.005) {
  if (left == null || left === "" || right == null || right === "") return true;
  const a = Number(left);
  const b = Number(right);
  return Number.isFinite(a) && Number.isFinite(b) && Math.abs(a - b) <= tolerance;
}

function actorBaselineRotationClose(left, right) {
  return actorBaselineNumberClose(left, right, 1.5);
}

function numberplateCalibrationCandidateKeys(placement = {}, index = 0) {
  const keys = [];
  const push = (value) => {
    const key = String(value || "").trim();
    if (key && !keys.includes(key)) keys.push(key);
  };
  const label = String(placement?.label || "").toUpperCase();
  const actorIndex = placement?.index ?? index;
  const surface = Number(placement?.surface ?? placement?.outer_field_number ?? 0);
  if (label && actorIndex !== "") push(`${label}:${actorIndex}`);
  push(placement?.sourceCalibrationKey || placement?.calibrationSourceKey || "");
  if (surface) {
    push(`SURFACE_${surface}`);
    push(`SURFACE:${surface}`);
    if (surface === 1) push("HOOD");
    if (surface === 3) push("DOOR_L");
    if (surface === 4) push("DOOR_R");
  }
  push(label);
  return keys;
}

function numberplateCalibrationByKeys(calibrations = {}, keys = []) {
  for (const key of keys) {
    const row = numberplateCalibrationByKey(calibrations, key);
    if (row) return row;
  }
  return null;
}

function numberplateCalibrationRowMatchesPlacement(rowKey = "", label = "", surface = 0) {
  if (rowKey.startsWith("__")) return false;
  if (label && rowKey.startsWith(label)) return true;
  if (surface && (rowKey === `SURFACE_${surface}` || rowKey === `SURFACE:${surface}`)) return true;
  if (surface === 1 && rowKey === "HOOD") return true;
  if (surface === 3 && rowKey === "DOOR_L") return true;
  if (surface === 4 && rowKey === "DOOR_R") return true;
  return false;
}

function numberplateCalibrationByActorBaseline(calibrations = {}, placement = {}, label = "") {
  const expectedLabel = String(label || placement.label || "").toUpperCase();
  const surface = Number(placement.surface ?? placement.outer_field_number ?? 0);
  const position = Array.isArray(placement.position) ? placement.position : [];
  const rotation = Array.isArray(placement.rotation) ? placement.rotation : [];
  const scale = Array.isArray(placement.scale) ? placement.scale : [];
  for (const [rowKey, rawRow] of Object.entries(calibrations || {})) {
    const key = String(rowKey || "").toUpperCase();
    if (!numberplateCalibrationRowMatchesPlacement(key, expectedLabel, surface)) continue;
    const transform = rawRow?.transform || rawRow;
    const baseline = transform?.actorBaseline;
    if (!baseline || typeof baseline !== "object") continue;
    const matches =
      actorBaselineNumberClose(baseline.position_x, position[0]) &&
      actorBaselineNumberClose(baseline.position_y, position[1]) &&
      actorBaselineNumberClose(baseline.position_z, position[2]) &&
      actorBaselineRotationClose(baseline.rotation_x, rotation[0]) &&
      actorBaselineRotationClose(baseline.rotation_y, rotation[1]) &&
      actorBaselineRotationClose(baseline.rotation_z, rotation[2]) &&
      actorBaselineNumberClose(baseline.scale_x, scale[0]) &&
      actorBaselineNumberClose(baseline.scale_y, scale[1]) &&
      actorBaselineNumberClose(baseline.scale_z, scale[2]);
    if (matches) return rawRow;
  }
  return null;
}

function numberplateCalibrationByKey(calibrations = {}, key = "") {
  const normalized = String(key || "").trim();
  if (!normalized) return null;
  return calibrations[normalized] || calibrations[normalized.toUpperCase()] || null;
}

function numberplateBuiltinCalibrationFromCar(carBuiltins = {}, placement = {}, index = 0) {
  const label = String(placement?.label || "").toUpperCase();
  const keys = numberplateCalibrationCandidateKeys(placement, index);
  const rawRow = numberplateCalibrationByKeys(carBuiltins, keys) || numberplateCalibrationByActorBaseline(carBuiltins, placement, label) || null;
  const transform = rawRow?.transform || rawRow;
  if (!transform || typeof transform !== "object") return null;
  const surfaceKind = numberplateSurfaceKind(placement);
  const surfaceExport = carBuiltins.__export?.[surfaceKind] && typeof carBuiltins.__export[surfaceKind] === "object" ? carBuiltins.__export[surfaceKind] : {};
  const rowExport = rawRow?.exportFactors && typeof rawRow.exportFactors === "object" ? rawRow.exportFactors : {};
  return {
    ...transform,
    exportFactors: {
      ...surfaceExport,
      ...rowExport,
    },
  };
}

async function numberplateBuiltinCalibrationForPlacement(placement = {}, index = 0) {
  const builtins = await loadNumberplateBuiltinCalibrations();
  const carId = liveryPreviewCarId();
  const carBuiltins = builtins?.[carId] || builtins?.[carId.toLowerCase()] || {};
  return numberplateBuiltinCalibrationFromCar(carBuiltins, placement, index);
}

async function selectedNumberplateBuiltinCalibration() {
  const { placement, index } = selectedNumberplatePlacement();
  return numberplateBuiltinCalibrationForPlacement(placement, index);
}

function generatedNumberplateExportFactors(surfaceKind = "other") {
  if (surfaceKind === "hood") {
    return {
      position_x: 1,
      position_y: 1,
      position_z: 1,
      rotation_y: 1,
      scale_x: 1,
      scale_z: 1,
    };
  }
  if (surfaceKind === "door") {
    return {
      position_y: 1,
      position_z: 1,
      rotation_y: 0.5,
      scale_x: 1,
      scale_z: 1,
    };
  }
  return {};
}

async function selectedNumberplateFitBaselineCalibration() {
  const { placement, index } = selectedNumberplatePlacement();
  return numberplateFitBaselineCalibrationForPlacement(placement, index);
}

function numberplateBackgroundForPlacement(placement = {}, data = state.liveryPreviewNumberplate || {}) {
  return {
    path: placement?.backgroundPng || data?.backgroundPng || "",
    rel: placement?.backgroundRel || data?.backgroundRel || "",
    status: placement?.backgroundStatus || data?.backgroundStatus || "",
  };
}

function numberplateSourcePlacementForBaseline(placement = {}) {
  const sourceIndex = Number(placement?.sourcePlacementIndex ?? placement?.source_index);
  if (!Number.isFinite(sourceIndex)) return null;
  const allPlacements = state.liveryPreviewNumberplate?.allPlacements || [];
  return allPlacements.find((row) => Number(row?.index) === sourceIndex) || null;
}

function stableGeneratedDoorRotationBaseline(placement = {}) {
  const label = String(placement?.label || "").toUpperCase();
  const surface = Number(placement?.surface ?? placement?.outer_field_number ?? 0);
  const isRight = surface === 4 || label.includes("_R") || label.endsWith("R");
  return isRight ? -90 : 90;
}

async function numberplateFitBaselineCalibrationForPlacement(placement = {}, index = 0) {
  if (!placement || placement.index == null) return null;
  const baselinePlacement = placement;
  const baselineIndex = index;
  const background = numberplateBackgroundForPlacement(placement);
  const baseline = await window.LiveryLabPreview3d?.getMappedNumberplateTransform?.({
    placement: baselinePlacement,
    index: baselineIndex,
    backgroundPath: background.path,
    carId: liveryPreviewCarId(),
    calibrations: {},
    forceOpaque: false,
  });
  if (!baseline) return null;
  const surfaceKind = numberplateSurfaceKind(placement);
  if (surfaceKind === "door") {
    baseline.rx = 0;
    baseline.ry = stableGeneratedDoorRotationBaseline(placement);
    baseline.rz = 0;
  }
  const actorBaseline = placement?.actorBaseline && typeof placement.actorBaseline === "object" ? placement.actorBaseline : null;
  const exportFactors = generatedNumberplateExportFactors(surfaceKind);
  if (surfaceKind === "hood" && actorBaseline) {
    const hasRotationX = actorBaseline.rotation_x != null && actorBaseline.rotation_x !== "";
    const hasRotationY = actorBaseline.rotation_y != null && actorBaseline.rotation_y !== "";
    if (hasRotationX && !hasRotationY) {
      delete exportFactors.rotation_y;
      exportFactors.rotation_x = 1;
    } else if (hasRotationY) {
      delete exportFactors.rotation_x;
      exportFactors.rotation_y = 1;
    }
  }
  return {
    ...baseline,
    ...(actorBaseline ? { actorBaseline } : {}),
    generatedFromFit: true,
    generatedFitSourcePlacementIndex: baselinePlacement?.index ?? null,
    sourcePlacementIndex: placement?.sourcePlacementIndex ?? placement?.source_index ?? null,
    previewRotationKey: surfaceKind === "hood" ? "rz" : "rx",
    exportFactors,
  };
}

async function selectedNumberplateApplyCalibration() {
  const { placement, index } = selectedNumberplatePlacement();
  return await numberplateApplyCalibrationForPlacement(placement, index);
}

async function numberplateApplyCalibrationForPlacement(placement = {}, index = 0) {
  return await numberplateBuiltinCalibrationForPlacement(placement, index) || await numberplateFitBaselineCalibrationForPlacement(placement, index);
}

function normalizeVehicleSideNumberplateTransform(placement = {}, transform = {}) {
  const label = String(placement?.label || "").toUpperCase();
  if (!label.includes("DOOR")) return { ...transform };
  const isLeft = label.includes("_L") || label.endsWith("L");
  const isRight = label.includes("_R") || label.endsWith("R");
  if (!isLeft && !isRight) return { ...transform };
  const sideSign = isLeft ? 1 : -1;
  const normalized = { ...transform };
  const x = Number(normalized.x);
  if (Number.isFinite(x) && x !== 0) normalized.x = Math.abs(x) * sideSign;
  const ry = Number(normalized.ry);
  if (Number.isFinite(ry) && ry !== 0) normalized.ry = Math.abs(ry) * sideSign;
  return normalized;
}

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

function numberplateNumbersClose(left, right, tolerance = 0.001) {
  const a = numberplateTransformNumber(left);
  const b = numberplateTransformNumber(right);
  return a != null && b != null && Math.abs(a - b) <= tolerance;
}

function numberplateTransformLooksLikeActorRaw(placement = {}, transform = {}) {
  if (numberplateSurfaceKind(placement) !== "door") return false;
  const rotation = Array.isArray(placement.rotation) ? placement.rotation : [];
  const scale = Array.isArray(placement.scale) ? placement.scale : [];
  const rz = numberplateTransformNumber(transform.rz);
  const sy = numberplateTransformNumber(transform.sy);
  const x = numberplateTransformNumber(transform.x);
  const actorRz = numberplateTransformNumber(rotation[2]);
  const actorDepth = numberplateTransformNumber(scale[1]);
  const hasActorRoll = rz != null && Math.abs(rz) >= 45;
  const matchesActorRoll = actorRz != null && rz != null && numberplateNumbersClose(rz, actorRz, 1);
  const matchesActorDepth = actorDepth != null && sy != null && actorDepth <= 0.35 && numberplateNumbersClose(Math.abs(sy), Math.abs(actorDepth), 0.025);
  const centeredLikeActor = x == null || Math.abs(x) < 0.08;
  return (hasActorRoll && matchesActorDepth) || (matchesActorRoll && centeredLikeActor);
}

function hoodRotationBaseline(calibration = {}) {
  return {
    ry: Number.isFinite(Number(calibration?.ry)) ? Number(calibration.ry) : 0,
    rz: Number.isFinite(Number(calibration?.rz)) ? Number(calibration.rz) : 0,
  };
}

function hoodPreviewRotationKey(calibration = {}) {
  const key = String(calibration?.previewRotationKey || calibration?.preview_rotation_key || "").trim().toLowerCase();
  return ["rx", "ry", "rz"].includes(key) ? key : "";
}

function previewTransformForNumberplate(placement = {}, transform = {}, calibration = {}) {
  const mapped = { ...transform };
  if (numberplateSurfaceKind(placement) !== "hood") return mapped;
  if (hoodPreviewRotationKey(calibration) === "rz") return mapped;
  const baseline = hoodRotationBaseline(calibration);
  const ry = Number(transform?.ry);
  const rz = Number(transform?.rz);
  if (Number.isFinite(ry) && Number.isFinite(rz)) {
    mapped.ry = baseline.ry + (rz - baseline.rz);
    mapped.rz = baseline.rz + (ry - baseline.ry);
  }
  return mapped;
}

function uiTransformFromNumberplatePreview(placement = {}, transform = {}, calibration = {}) {
  const mapped = { ...transform };
  if (numberplateSurfaceKind(placement) !== "hood") return mapped;
  if (hoodPreviewRotationKey(calibration) === "rz") return mapped;
  const baseline = hoodRotationBaseline(calibration);
  const ry = Number(transform?.ry);
  const rz = Number(transform?.rz);
  if (Number.isFinite(ry) && Number.isFinite(rz)) {
    mapped.ry = baseline.ry + (rz - baseline.rz);
    mapped.rz = baseline.rz + (ry - baseline.ry);
  }
  return mapped;
}

function savedNumberplateTunerState(key, placement = {}) {
  const saved = numberplateCalibrationMap()[key];
  if (!saved?.transform) return null;
  if (numberplateTransformLooksLikeActorRaw(placement, saved.transform)) return null;
  const defaultTextLayer = numberplateTextLayerDefault(selectedNumberplateBackground());
  return {
    transform: normalizeVehicleSideNumberplateTransform(placement, saved.transform),
    opacity: saved.opacity ?? 1,
    text: "",
    textLayer: coerceNumberplateTextLayerValues(saved.textLayer || saved.textPlacement || {}, defaultTextLayer),
  };
}

function currentNumberplateTextLayer() {
  return numberplateTextLayerDefault(selectedNumberplateBackground());
}

function currentNumberplateTunerState() {
  return {
    transform: currentNumberplateTransform(),
    opacity: normalizePreviewOpacity(numberValue("liveryPreviewPlateOpacity", 1)),
    text: "",
    textLayer: currentNumberplateTextLayer(),
  };
}

function storeCurrentNumberplateTunerState() {
  const key = selectedNumberplateTunerKey();
  if (!key) return;
  state.liveryPreviewNumberplateTunerValues[key] = currentNumberplateTunerState();
}

function markNumberplateTunerReady(key = selectedNumberplateTunerKey()) {
  state.liveryPreviewNumberplateTunerReadyKey = key || "";
}

function numberplateUndoStack(key = selectedNumberplateTunerKey()) {
  if (!key) return [];
  if (!Array.isArray(state.liveryPreviewNumberplateUndo[key])) {
    state.liveryPreviewNumberplateUndo[key] = [];
  }
  return state.liveryPreviewNumberplateUndo[key];
}

function numberplateSnapshotEquals(left = {}, right = {}) {
  const a = left.transform || {};
  const b = right.transform || {};
  return ["x", "y", "z", "rx", "ry", "rz", "sx", "sy"].every((key) => Math.abs(Number(a[key] ?? 0) - Number(b[key] ?? 0)) < 0.0001)
    && Math.abs(Number(left.opacity ?? 1) - Number(right.opacity ?? 1)) < 0.0001;
}

function updateNumberplateUndoButton() {
  const button = $("liveryPreviewUndoNumberplate");
  if (!button) return;
  button.disabled = numberplateUndoStack().length <= 0;
}

function pushNumberplateUndoSnapshot(snapshot = currentNumberplateTunerState()) {
  const key = selectedNumberplateTunerKey();
  if (!key || !snapshot?.transform) return;
  const stack = numberplateUndoStack(key);
  if (stack.length && numberplateSnapshotEquals(stack[stack.length - 1], snapshot)) return;
  stack.push(JSON.parse(JSON.stringify(snapshot)));
  while (stack.length > 3) stack.shift();
  updateNumberplateUndoButton();
}

function clearNumberplateUndo(key = selectedNumberplateTunerKey()) {
  if (key) state.liveryPreviewNumberplateUndo[key] = [];
  updateNumberplateUndoButton();
}

function selectedNumberplateLabel(placement = selectedNumberplatePlacement().placement, fallbackIndex = selectedNumberplatePlacement().index) {
  const label = placement?.label || "Numberplate";
  const surface = placement?.surface ?? placement?.outer_field_number ?? "";
  const actorIndex = placement?.index ?? fallbackIndex ?? "";
  return `${label}${surface ? ` | Surface ${surface}` : ""}${actorIndex !== "" ? ` | #${actorIndex}` : ""}`;
}

function renderSelectedNumberplateLabel() {
  const { placement, index } = selectedNumberplatePlacement();
  const label = $("liveryPreviewNumberplateSelectedLabel");
  if (label) label.textContent = placement && Object.keys(placement).length ? selectedNumberplateLabel(placement, index) : "No plate selected";
  renderNumberplateActorGhostOffsetSummary();
}

function renderNumberplateWorkflowStatus(status = "") {
  const node = $("liveryPreviewNumberplateWorkflowStatus");
  if (!node) return;
  node.classList.remove("is-dirty", "needs-apply", "is-applied");
  if (state.liveryPreviewNumberplateUnsavedChanges) {
    node.textContent = "Unsaved preview changes";
    node.classList.add("is-dirty");
    return;
  }
  if (status === "applied") {
    node.textContent = "Applied - activate in game";
    node.classList.add("is-applied");
    return;
  }
  if (state.liveryPreviewNumberplateSavedNeedsApply) {
    node.textContent = "Preview saved - apply to skin";
    node.classList.add("needs-apply");
    return;
  }
  node.textContent = "Saved";
}

function updateNumberplateGizmoUnsavedIndicator(tunerState = null) {
  const tuner = tunerState || window.LiveryLabPreview3d?.getNumberplateTunerState?.() || {};
  state.liveryPreviewNumberplateUnsavedChanges = Boolean(tuner.dirty);
  const badge = $("liveryPreviewNumberplateUnsaved");
  if (badge) badge.hidden = !state.liveryPreviewNumberplateUnsavedChanges;
  renderNumberplateWorkflowStatus();
  updateNumberplateUndoButton();
}

function selectedNumberplateToolMode() {
  const active = document.querySelector("[data-numberplate-tool].active");
  return active?.dataset.numberplateTool || "translate";
}

function updateNumberplateToolFields(mode = selectedNumberplateToolMode()) {
  const next = ["translate", "rotate", "scale"].includes(String(mode || "")) ? String(mode) : "translate";
  document.querySelectorAll("[data-numberplate-tool-fields]").forEach((panel) => {
    panel.hidden = panel.dataset.numberplateToolFields !== next;
  });
}

function setNumberplateToolMode(mode = "translate", options = {}) {
  const next = ["translate", "rotate", "scale"].includes(String(mode || "")) ? String(mode) : "translate";
  document.querySelectorAll("[data-numberplate-tool]").forEach((button) => {
    const active = button.dataset.numberplateTool === next;
    button.classList.toggle("active", active);
    button.setAttribute("aria-pressed", active ? "true" : "false");
  });
  updateNumberplateToolFields(next);
  if (options.apply !== false) updateNumberplateGizmoMode();
  return next;
}

async function syncNumberplateInputsFromGizmo(tunerState = {}) {
  if (!tunerState?.transform) return;
  if (state.liveryPreviewNumberplateActiveInputId) return;
  const { placement } = selectedNumberplatePlacement();
  const calibration = await selectedNumberplateBuiltinCalibration();
  applyNumberplateTunerState({
    transform: uiTransformFromNumberplatePreview(placement, tunerState.transform, calibration || tunerState.transform),
    opacity: numberValue("liveryPreviewPlateOpacity", 1),
    text: "",
    textLayer: currentNumberplateTextLayer(),
  });
  storeCurrentNumberplateTunerState();
  updateNumberplateGizmoUnsavedIndicator(tunerState);
  const mode = tunerState.mode || selectedNumberplateToolMode();
  $("liveryPreviewNumberplateInfo").textContent = [
    `Gizmo ${mode}: ${selectedNumberplateTunerKey()}`,
    "Use Roundtrip to inspect the preview-to-actor delta before exporting.",
  ].join("\n");
}

function handleNumberplateTransformStart(detail = {}) {
  const snapshot = {
    transform: detail.transform || currentNumberplateTransform(),
    opacity: normalizePreviewOpacity(numberValue("liveryPreviewPlateOpacity", 1)),
    text: "",
    textLayer: currentNumberplateTextLayer(),
  };
  pushNumberplateUndoSnapshot(snapshot);
}

function setNumberplateGizmoEnabled(enabled = true) {
  const mode = selectedNumberplateToolMode();
  const result = window.LiveryLabPreview3d?.setNumberplateTunerEnabled?.(enabled, mode) || {};
  state.liveryPreviewNumberplateGizmoEnabled = Boolean(result.enabled);
  updateNumberplateGizmoUnsavedIndicator(result);
  if (enabled && !result.active) {
    $("liveryPreviewNumberplateInfo").textContent = [
      "Numberplate gizmo could not attach.",
      ...numberplateGizmoDiagnostics(result),
      "Apply the selected overlay first, then tune again.",
    ].join("\n");
  }
  return result;
}

function updateNumberplateGizmoMode() {
  const mode = selectedNumberplateToolMode();
  const result = window.LiveryLabPreview3d?.setNumberplateTunerMode?.(mode) || {};
  if (state.liveryPreviewNumberplateGizmoEnabled) {
    $("liveryPreviewNumberplateInfo").textContent = `Numberplate gizmo mode: ${mode}.`;
  }
  updateNumberplateGizmoUnsavedIndicator(result);
}

function numberplateGizmoDiagnostics(result = {}) {
  return [
    `Gizmo ${result.active ? "active" : "inactive"}: ${result.mode || selectedNumberplateToolMode()}`,
    `Viewer API: ${typeof window.LiveryLabPreview3d?.setNumberplateTunerEnabled === "function" ? "yes" : "no"}`,
    `Overlay target: ${result.hasTarget ? "yes" : "no"}`,
    `Overlay mode: ${result.numberplateMode || "-"}`,
    `Anchor: ${result.hasAnchor ? "yes" : "no"}`,
    `Controls: ${result.hasControls ? "yes" : "no"}`,
    `Helper: ${result.helperInScene ? "scene" : "no"}${result.helperVisible ? " visible" : ""}`,
  ];
}

function applyNumberplateTunerState(values) {
  const transform = values?.transform || values || {};
  setNumberInput("liveryPreviewPlateX", transform.x);
  setNumberInput("liveryPreviewPlateY", transform.y);
  setNumberInput("liveryPreviewPlateZ", transform.z);
  setNumberInput("liveryPreviewPlateRotX", transform.rx);
  setNumberInput("liveryPreviewPlateRotY", transform.ry);
  setNumberInput("liveryPreviewPlateRotZ", transform.rz);
  setNumberplateScaleInputs(transform);
  setNumberInput("liveryPreviewPlateOpacity", values?.opacity ?? 1);
}

function setNumberInput(id, value, options = {}) {
  const input = $(id);
  if (!input) return;
  if (options.preserveFocused !== false && document.activeElement === input) return;
  const numeric = Number(value);
  input.value = Number.isFinite(numeric) ? String(Math.round(numeric * 1000) / 1000) : input.value;
}

function numberplateEditInputIsReady(id) {
  const input = $(id);
  if (!input) return true;
  const raw = String(input.value || "").trim();
  if (!raw || ["-", "+", ".", "-.", "+."].includes(raw) || /[.,]$/.test(raw)) return false;
  return Number.isFinite(Number(raw));
}

function numberplateEditInputsReady() {
  return [
    "liveryPreviewPlateX",
    "liveryPreviewPlateY",
    "liveryPreviewPlateZ",
    "liveryPreviewPlateRotX",
    "liveryPreviewPlateRotY",
    "liveryPreviewPlateRotZ",
    "liveryPreviewPlateScale",
    "liveryPreviewPlateOpacity",
  ].every(numberplateEditInputIsReady);
}

function commitNumberplateEditInput(id) {
  const input = $(id);
  if (!input) return;
  const raw = String(input.value || "").trim();
  if (!raw) return;
  const numeric = Number(raw);
  if (Number.isFinite(numeric)) setNumberInput(id, numeric, { preserveFocused: false });
}

function normalizePreviewOpacity(value) {
  const numeric = Number(value);
  if (!Number.isFinite(numeric)) return 1;
  return numeric > 0 && numeric <= 1 ? numeric : 1;
}

function applyNumberplatePlacementDefaults(placement) {
  const position = placement?.position || [0, 0, 0];
  const rotation = placement?.rotation || [0, 0, 0];
  const scale = placement?.scale || [1, 0.28, 1];
  setNumberInput("liveryPreviewPlateX", position[0] ?? 0);
  setNumberInput("liveryPreviewPlateY", position[1] ?? 0);
  setNumberInput("liveryPreviewPlateZ", position[2] ?? 0);
  setNumberInput("liveryPreviewPlateRotX", rotation[0] ?? 0);
  setNumberInput("liveryPreviewPlateRotY", rotation[1] ?? 0);
  setNumberInput("liveryPreviewPlateRotZ", rotation[2] ?? 0);
  setNumberplateScaleInputs({ sx: Math.abs(Number(scale[0] ?? 1)) || 1, sy: Math.abs(Number(scale[1] ?? 0.28)) || 0.28 });
  setNumberInput("liveryPreviewPlateOpacity", normalizePreviewOpacity(placement?.opacity));
}

function applyNumberplateVisibleDefaults() {
  const transform = window.LiveryLabPreview3d?.getDefaultNumberplateTransform?.();
  if (!transform) return false;
  setNumberInput("liveryPreviewPlateX", transform.x);
  setNumberInput("liveryPreviewPlateY", transform.y);
  setNumberInput("liveryPreviewPlateZ", transform.z);
  setNumberInput("liveryPreviewPlateRotX", transform.rx);
  setNumberInput("liveryPreviewPlateRotY", transform.ry);
  setNumberInput("liveryPreviewPlateRotZ", transform.rz);
  setNumberplateScaleInputs(transform);
  setNumberInput("liveryPreviewPlateOpacity", 1);
  return true;
}

async function applyNumberplateOverlay(rebuild = false) {
  const data = state.liveryPreviewNumberplate || {};
  const background = selectedNumberplateBackground(data);
  const backgroundPath = background.path;
  if (!backgroundPath) {
    $("liveryPreviewNumberplateInfo").textContent = "No numberplate background PNG is available.";
    return;
  }
  const { placement } = selectedNumberplatePlacement();
  const calibration = await selectedNumberplateBuiltinCalibration();
  const transform = currentNumberplateTransform();
  const payload = {
    backgroundPath,
    fontPath: "",
    fontKey: "",
    text: "",
    textLayer: currentNumberplateTextLayer(),
    textColor: null,
    opacity: normalizePreviewOpacity(numberValue("liveryPreviewPlateOpacity", 1)),
    transform: previewTransformForNumberplate(placement, transform, calibration || transform),
    occludeByModel: true,
  };
  try {
    const overlayState = window.LiveryLabPreview3d?.hasNumberplateOverlay?.() || {};
    if (rebuild || !overlayState.visible || !overlayState.single) {
      const result = await window.LiveryLabPreview3d.showNumberplateOverlay(payload);
      if (result?.cancelled) return;
    } else {
      window.LiveryLabPreview3d.updateNumberplateOverlay(payload);
    }
    $("liveryPreviewNumberplateInfo").textContent = [
      `Overlay active: ${background.rel || backgroundPath}`,
    ].join("\n");
    setLiveryPreviewNumberplatesVisible(false);
  } catch (error) {
    $("liveryPreviewNumberplateInfo").textContent = `Overlay failed: ${error?.message || error}`;
  }
}

function mappedNumberplatesAreEditable() {
  const overlayState = window.LiveryLabPreview3d?.hasNumberplateOverlay?.() || {};
  return overlayState.visible && overlayState.mode === "mapped-group";
}

function selectMappedNumberplateForCurrentSelection() {
  const { placement, index } = selectedNumberplatePlacement();
  if (!placement || placement.index == null) return null;
  return window.LiveryLabPreview3d?.selectMappedNumberplate?.({
    placementIndex: index,
    actorIndex: placement.index,
    label: placement.label || "",
    surface: placement.surface ?? placement.outer_field_number ?? "",
  }) || null;
}

function deselectNumberplateViewerTarget() {
  window.LiveryLabPreview3d?.deselectNumberplate?.();
  state.liveryPreviewNumberplateGizmoEnabled = false;
  state.liveryPreviewNumberplateInputUndoArmed = false;
  const label = $("liveryPreviewNumberplateSelectedLabel");
  if (label) label.textContent = "No plate selected";
  updateNumberplateUndoButton();
}

async function deselectNumberplateEditor(options = {}) {
  const fromUser = options.fromUser !== false;
  if (state.liveryPreviewNumberplateUnsavedChanges) {
    const { placement, index } = selectedNumberplatePlacement();
    const choice = await showChoiceModal({
      type: "warning",
      title: "Unsaved preview changes",
      subtitle: "Deselect plate",
      message: "Save the current preview placement before leaving edit mode?",
      details: [
        `Current: ${selectedNumberplateLabel(placement, index)}`,
        "Save Preview Placement keeps the tuned preview values.",
        "Discard Changes restores the visible numberplates from the last saved preview data.",
      ].join("\n"),
      confirmLabel: "Save and Deselect",
      alternateLabel: "Discard Changes",
      cancelLabel: "Keep Editing",
    });
    if (choice === true) {
      const saved = await saveSelectedNumberplatePlacement(false);
      if (!saved) return false;
    } else if (choice === "alternate") {
      state.liveryPreviewNumberplateUnsavedChanges = false;
      updateNumberplateGizmoUnsavedIndicator(window.LiveryLabPreview3d?.markNumberplateTunerSaved?.() || { dirty: false });
      clearNumberplateUndo();
      await showMappedNumberplates();
    } else {
      return false;
    }
  }
  deselectNumberplateViewerTarget();
  if (fromUser) {
    $("liveryPreviewNumberplateInfo").textContent = state.liveryPreviewNumberplateSavedNeedsApply
      ? "Plate deselected. Preview changes are saved; Apply Placement to Skin is still needed for EVO."
      : "Plate deselected. Click a numberplate on the model to edit it.";
  }
  return true;
}

async function undoSelectedNumberplateMove() {
  const stack = numberplateUndoStack();
  const snapshot = stack.pop();
  updateNumberplateUndoButton();
  if (!snapshot?.transform) return false;
  const { placement } = selectedNumberplatePlacement();
  const calibration = await selectedNumberplateBuiltinCalibration();
  applyNumberplateTunerState(snapshot);
  const previewTransform = previewTransformForNumberplate(placement, snapshot.transform, calibration || snapshot.transform);
  if (mappedNumberplatesAreEditable()) {
    window.LiveryLabPreview3d?.updateSelectedNumberplateTransform?.(previewTransform);
  } else {
    await applyNumberplateOverlay(false);
  }
  storeCurrentNumberplateTunerState();
  state.liveryPreviewNumberplateUnsavedChanges = true;
  updateNumberplateGizmoUnsavedIndicator({ dirty: true });
  $("liveryPreviewNumberplateInfo").textContent = [
    `Undo: ${selectedNumberplateTunerKey()}`,
    "Preview placement has unsaved changes.",
  ].join("\n");
  return true;
}

async function loadSelectedNumberplateTuner(showOverlay = true, reset = false) {
  const data = state.liveryPreviewNumberplate || {};
  const background = selectedNumberplateBackground(data);
  const backgroundPath = background.path;
  const { placement, index } = selectedNumberplatePlacement();
  const key = selectedNumberplateTunerKey();
  markNumberplateTunerReady("");
  if (!placement || !Object.keys(placement).length) {
    $("liveryPreviewNumberplateInfo").textContent = "No selected placement is available.";
    return;
  }
  try {
    applyNumberplateFitToViewer(state.liveryPreviewNumberplateFit || NUMBERPLATE_FIT_DEFAULT);
    await loadNumberplateBuiltinCalibrations();
    await loadNumberplateStudioTemplatesForPreview();
    const defaultTextLayer = numberplateTextLayerDefault(background);
    const saved = state.liveryPreviewNumberplateTunerValues[key] || savedNumberplateTunerState(key, placement);
    renderSelectedNumberplateLabel();
    if (showOverlay && mappedNumberplatesAreEditable()) {
      const selected = selectMappedNumberplateForCurrentSelection();
      if (!selected?.selected) throw new Error("Selected mapped numberplate is not available");
      let transform = selected.transform;
      if (reset) {
        transform = await window.LiveryLabPreview3d?.getMappedNumberplateTransform?.({
          placement,
          index,
          backgroundPath,
          carId: liveryPreviewCarId(),
          calibrations: await numberplateViewerCalibrations(),
          forceOpaque: false,
        }) || transform;
        window.LiveryLabPreview3d?.updateSelectedNumberplateTransform?.(previewTransformForNumberplate(placement, transform, await selectedNumberplateBuiltinCalibration() || transform));
      }
      applyNumberplateTunerState({ transform, opacity: 1, text: "", textLayer: defaultTextLayer });
      state.liveryPreviewNumberplateTunerValues[key] = currentNumberplateTunerState();
      markNumberplateTunerReady(key);
      updateNumberplateUndoButton();
      const gizmo = setNumberplateGizmoEnabled(true);
      $("liveryPreviewNumberplateInfo").textContent = [
        `Editing selected plate: ${selectedNumberplateLabel(placement, index)}`,
        "Move, rotate, or scale with the gizmo. Save Preview Placement before selecting another plate.",
        state.liveryPreviewNumberplateSavedNeedsApply ? "Preview values are saved; Apply Placement to Skin is still needed for EVO." : "",
        ...(debugToolEnabled("numberplateCalibration") ? numberplateGizmoDiagnostics(gizmo) : []),
      ].filter(Boolean).join("\n");
      return;
    }
    if (!reset && saved) {
      applyNumberplateTunerState(saved);
      markNumberplateTunerReady(key);
      if (showOverlay) await applyNumberplateOverlay(true);
      const gizmo = showOverlay ? setNumberplateGizmoEnabled(true) : {};
      $("liveryPreviewNumberplateInfo").textContent = [
        `Tuning selected placement: ${placement.label || "-"} (${placement.index ?? index})`,
        "Restored saved tuner values for this surface.",
        ...(debugToolEnabled("numberplateCalibration") ? numberplateGizmoDiagnostics(gizmo) : []),
        "Use the gizmo handles or edit values directly. Copy Values stores the current tuner numbers.",
      ].filter(Boolean).join("\n");
      return;
    }
    const transform = await window.LiveryLabPreview3d?.getMappedNumberplateTransform?.({
      placement,
      index,
      backgroundPath,
      carId: liveryPreviewCarId(),
      calibrations: await numberplateViewerCalibrations(),
      forceOpaque: false,
    });
    if (!transform) throw new Error("3D viewer did not return a mapped transform");
    applyNumberplateTunerState({ transform, opacity: 1, text: "", textLayer: defaultTextLayer });
    state.liveryPreviewNumberplateTunerValues[key] = currentNumberplateTunerState();
    markNumberplateTunerReady(key);
    updateNumberplateUndoButton();
    if (showOverlay) await applyNumberplateOverlay(true);
    const gizmo = showOverlay ? setNumberplateGizmoEnabled(true) : {};
    $("liveryPreviewNumberplateInfo").textContent = [
      `Tuning selected placement: ${placement.label || "-"} (${placement.index ?? index})`,
      `Texture: ${background.rel || backgroundPath || "-"}`,
      ...(debugToolEnabled("numberplateCalibration") ? numberplateGizmoDiagnostics(gizmo) : []),
      "Use the gizmo handles or edit values directly, then save the preview placement.",
    ].filter(Boolean).join("\n");
  } catch (error) {
    $("liveryPreviewNumberplateInfo").textContent = `Tuner setup failed: ${error?.message || error}`;
  }
}

async function copyNumberplateTunerValues() {
  const { placement } = selectedNumberplatePlacement();
  const payload = {
    placement: placement?.label || "",
    actorIndex: placement?.index ?? "",
    transform: currentNumberplateTransform(),
  };
  const text = JSON.stringify(payload, null, 2);
  try {
    await navigator.clipboard.writeText(text);
    $("liveryPreviewNumberplateInfo").textContent = `Copied tuner values:\n${text}`;
  } catch (_error) {
    $("liveryPreviewNumberplateInfo").textContent = `Tuner values:\n${text}`;
  }
}

async function copySelectedNumberplateCalibrationRow() {
  if (!debugToolEnabled("numberplateCalibration")) {
    $("liveryPreviewNumberplateInfo").textContent = "Calibration copy requires debug mode.";
    return;
  }
  storeCurrentNumberplateTunerState();
  const { placement, index } = selectedNumberplatePlacement();
  if (!placement || placement.index == null) {
    $("liveryPreviewNumberplateInfo").textContent = "No selected numberplate placement is available.";
    return;
  }
  const key = selectedNumberplateTunerKey().toUpperCase();
  const carId = liveryPreviewCarId();
  const transform = currentNumberplateTransform();
  const background = selectedNumberplateBackground();
  const textLayer = currentNumberplateTextLayer();
  const textColor = selectedNumberplateTextColor();
  const textLayerKey = selectedNumberplateTextLayerKey(background);
  const row = {
    x: Number(transform.x),
    y: Number(transform.y),
    z: Number(transform.z),
    rx: Number(transform.rx),
    ry: Number(transform.ry),
    rz: Number(transform.rz),
    sx: Number(transform.sx),
    sy: Number(transform.sy),
  };
  const surfaceKind = numberplateSurfaceKind(placement);
  const actorPosition = Array.isArray(placement.position) ? placement.position : [];
  const actorRotation = Array.isArray(placement.rotation) ? placement.rotation : [];
  const actorScale = Array.isArray(placement.scale) ? placement.scale : [];
  const actorBaseline = {
    position_x: actorPosition[0] ?? null,
    position_y: actorPosition[1] ?? null,
    position_z: actorPosition[2] ?? null,
    rotation_x: actorRotation[0] ?? null,
    rotation_y: actorRotation[1] ?? null,
    rotation_z: actorRotation[2] ?? null,
    scale_x: actorScale[0] ?? null,
    scale_y: actorScale[1] ?? null,
    scale_z: actorScale[2] ?? null,
  };
  const exportDefaults = surfaceKind === "door"
    ? { position_y: 0.5, position_z: 0.5, scale_x: 1, scale_z: 1 }
    : surfaceKind === "hood"
      ? { position_x: 0.5, position_y: 0.5, position_z: 0.5, scale_x: 1, scale_z: 1 }
      : {};
  const rowWithBaseline = { ...row, actorBaseline };
  const patch = {
    [carId]: {
      ...(Object.keys(exportDefaults).length ? { __export: { [surfaceKind]: exportDefaults } } : {}),
      [key]: rowWithBaseline,
    },
  };
  const textLayerPatch = textLayerKey
    ? {
        textLayers: {
          backgrounds: {
            [textLayerKey]: textLayer,
          },
        },
      }
    : {};
  const payload = {
    carId,
    placementKey: key,
    backgroundKey: textLayerKey,
    placement: {
      label: placement.label || "",
      actorIndex: placement.index ?? index,
      surface: placement.surface ?? placement.outer_field_number ?? "",
      numberplateIndex: placement.numberplate_index ?? placement.field_2 ?? "",
    },
    row: rowWithBaseline,
    textLayer,
    textColor,
    patch,
    textLayerPatch,
    note: "Paste the row, patch, or textLayerPatch into web/assets/numberplate_calibrations.json after checking whether this is a baseline calibration or a custom placement target.",
  };
  const text = JSON.stringify(payload, null, 2);
  try {
    await navigator.clipboard.writeText(text);
    $("liveryPreviewNumberplateInfo").textContent = [
      `Copied calibration row for ${carId} / ${key}.`,
      "Review before adding it to numberplate_calibrations.json.",
      text,
    ].join("\n");
  } catch (_error) {
    $("liveryPreviewNumberplateInfo").textContent = `Calibration row:\n${text}`;
  }
}

async function runNumberplatePlacementDiagnostics(mode = "compare") {
  const data = state.liveryPreviewNumberplate || {};
  const placements = data.placements || [];
  const background = selectedNumberplateBackground(data);
  const diagnostics = $("liveryPreviewNumberplateDiagnostics");
  try {
    applyNumberplateFitToViewer(state.liveryPreviewNumberplateFit || NUMBERPLATE_FIT_DEFAULT);
    const result = await window.LiveryLabPreview3d?.showNumberplatePlacementDiagnostics?.({
      placements,
      mode,
      carId: liveryPreviewCarId(),
      backgroundPath: background.path,
      calibrations: await numberplateViewerCalibrations(),
      forceOpaque: false,
    });
    if (result?.cancelled) return;
    if (diagnostics) {
      diagnostics.hidden = false;
      diagnostics.textContent = JSON.stringify(result?.rows || [], null, 2);
    }
    $("liveryPreviewNumberplateInfo").textContent = [
      `Placement diagnostics active: ${result?.placements ?? placements.length} surfaces (${result?.mode || mode}).`,
      `Filtered placements: ${data?.placementFilter?.selected ?? placements.length} of ${data?.placementFilter?.total ?? placements.length}`,
      "Raw Actor uses the parsed actor position, rotation and scale values directly.",
      "Mapped Preview uses the current LiveryLab actor-to-preview mapping.",
      debugToolEnabled("numberplateCalibration") ? `Fit: ${numberplateFitSummary()}` : "",
    ].filter(Boolean).join("\n");
  } catch (error) {
    if (diagnostics) diagnostics.hidden = true;
    $("liveryPreviewNumberplateInfo").textContent = `Placement diagnostics failed: ${error?.message || error}`;
  }
}

async function runNumberplateRoundtripDiagnostics() {
  const skinKey = state.liveryPreviewSkinKey || "";
  const { placement, index } = selectedNumberplatePlacement();
  const diagnostics = $("liveryPreviewNumberplateDiagnostics");
  if (!skinKey || !placement || placement.index == null) {
    $("liveryPreviewNumberplateInfo").textContent = "No selected numberplate placement is available.";
    return;
  }
  storeCurrentNumberplateTunerState();
  const calibration = await selectedNumberplateApplyCalibration();
  const payload = {
    skinKey,
    root: state.root,
    placement,
    transform: currentNumberplateTransform(),
    calibration: calibration || {},
  };
  const result = await window.pywebview.api.inspect_livery_numberplate_roundtrip(payload);
  if (!result.ok) {
    if (diagnostics) diagnostics.hidden = true;
    $("liveryPreviewNumberplateInfo").textContent = `Roundtrip diagnostics failed: ${result.message || ""}`;
    return;
  }
  if (diagnostics) {
    diagnostics.hidden = false;
    diagnostics.textContent = JSON.stringify(result.data || {}, null, 2);
  }
  const warnings = Array.isArray(result.data?.warnings) ? result.data.warnings : [];
  $("liveryPreviewNumberplateInfo").textContent = [
    `Roundtrip diagnostics: ${placement.label || "-"} #${placement.index ?? index}`,
    `Surface: ${result.data?.surfaceKind || "-"} (${result.data?.surface ?? "-"})`,
    `Calibration: ${result.data?.hasCalibration ? "yes" : "no"}`,
    `Actor target export: ${result.data?.supported ? "ready" : "diagnostic only"}`,
    ...(warnings.length ? ["Warnings:", ...warnings.map((row) => `- ${row}`)] : []),
  ].join("\n");
}

async function showMappedNumberplates() {
  if (liveryPreviewNumberplatesBlocked()) {
    const message = normalizeNumberplateSupport(state.liveryPreviewNumberplateSupport || state.liveryPreviewProject || {}).message
      || "This car does not support numberplates yet";
    $("liveryPreviewNumberplateInfo").textContent = message;
    setLiveryPreviewNumberplatesVisible(false);
    return;
  }
  if (liveryPreviewNumberplatesHidden()) {
    $("liveryPreviewNumberplateInfo").textContent = "Numberplates are disabled for this skin.";
    setLiveryPreviewNumberplatesVisible(false);
    window.LiveryLabPreview3d?.removeNumberplateOverlay?.();
    return;
  }
  const data = state.liveryPreviewNumberplate || {};
  const placements = data.placements || [];
  const background = selectedNumberplateBackground(data);
  const backgroundPath = background.path;
  if (!backgroundPath && !placements.some((placement) => placement?.backgroundPng)) {
    $("liveryPreviewNumberplateInfo").textContent = "No numberplate background PNG is available.";
    setLiveryPreviewNumberplatesVisible(false);
    return;
  }
  try {
    applyNumberplateFitToViewer(state.liveryPreviewNumberplateFit || NUMBERPLATE_FIT_DEFAULT);
    state.liveryPreviewNumberplateGizmoEnabled = false;
    updateNumberplateGizmoUnsavedIndicator(window.LiveryLabPreview3d?.setNumberplateTunerEnabled?.(false) || {});
    const result = await window.LiveryLabPreview3d?.showMappedNumberplates?.({
      placements,
      backgroundPath,
      fontPath: "",
      fontKey: "",
      carId: liveryPreviewCarId(),
      calibrations: await numberplateViewerCalibrations(),
      text: "",
      textLayer: currentNumberplateTextLayer(),
      textColor: null,
      opacity: normalizePreviewOpacity(numberValue("liveryPreviewPlateOpacity", 1)),
      forceOpaque: false,
      occludeByModel: true,
      debugLabels: debugToolEnabled("numberplateCalibration") && state.liveryPreviewNumberplateDebugLabels !== false,
    });
    if (result?.cancelled) return;
    $("liveryPreviewNumberplateInfo").textContent = [
      `Real mapped numberplates active: ${result?.placements ?? placements.length} surfaces.`,
      `Texture: ${result?.backgroundLabel || background.rel || result?.backgroundPath || backgroundPath}`,
      `Filtered placements: ${data?.placementFilter?.selected ?? placements.length} of ${data?.placementFilter?.total ?? placements.length}`,
      debugToolEnabled("numberplateCalibration") ? `Fit: ${numberplateFitSummary()}` : "",
    ].filter(Boolean).join("\n");
    setLiveryPreviewNumberplatesVisible(true);
  } catch (error) {
    $("liveryPreviewNumberplateInfo").textContent = `Real mapped numberplates failed: ${error?.message || error}`;
    setLiveryPreviewNumberplatesVisible(false);
  }
}

function setNumberplateActorGhostDiagnostics(result = {}) {
  const diagnostics = $("liveryPreviewNumberplateDiagnostics");
  if (!diagnostics || !debugToolEnabled("numberplateCalibration")) return "";
  const payload = result?.actorGhostDiagnostics || {};
  diagnostics.hidden = false;
  diagnostics.textContent = JSON.stringify(payload, null, 2);
  if (!payload || typeof payload !== "object" || !Array.isArray(payload.rows)) return "";
  const source = payload.actorCalibrationSource || result?.actorCalibrationSource || "unknown";
  const status = payload.roundtripMatch ? "roundtrip match" : "differs from preview";
  const tuned = Number(payload.actorGhostOffsetCount || 0);
  return `Actor ghost diagnostics: ${status} (${source}${tuned ? `, ${tuned} tuned` : ""}).`;
}

async function showNumberplateActorGhostOverlay() {
  const data = state.liveryPreviewNumberplate || {};
  const placements = data.placements || [];
  const background = selectedNumberplateBackground(data);
  const backgroundPath = background.path;
  if (!backgroundPath && !placements.some((placement) => placement?.backgroundPng)) {
    $("liveryPreviewNumberplateInfo").textContent = "No numberplate background PNG is available.";
    setLiveryPreviewNumberplatesVisible(false);
    return;
  }
  try {
    applyNumberplateFitToViewer(state.liveryPreviewNumberplateFit || NUMBERPLATE_FIT_DEFAULT);
    const actorProjectionOffset = readNumberplateActorProjectionOffsetInputs();
    const previewCalibrations = await numberplateViewerCalibrations();
    const actorCalibrations = await numberplateViewerBuiltinCalibrations();
    const actorGhostOffsets = numberplateActorGhostOffsetsForViewer();
    state.liveryPreviewNumberplateGizmoEnabled = false;
    updateNumberplateGizmoUnsavedIndicator(window.LiveryLabPreview3d?.setNumberplateTunerEnabled?.(false) || {});
    const result = await window.LiveryLabPreview3d?.showNumberplatePreviewActorOverlay?.({
      placements,
      backgroundPath,
      fontPath: "",
      fontKey: "",
      carId: liveryPreviewCarId(),
      calibrations: previewCalibrations,
      actorCalibrations,
      actorGhostOffsets,
      text: "",
      textLayer: currentNumberplateTextLayer(),
      textColor: null,
      opacity: normalizePreviewOpacity(numberValue("liveryPreviewPlateOpacity", 1)),
      previewOpacity: 0.84,
      actorOpacity: 0.48,
      actorProjectionOffset,
      forceOpaque: false,
      debugLabels: debugToolEnabled("numberplateCalibration") && state.liveryPreviewNumberplateDebugLabels !== false,
    });
    if (result?.cancelled) return;
    const diagnosticsSummary = setNumberplateActorGhostDiagnostics(result);
    $("liveryPreviewNumberplateInfo").textContent = [
      `Preview + Actor Ghost active: ${result?.placements ?? placements.length} surfaces.`,
      "White plate = LiveryLab preview target.",
      "Blue transparent plate = built-in actor/game projection.",
      diagnosticsSummary,
      Object.keys(actorGhostOffsets).length ? `Actor ghost tuner offsets: ${Object.keys(actorGhostOffsets).length} placement(s).` : "",
      numberplateActorProjectionOffsetSummary(actorProjectionOffset),
      `Texture: ${result?.backgroundLabel || background.rel || result?.backgroundPath || backgroundPath}`,
    ].filter(Boolean).join("\n");
    setLiveryPreviewNumberplatesVisible(true);
  } catch (error) {
    $("liveryPreviewNumberplateInfo").textContent = `Actor ghost overlay failed: ${error?.message || error}`;
    setLiveryPreviewNumberplatesVisible(false);
  }
}

async function resetNumberplateActorProjectionOffset() {
  setNumberplateActorProjectionOffsetInputs(NUMBERPLATE_ACTOR_PROJECTION_OFFSET_DEFAULT);
  await showNumberplateActorGhostOverlay();
  setLiveryPreviewNumberplateDevPanelVisible(true);
}

async function saveSelectedNumberplatePlacement(writeActor = false, options = {}) {
  if (state.liveryPreviewReadOnly) {
    $("liveryPreviewNumberplateInfo").textContent = "Numberplate placement editing is disabled for read-only installed liveries.";
    return false;
  }
  const skinKey = state.liveryPreviewSkinKey || "";
  const { placement, index } = selectedNumberplatePlacement();
  if (!skinKey || !placement || placement.index == null) {
    $("liveryPreviewNumberplateInfo").textContent = "No selected numberplate placement is available.";
    return false;
  }
  const key = selectedNumberplateTunerKey();
  if (state.liveryPreviewNumberplateTunerReadyKey !== key) {
    $("liveryPreviewNumberplateInfo").textContent = "Placement values are still loading. Select the plate again before saving.";
    return false;
  }
  if (writeActor) {
    $("liveryPreviewNumberplateInfo").textContent = "Direct actor writes from 3D preview coordinates are disabled.";
    return false;
  }
  const transform = currentNumberplateTransform();
  if (numberplateTransformLooksLikeActorRaw(placement, transform)) {
    $("liveryPreviewNumberplateInfo").textContent = "Save blocked: placement values look like raw actor/game coordinates, not 3D Preview coordinates.";
    showStatusModal({
      type: "error",
      title: "Numberplate Overlay",
      subtitle: "Save blocked",
      message: "The selected placement looks like raw actor/game coordinates. Reload the 3D preview placement and save again.",
    });
    return false;
  }
  storeCurrentNumberplateTunerState();
  const payload = {
    skinKey,
    root: state.root,
    placement,
    transform,
    text: "",
    textLayer: currentNumberplateTextLayer(),
    writeActor,
  };
  const result = await window.pywebview.api.save_livery_numberplate_placement(payload);
  if (!result.ok) {
    $("liveryPreviewNumberplateInfo").textContent = `Save failed: ${result.message || ""}`;
    showStatusModal({
      type: "error",
      title: "Numberplate Overlay",
      subtitle: "Save failed",
      message: result.message || "Numberplate placement could not be saved.",
    });
    return false;
  }
  const overlay = result.data?.numberplateOverlay || {};
  state.liveryPreviewNumberplate.numberplateOverlay = overlay;
  state.liveryPreviewNumberplateSavedNeedsApply = true;
  updateNumberplateGizmoUnsavedIndicator(window.LiveryLabPreview3d?.markNumberplateTunerSaved?.() || { dirty: false });
  updateNumberplateUndoButton();
  renderNumberplateWorkflowStatus();
  $("liveryPreviewNumberplateInfo").textContent = result.message || (writeActor ? "Numberplate placement activated in game." : "Preview placement saved.");
  if (!options.quiet) {
    showStatusModal({
      type: "success",
      title: "Numberplate Overlay",
      subtitle: writeActor ? "Placement activated" : "Placement saved",
      message: result.message || (writeActor ? "Numberplate placement activated in game." : "Preview placement saved."),
      details: [
        `Save key: ${result.data?.saveKey || "-"}`,
        `Project backup: ${result.data?.projectBackup || "-"}`,
        `Actor backup: ${result.data?.actorBackup || "-"}`,
      ].join("\n"),
    });
  }
  return true;
}

async function reloadLiveryPreviewTextureFromMenu() {
  setLiveryPreviewWatchBadge("Reloading texture", "reloading");
  $("liveryPreviewStatus").textContent = "Reloading live texture...";
  const imageOk = await reloadPreview3dTexture({ reloadViewer: false });
  let viewerOk = false;
  let viewer = null;
  let materialMode = "";
  try {
    if (window.LiveryLabPreview3d?.reloadTexture) {
      const result = await reloadLiveryPreviewViewerTextureWithMaterialMode();
      viewer = result.viewer;
      materialMode = result.materialMode;
      viewerOk = true;
    }
  } catch (error) {
    $("liveryPreviewStatus").textContent = `Live texture reload failed:\n${error?.message || error}`;
  }
  if (imageOk || viewerOk) {
    setLiveryPreviewWatchBadge(`Reloaded ${new Date().toLocaleTimeString()}`, "watching");
    $("liveryPreviewStatus").textContent = [
      "Live texture reloaded.",
      `Preview mode: ${liveryPreviewModeLabel()}`,
      ...(viewer ? liveryPreviewAoStatusLines(viewer) : []),
      viewer ? `Textured materials: ${viewer.appliedMaterials}` : "",
      ...(materialMode ? liveryPreviewMaterialModeLegend(materialMode) : []),
    ].filter(Boolean).join("\n");
  } else {
    setLiveryPreviewWatchBadge("Reload failed", "error");
    $("liveryPreviewStatus").textContent = "Live texture reload failed.";
  }
  scheduleLiveryPreviewWatcherPoll();
}

function actorNudgeSelection(sign = 1) {
  const raw = $("liveryPreviewActorNudgeField")?.value || "position:0";
  const [vector, axisText] = String(raw).split(":");
  const axis = Number(axisText || 0);
  const magnitude = Math.abs(numberValue("liveryPreviewActorNudgeDelta", 0.05));
  return {
    vector,
    axis,
    delta: magnitude * (sign < 0 ? -1 : 1),
  };
}

function actorNudgeFieldLabel(vector, axis) {
  const axisLabels = ["x", "y", "z"];
  return `${vector}.${axisLabels[Number(axis)] || axis}`;
}

function actorNudgeValue(row, vector, axis) {
  const values = row?.[vector];
  if (!Array.isArray(values)) return null;
  const value = values[Number(axis)];
  return value == null || value === "" ? null : Number(value);
}

function actorGhostFieldLabel(key = "") {
  const labels = {
    x: "position.x",
    y: "position.y",
    z: "position.z",
    rx: "rotation.x",
    ry: "rotation.y",
    rz: "rotation.z",
    sx: "scale.x",
    sy: "scale.y",
  };
  return labels[key] || key || "-";
}

function actorGhostNudgeSelection(sign = 1) {
  const field = $("liveryPreviewActorGhostField")?.value || "x";
  const magnitude = Math.abs(numberValue("liveryPreviewActorGhostDelta", 0.01));
  return {
    field,
    delta: magnitude * (sign < 0 ? -1 : 1),
  };
}

async function nudgeSelectedNumberplateActorGhost(sign = 1) {
  const { placement, index } = selectedNumberplatePlacement();
  const key = numberplateActorGhostOffsetKey(placement, index);
  if (!key) {
    $("liveryPreviewNumberplateInfo").textContent = "No selected numberplate placement is available.";
    return;
  }
  const nudge = actorGhostNudgeSelection(sign);
  const current = selectedNumberplateActorGhostOffset();
  current[nudge.field] = numberplateActorGhostOffsetNumber(
    Number(current[nudge.field] || 0) + nudge.delta,
    NUMBERPLATE_ACTOR_GHOST_OFFSET_DEFAULT[nudge.field] || 0,
    nudge.field.startsWith("r") ? -180 : nudge.field.startsWith("s") ? -2 : -5,
    nudge.field.startsWith("r") ? 180 : nudge.field.startsWith("s") ? 2 : 5,
  );
  if (numberplateActorGhostOffsetHasValue(current)) {
    state.liveryPreviewActorGhostOffsets[key] = current;
  } else {
    delete state.liveryPreviewActorGhostOffsets[key];
  }
  renderNumberplateActorGhostOffsetSummary();
  await showNumberplateActorGhostOverlay();
  setLiveryPreviewNumberplateDevPanelVisible(true);
  $("liveryPreviewNumberplateInfo").textContent = [
    `Actor ghost tuned: ${placement.label || "-"} #${placement.index ?? index} ${actorGhostFieldLabel(nudge.field)}`,
    `Delta ${formatActorNudgeValue(nudge.delta)}. ${actorGhostOffsetSummaryText(current)}`,
    "Preview only. No actor or game files were written.",
  ].join("\n");
}

async function resetSelectedNumberplateActorGhost() {
  const { placement, index } = selectedNumberplatePlacement();
  const key = numberplateActorGhostOffsetKey(placement, index);
  if (key) delete state.liveryPreviewActorGhostOffsets[key];
  renderNumberplateActorGhostOffsetSummary();
  await showNumberplateActorGhostOverlay();
  setLiveryPreviewNumberplateDevPanelVisible(true);
}

async function resetAllNumberplateActorGhostOffsets() {
  state.liveryPreviewActorGhostOffsets = {};
  renderNumberplateActorGhostOffsetSummary();
  await showNumberplateActorGhostOverlay();
  setLiveryPreviewNumberplateDevPanelVisible(true);
}

function formatActorNudgeValue(value) {
  if (value == null || !Number.isFinite(Number(value))) return "null";
  return String(Math.round(Number(value) * 1000000) / 1000000);
}

function replaceNumberplatePlacementRows(rows = []) {
  if (!Array.isArray(rows) || !state.liveryPreviewNumberplate) return;
  state.liveryPreviewNumberplate.placements = rows;
  const select = $("liveryPreviewNumberplatePlacement");
  if (!select) return;
  const previousIndex = Number(select.value || 0);
  select.innerHTML = "";
  rows.forEach((placement, index) => {
    const option = document.createElement("option");
    option.value = String(index);
    option.textContent = `${placement.label || `Placement ${index}`} #${placement.index ?? index}`;
    select.appendChild(option);
  });
  if (rows.length) {
    select.value = String(Math.max(0, Math.min(previousIndex, rows.length - 1)));
    state.liveryPreviewNumberplateSelectedIndex = Number(select.value || 0);
  }
  renderSelectedNumberplateLabel();
}

function updateNumberplateResetStateFromResult(data = {}) {
  replaceNumberplatePlacementRows(data.placements || []);
  if (!state.liveryPreviewNumberplate) return;
  if (Array.isArray(data.allPlacements)) {
    state.liveryPreviewNumberplate.allPlacements = data.allPlacements;
  }
  if (data.numberplateOverlay) {
    state.liveryPreviewNumberplate.numberplateOverlay = data.numberplateOverlay;
  }
  state.liveryPreviewNumberplateTunerValues = {};
  state.liveryPreviewNumberplateGizmoEnabled = false;
  updateNumberplateGizmoUnsavedIndicator(window.LiveryLabPreview3d?.markNumberplateTunerSaved?.() || { dirty: false });
  updateNumberplateGizmoUnsavedIndicator(window.LiveryLabPreview3d?.setNumberplateTunerEnabled?.(false) || { dirty: false });
  window.LiveryLabPreview3d?.removeNumberplateOverlay?.();
}

async function nudgeSelectedNumberplateActor(sign = 1) {
  const skinKey = state.liveryPreviewSkinKey || "";
  const { placement, index } = selectedNumberplatePlacement();
  if (!skinKey || !placement || placement.index == null) {
    $("liveryPreviewNumberplateInfo").textContent = "No selected numberplate placement is available.";
    return;
  }
  const nudge = actorNudgeSelection(sign);
  $("liveryPreviewNumberplateInfo").textContent = `Nudging actor ${placement.label || "-"} #${placement.index ?? index}...`;
  log(`${t("log_running")} nudge_livery_numberplate_actor_placement...`);
  const result = await window.pywebview.api.nudge_livery_numberplate_actor_placement({
    skinKey,
    root: state.root,
    placement,
    vector: nudge.vector,
    axis: nudge.axis,
    delta: nudge.delta,
  });
  if (!result.ok) {
    $("liveryPreviewNumberplateInfo").textContent = `Actor nudge failed: ${result.message || ""}`;
    showStatusModal({
      type: "error",
      title: "Actor Nudge",
      subtitle: "Nudge failed",
      message: result.message || "Actor placement could not be nudged.",
    });
    return;
  }
  replaceNumberplatePlacementRows(result.data?.placements || []);
  if (state.liveryPreviewNumberplate && Array.isArray(result.data?.allPlacements)) {
    state.liveryPreviewNumberplate.allPlacements = result.data.allPlacements;
  }
  const beforeValue = actorNudgeValue(result.data?.before, nudge.vector, nudge.axis);
  const afterValue = actorNudgeValue(result.data?.after, nudge.vector, nudge.axis);
  const fieldLabel = actorNudgeFieldLabel(nudge.vector, nudge.axis);
  const summary = [
    `Actor updated: ${placement.label || "-"} #${placement.index ?? index} ${fieldLabel}`,
    `${formatActorNudgeValue(beforeValue)} -> ${formatActorNudgeValue(afterValue)} (delta ${formatActorNudgeValue(nudge.delta)})`,
    `Backup: ${result.data?.actorBackup || "-"}`,
    "Use Activate in game to publish the updated local skin to EVO.",
  ].join("\n");
  const diagnostics = $("liveryPreviewNumberplateDiagnostics");
  if (diagnostics) {
    diagnostics.hidden = false;
    diagnostics.textContent = JSON.stringify({
      before: result.data?.before || {},
      after: result.data?.after || {},
    }, null, 2);
  }
  if ($("liveryPreviewShowNumberplates")?.checked !== false) {
    await showNumberplateActorGhostOverlay();
    setLiveryPreviewNumberplateDevPanelVisible(true);
  }
  $("liveryPreviewNumberplateInfo").textContent = summary;
  await loadMySkins();
}

async function resetAllNumberplateActorBase() {
  const skinKey = state.liveryPreviewSkinKey || "";
  if (!skinKey) {
    $("liveryPreviewNumberplateInfo").textContent = "No selected livery is available.";
    return;
  }
  const result = await showConfirmModal({
    type: "warning",
    title: "Actor Restore",
    subtitle: "All numberplate placements",
    message: "Restore all custom numberplate actor placements from the source skin?",
    details: [
      `Skin: ${skinKey}`,
      "The source_skin visual selector is used to pick the correct original placements.",
      "Only this local LiveryLab skin actor is written.",
      "Saved preview overlay placements for restored actor indices are cleared.",
      "Nothing is written to the active EVO skin folder here.",
    ].join("\n"),
    confirmLabel: "Restore All",
    loadingLabel: "Restoring...",
    onConfirm: async () => {
      log(`${t("log_running")} reset_livery_numberplate_actor_placements_from_source...`);
      return window.pywebview.api.reset_livery_numberplate_actor_placements_from_source({
        skinKey,
        root: state.root,
      });
    },
  });
  if (!result) return;
  if (!result.ok) {
    $("liveryPreviewNumberplateInfo").textContent = `Actor restore failed: ${result.message || ""}`;
    showStatusModal({
      type: "error",
      title: "Actor Restore",
      subtitle: "Restore failed",
      message: result.message || "Actor placements could not be restored.",
    });
    return;
  }
  updateNumberplateResetStateFromResult(result.data || {});
  $("liveryPreviewNumberplateInfo").textContent = result.message || "Actor placements restored from source skin.";
  const diagnostics = $("liveryPreviewNumberplateDiagnostics");
  if (diagnostics) {
    diagnostics.hidden = false;
    diagnostics.textContent = JSON.stringify({
      restored: result.data?.restored || [],
      warnings: result.data?.warnings || [],
      sourceSelector: result.data?.sourceSelector || {},
      overlay: result.data?.overlay || {},
    }, null, 2);
  }
  await loadSelectedNumberplateTuner(true, true);
}

async function restoreSelectedNumberplateActorBase() {
  const skinKey = state.liveryPreviewSkinKey || "";
  const { placement, index } = selectedNumberplatePlacement();
  if (!skinKey || !placement || placement.index == null) {
    $("liveryPreviewNumberplateInfo").textContent = "No selected numberplate placement is available.";
    return;
  }
  const result = await showConfirmModal({
    type: "warning",
    title: "Actor Restore",
    subtitle: "Numberplate placement",
    message: "Restore this custom actor placement from its original base placement?",
    details: [
      `Skin: ${skinKey}`,
      `Placement: ${placement.label || "-"} (${placement.index ?? index})`,
      "A backup is written before the actor changes.",
      "Only this selected custom placement is restored.",
    ].join("\n"),
    confirmLabel: "Restore Base",
    loadingLabel: "Restoring...",
    onConfirm: async () => {
      log(`${t("log_running")} restore_livery_numberplate_actor_placement_from_base...`);
      return window.pywebview.api.restore_livery_numberplate_actor_placement_from_base({
        skinKey,
        root: state.root,
        placement,
      });
    },
  });
  if (!result) return;
  if (!result.ok) {
    $("liveryPreviewNumberplateInfo").textContent = `Actor restore failed: ${result.message || ""}`;
    showStatusModal({
      type: "error",
      title: "Actor Restore",
      subtitle: "Restore failed",
      message: result.message || "Actor placement could not be restored.",
    });
    return;
  }
  updateNumberplateResetStateFromResult(result.data || {});
  $("liveryPreviewNumberplateInfo").textContent = result.message || "Actor placement restored from base.";
  const diagnostics = $("liveryPreviewNumberplateDiagnostics");
  if (diagnostics) {
    diagnostics.hidden = false;
    diagnostics.textContent = JSON.stringify({
      restored: result.data?.restored || [],
      warnings: result.data?.warnings || [],
      sourceSelector: result.data?.sourceSelector || {},
      overlay: result.data?.overlay || {},
    }, null, 2);
  }
  await loadSelectedNumberplateTuner(true, true);
}

function actorPlacementApplySummary(data = {}, placement = {}, fallbackIndex = 0) {
  const delta = data.actorDelta || {};
  const fmt = (value) => formatActorNudgeValue(value);
  const deltaParts = [
    delta.position_x != null ? `position.x ${fmt(delta.position_x)}` : "",
    delta.position_y != null ? `position.y ${fmt(delta.position_y)}` : "",
    delta.position_z != null ? `position.z ${fmt(delta.position_z)}` : "",
    delta.rotation_x != null ? `rotation.x ${fmt(delta.rotation_x)}` : "",
    delta.rotation_y != null ? `rotation.y ${fmt(delta.rotation_y)}` : "",
    delta.rotation_z != null ? `rotation.z ${fmt(delta.rotation_z)}` : "",
    delta.scale_x != null ? `scale.x ${fmt(delta.scale_x)}` : "",
    delta.scale_z != null ? `scale.z ${fmt(delta.scale_z)}` : "",
  ].filter(Boolean);
  return [
    `Placement applied to local skin: ${placement.label || "-"} #${placement.index ?? fallbackIndex}`,
    `Actor deltas: ${deltaParts.join(", ") || "-"}`,
    data.actorChanged ? `Backup: ${data.actorBackup || "-"}` : "No actor write was needed.",
    "Use Activate in game to publish the updated local skin to EVO.",
  ].join("\n");
}

function numberplateOverlayKeyForPlacement(placement = {}, fallbackIndex = 0) {
  const label = String(placement?.label || "").trim();
  const actorIndex = placement?.index ?? fallbackIndex;
  return label && actorIndex !== "" && actorIndex != null ? `${label}:${actorIndex}` : "";
}

function savedNumberplateApplyItems() {
  const placements = state.liveryPreviewNumberplate?.placements || [];
  const overlay = numberplateCalibrationMap();
  const items = [];
  const seen = new Set();
  placements.forEach((placement, index) => {
    const surfaceKind = numberplateSurfaceKind(placement);
    if (!["door", "hood"].includes(surfaceKind)) return;
    const directKey = numberplateOverlayKeyForPlacement(placement, index);
    const savedEntry = overlay[directKey] || Object.values(overlay).find((entry) => (
      String(entry?.actorIndex ?? "") === String(placement?.index ?? "")
      && String(entry?.surface ?? "") === String(placement?.surface ?? placement?.outer_field_number ?? "")
    ));
    if (!savedEntry?.transform) return;
    if (numberplateTransformLooksLikeActorRaw(placement, savedEntry.transform)) return;
    const key = directKey || `${placement.label || surfaceKind}:${placement.index ?? index}`;
    if (seen.has(key)) return;
    seen.add(key);
    items.push({
      key,
      placement,
      index,
      surfaceKind,
      transform: normalizeVehicleSideNumberplateTransform(placement, savedEntry.transform),
    });
  });
  return items;
}

function actorPlacementApplyBatchSummary(data = {}) {
  const items = Array.isArray(data.items) ? data.items : [];
  const changed = items.filter((item) => item?.data?.actorChanged).length;
  const labels = items.map((item) => `${item.placement?.label || "-"} #${item.placement?.index ?? item.index}`).join(", ");
  return [
    `Placements applied to local skin: ${labels || "-"}`,
    `Actor writes: ${changed}/${items.length}`,
    "Use Activate in game to publish the updated local skin to EVO.",
  ].join("\n");
}

function setNumberplateApplyDiagnostics(payload = {}) {
  const text = JSON.stringify(payload, null, 2);
  const visible = $("liveryPreviewNumberplateApplyDiagnostics");
  if (visible) {
    visible.hidden = false;
    visible.textContent = text;
  }
  const wrap = $("liveryPreviewNumberplateApplyDiagnosticsWrap");
  if (wrap) wrap.open = true;
  const legacy = $("liveryPreviewNumberplateDiagnostics");
  if (legacy) {
    legacy.hidden = false;
    legacy.textContent = text;
  }
}

async function resolveUnsavedNumberplateSelectionChange(nextIndex = 0) {
  if (!state.liveryPreviewNumberplateUnsavedChanges) return true;
  const { placement, index } = selectedNumberplatePlacement();
  const choice = await showChoiceModal({
    type: "warning",
    title: "Unsaved preview changes",
    subtitle: "Numberplate placement",
    message: "Save the current preview placement before selecting another plate?",
    details: [
      `Current: ${selectedNumberplateLabel(placement, index)}`,
      "Save Preview Placement keeps the tuned preview values.",
      "Discard Changes restores the visible numberplates from the last saved preview data.",
    ].join("\n"),
    confirmLabel: "Save and Select",
    alternateLabel: "Discard Changes",
    cancelLabel: "Keep Editing",
  });
  if (choice === true) {
    return await saveSelectedNumberplatePlacement(false);
  }
  if (choice === "alternate") {
    state.liveryPreviewNumberplateUnsavedChanges = false;
    updateNumberplateGizmoUnsavedIndicator(window.LiveryLabPreview3d?.markNumberplateTunerSaved?.() || { dirty: false });
    clearNumberplateUndo();
    await showMappedNumberplates();
    return true;
  }
  const select = $("liveryPreviewNumberplatePlacement");
  if (select) select.value = String(state.liveryPreviewNumberplateSelectedIndex || 0);
  return false;
}

async function selectNumberplateByIndex(nextIndex = 0, options = {}) {
  const placements = state.liveryPreviewNumberplate?.placements || [];
  const normalizedIndex = Math.max(0, Math.min(Number(nextIndex) || 0, Math.max(0, placements.length - 1)));
  if (!placements.length) return false;
  if (normalizedIndex !== state.liveryPreviewNumberplateSelectedIndex) {
    const canSwitch = await resolveUnsavedNumberplateSelectionChange(normalizedIndex);
    if (!canSwitch) return false;
  }
  const select = $("liveryPreviewNumberplatePlacement");
  if (select) select.value = String(normalizedIndex);
  state.liveryPreviewNumberplateSelectedIndex = normalizedIndex;
  markNumberplateTunerReady("");
  updateNumberplateUndoButton();
  setLiveryPreviewNumberplatePanelVisible(options.openPanel !== false);
  await loadSelectedNumberplateTuner(options.showOverlay !== false);
  return true;
}

async function exportSelectedNumberplatePlacementToActor() {
  try {
    const skinKey = state.liveryPreviewSkinKey || "";
    const { placement, index } = selectedNumberplatePlacement();
    if (!skinKey || !placement || placement.index == null) {
      $("liveryPreviewNumberplateInfo").textContent = "No selected numberplate placement is available.";
      return;
    }
    if (state.liveryPreviewNumberplateUnsavedChanges) {
      const saved = await saveSelectedNumberplatePlacement(false, { quiet: true });
      if (!saved) return;
    } else {
      storeCurrentNumberplateTunerState();
    }
    const items = savedNumberplateApplyItems();
    if (!items.length) {
      $("liveryPreviewNumberplateInfo").textContent = "No saved hood or door preview placements are available. Save Preview Placement first.";
      return;
    }
    const itemsWithCalibration = [];
    for (const item of items) {
      const calibration = await numberplateApplyCalibrationForPlacement(item.placement, item.index);
      if (!calibration) continue;
      itemsWithCalibration.push({ ...item, calibration });
    }
    if (!itemsWithCalibration.length) {
      $("liveryPreviewNumberplateInfo").textContent = "No preview baseline is available for the saved placements.";
      return;
    }
    $("liveryPreviewNumberplateInfo").textContent = `Preparing placement apply: ${itemsWithCalibration.length} saved placement(s)`;
    const result = await showConfirmModal({
      type: "warning",
      title: "Apply Placements to Skin",
      subtitle: "Preview delta to local actor",
      message: "Apply all saved hood and door preview placements to this local skin actor?",
      details: [
        `Skin: ${skinKey}`,
        `Placements: ${itemsWithCalibration.map((item) => `${item.placement.label || "-"} #${item.placement.index ?? item.index}`).join(", ")}`,
        "Saved preview values are compared to their built-in calibration or car fit baseline.",
        "Hood and door placements are written from the skin-specific preview JSON, not from only the currently selected plate.",
        "A backup is written before actor changes.",
        "Nothing is written to the active EVO skin folder here.",
      ].join("\n"),
      confirmLabel: "Apply Placements",
      loadingLabel: "Applying...",
      onConfirm: async () => {
        $("liveryPreviewNumberplateInfo").textContent = `Applying ${itemsWithCalibration.length} placement(s) to local skin...`;
        const api = window.pywebview.api;
        const applyApi = api.apply_livery_numberplate_preview_delta_to_actor || api.apply_livery_numberplate_door_preview_delta_to_actor;
        log(`${t("log_running")} apply_livery_numberplate_preview_delta_to_actor...`);
        const applied = [];
        let last = null;
        for (const item of itemsWithCalibration) {
          const response = await applyApi.call(api, {
            skinKey,
            root: state.root,
            placement: item.placement,
            transform: item.transform,
            calibration: item.calibration,
          });
          if (!response?.ok) {
            throw new Error(response?.message || `Numberplate placement could not be applied: ${item.placement.label || item.key}`);
          }
          last = response;
          applied.push({ ...item, data: response.data || {} });
        }
        return {
          ok: true,
          data: {
            items: applied,
            placements: last?.data?.placements || [],
            allPlacements: last?.data?.allPlacements || [],
          },
        };
      },
    });
    if (!result) {
      $("liveryPreviewNumberplateInfo").textContent = "Apply placement cancelled.";
      return;
    }
    if (!result.ok) {
      $("liveryPreviewNumberplateInfo").textContent = `Apply placement failed: ${result.message || ""}`;
      showStatusModal({
        type: "error",
        title: "Apply Placement",
        subtitle: "Apply failed",
        message: result.message || "Numberplate placement could not be applied.",
      });
      return;
    }
    replaceNumberplatePlacementRows(result.data?.placements || []);
    if (Array.isArray(result.data?.allPlacements) && state.liveryPreviewNumberplate) {
      state.liveryPreviewNumberplate.allPlacements = result.data.allPlacements;
    }
    state.liveryPreviewNumberplateSavedNeedsApply = false;
    updateNumberplateGizmoUnsavedIndicator(window.LiveryLabPreview3d?.markNumberplateTunerSaved?.() || { dirty: false });
    renderNumberplateWorkflowStatus("applied");
    $("liveryPreviewNumberplateInfo").textContent = actorPlacementApplyBatchSummary(result.data || {});
    setNumberplateApplyDiagnostics({
        items: (result.data?.items || []).map((item) => ({
          key: item.key,
          placement: {
            label: item.placement?.label || "",
            actorIndex: item.placement?.index ?? null,
            surface: item.placement?.surface ?? item.placement?.outer_field_number ?? null,
            sourcePlacementIndex: item.placement?.sourcePlacementIndex ?? item.placement?.source_index ?? null,
          },
          savedTransform: item.transform || {},
          calibration: {
            generatedFromFit: item.calibration?.generatedFromFit === true,
            generatedFitSourcePlacementIndex: item.calibration?.generatedFitSourcePlacementIndex ?? null,
            sourcePlacementIndex: item.calibration?.sourcePlacementIndex ?? null,
            previewRotationKey: item.calibration?.previewRotationKey ?? "",
            baseRy: item.calibration?.ry ?? null,
            baseRx: item.calibration?.rx ?? null,
            baseRz: item.calibration?.rz ?? null,
          },
          backendCalibration: item.data?.calibrationDebug || {},
          previewDelta: item.data?.previewDelta || {},
          actorDelta: item.data?.actorDelta || {},
          actorTarget: item.data?.actorTarget || {},
          before: item.data?.before || {},
          base: item.data?.base || {},
          after: item.data?.after || {},
        })),
      });
    await loadMySkins();
  } catch (error) {
    $("liveryPreviewNumberplateInfo").textContent = `Apply placement failed: ${error?.message || error}`;
    showStatusModal({
      type: "error",
      title: "Apply Placement",
      subtitle: "Apply failed",
      message: error?.message || String(error || "Numberplate placement could not be applied."),
    });
  }
}

async function selectNumberplateFromViewer(detail = {}) {
  if ($("liveryPreviewWorkspace")?.hidden) return;
  if (!state.liveryPreviewNumberplate?.placements?.length) {
    await prepareNumberplateOverlayPanel({ openPanel: false, loadSelected: false, overwrite: false });
  }
  const placements = state.liveryPreviewNumberplate?.placements || [];
  if (!placements.length) return;
  let placementIndex = Number(detail.placementIndex);
  if (!Number.isInteger(placementIndex) || placementIndex < 0 || placementIndex >= placements.length) {
    placementIndex = placements.findIndex((placement) => String(placement.index ?? "") === String(detail.actorIndex ?? ""));
  }
  if (placementIndex < 0) {
    placementIndex = placements.findIndex((placement) => (
      String(placement.label || "") === String(detail.label || "")
      && String(placement.surface ?? placement.outer_field_number ?? "") === String(detail.surface ?? "")
    ));
  }
  if (placementIndex < 0) return;
  const devPanelOpen = $("liveryPreviewNumberplateDevPanel")?.hidden === false;
  await selectNumberplateByIndex(placementIndex, { openPanel: !devPanelOpen, showOverlay: true });
  if (devPanelOpen) setLiveryPreviewNumberplateDevPanelVisible(true);
}

function renderNumberplateOverlayPanel(data, options = {}) {
  if (!NUMBERPLATE_OVERLAY_WORKFLOW_ENABLED) return;
  state.liveryPreviewNumberplate = data || {};
  state.liveryPreviewNumberplateTunerValues = {};
  state.liveryPreviewNumberplateUndo = {};
  state.liveryPreviewNumberplateInputUndoArmed = false;
  state.liveryPreviewActorNudgeConfirmed = false;
  state.liveryPreviewNumberplateGizmoEnabled = false;
  state.liveryPreviewNumberplateUnsavedChanges = false;
  state.liveryPreviewNumberplateSavedNeedsApply = false;
  state.liveryPreviewNumberplateSelectedIndex = 0;
  state.liveryPreviewActorGhostOffsets = {};
  markNumberplateTunerReady("");
  window.LiveryLabPreview3d?.setNumberplateTunerEnabled?.(false);
  const panel = $("liveryPreviewNumberplatePanel");
  const select = $("liveryPreviewNumberplatePlacement");
  const info = $("liveryPreviewNumberplateInfo");
  const diagnostics = $("liveryPreviewNumberplateDiagnostics");
  if (!panel || !select) return;
  if (diagnostics) {
    diagnostics.hidden = true;
    diagnostics.textContent = "";
  }
  updateNumberplateGizmoUnsavedIndicator({ dirty: false });
  select.innerHTML = "";
  const placements = data?.placements || [];
  placements.forEach((placement, index) => {
    const option = document.createElement("option");
    option.value = String(index);
    option.textContent = `${placement.label || `Placement ${index}`} (${placement.index ?? index})`;
    select.append(option);
  });
  if (!placements.length) {
    const option = document.createElement("option");
    option.value = "";
    option.textContent = "No placements found";
    select.append(option);
  }
  renderSelectedNumberplateLabel();
  renderNumberplateWorkflowStatus();
  if (info) {
    const selected = placements[0] || {};
    const uniqueBackgrounds = new Set(placements.map((row) => row?.backgroundRel || "").filter(Boolean));
    info.textContent = [
      `Background: ${selected?.backgroundRel || data?.backgroundRel || "-"}`,
      `Status: ${selected?.backgroundStatus || data?.backgroundStatus || "-"}`,
      `Font: ${data?.fontRel || "-"}`,
      `Placements: ${data?.placementFilter?.selected ?? placements.length} of ${data?.placementFilter?.total ?? placements.length}`,
      `Placement backgrounds: ${uniqueBackgrounds.size || 0}`,
      `Selected actor placement: ${selected.label || "-"} (${selected.index ?? "-"})`,
    ].join("\n");
  }
  setLiveryPreviewNumberplatePanelVisible(options.openPanel !== false);
}

async function prepareNumberplateOverlayPanel(options = {}) {
  if (!NUMBERPLATE_OVERLAY_WORKFLOW_ENABLED) {
    setLiveryPreviewNumberplatesVisible(false);
    window.LiveryLabPreview3d?.removeNumberplateOverlay?.();
    $("liveryPreviewStatus").textContent = NUMBERPLATE_WORKFLOW_DISABLED_MESSAGE;
    return null;
  }
  const skinKey = state.liveryPreviewSkinKey || "";
  if (!skinKey) {
    $("liveryPreviewStatus").textContent = "No livery is attached to this preview workspace.";
    return;
  }
  if (liveryPreviewNumberplatesBlocked()) {
    const message = normalizeNumberplateSupport(state.liveryPreviewNumberplateSupport || state.liveryPreviewProject || {}).message
      || "This car does not support numberplates yet";
    $("liveryPreviewStatus").textContent = message;
    setLiveryPreviewNumberplatesVisible(false);
    return null;
  }
  if (liveryPreviewNumberplatesHidden()) {
    $("liveryPreviewStatus").textContent = "Numberplates are disabled for this skin.";
    setLiveryPreviewNumberplatesVisible(false);
    window.LiveryLabPreview3d?.removeNumberplateOverlay?.();
    return null;
  }
  if (options.removeOverlay !== false) {
    window.LiveryLabPreview3d?.removeNumberplateOverlay?.();
    setLiveryPreviewNumberplatesVisible(false);
  }
  $("liveryPreviewStatus").textContent = "Preparing numberplate overlay...";
  const result = await window.pywebview.api.prepare_livery_numberplate_overlay({
    skinKey,
    root: state.root,
    overwrite: Boolean(options.overwrite),
  });
  if (!result.ok) {
    $("liveryPreviewStatus").textContent = `Numberplate overlay failed:\n${result.message || ""}`;
    return;
  }
  renderNumberplateOverlayPanel(result.data || {}, { openPanel: options.openPanel !== false });
  if (options.loadSelected !== false) await loadSelectedNumberplateTuner(false);
  $("liveryPreviewStatus").textContent = result.message || "Numberplate overlay prepared.";
  return result.data || {};
}

async function toggleMappedNumberplates(visible) {
  if (!NUMBERPLATE_OVERLAY_WORKFLOW_ENABLED) {
    setLiveryPreviewNumberplatesVisible(false);
    window.LiveryLabPreview3d?.removeNumberplateOverlay?.();
    $("liveryPreviewStatus").textContent = NUMBERPLATE_WORKFLOW_DISABLED_MESSAGE;
    return;
  }
  if (visible) {
    if (liveryPreviewNumberplatesBlocked()) {
      const message = normalizeNumberplateSupport(state.liveryPreviewNumberplateSupport || state.liveryPreviewProject || {}).message
        || "This car does not support numberplates yet";
      setLiveryPreviewNumberplatesVisible(false);
      $("liveryPreviewStatus").textContent = message;
      return;
    }
    if (liveryPreviewNumberplatesHidden()) {
      setLiveryPreviewNumberplatesVisible(false);
      window.LiveryLabPreview3d?.removeNumberplateOverlay?.();
      $("liveryPreviewStatus").textContent = "Numberplates are disabled for this skin.";
      return;
    }
    setLiveryPreviewNumberplatesVisible(true);
    if (!state.liveryPreviewNumberplate?.placements?.length) {
      await prepareNumberplateOverlayPanel({ openPanel: false, loadSelected: false, overwrite: false });
    }
    await showMappedNumberplates();
    return;
  }
  setLiveryPreviewNumberplatesVisible(false);
  state.liveryPreviewNumberplateGizmoEnabled = false;
  updateNumberplateGizmoUnsavedIndicator(window.LiveryLabPreview3d?.setNumberplateTunerEnabled?.(false) || { dirty: false });
  window.LiveryLabPreview3d?.removeNumberplateOverlay?.();
  $("liveryPreviewNumberplateInfo").textContent = "Registered numberplates hidden.";
  $("liveryPreviewStatus").textContent = "Registered numberplates hidden.";
}

async function refreshOpenLiveryPreviewNumberplateAfterSkinUpdate(skinKey) {
  if (!NUMBERPLATE_OVERLAY_WORKFLOW_ENABLED) return false;
  if (!skinKey || $("liveryPreviewWorkspace")?.hidden) return false;
  if (String(state.liveryPreviewSkinKey || "").toLowerCase() !== String(skinKey || "").toLowerCase()) return false;
  if (liveryPreviewNumberplatesBlocked()) return false;
  if (liveryPreviewNumberplatesHidden()) return false;
  const overlayState = window.LiveryLabPreview3d?.hasNumberplateOverlay?.() || {};
  const panelWasOpen = $("liveryPreviewNumberplatePanel")?.hidden === false;
  const selectedPlacementValue = $("liveryPreviewNumberplatePlacement")?.value || "0";
  $("liveryPreviewStatus").textContent = "Numberplate changed in My Liveries. Refreshing 3D preview...";
  const result = await window.pywebview.api.prepare_livery_numberplate_overlay({
    skinKey,
    root: state.root,
    overwrite: false,
  });
  if (!result.ok) {
    $("liveryPreviewStatus").textContent = `Numberplate refresh failed:\n${result.message || ""}`;
    return false;
  }
  renderNumberplateOverlayPanel(result.data || {});
  const placementSelect = $("liveryPreviewNumberplatePlacement");
  if (placementSelect && Array.from(placementSelect.options).some((option) => option.value === selectedPlacementValue)) {
    placementSelect.value = selectedPlacementValue;
  }
  await loadSelectedNumberplateTuner(false);
  if (overlayState.visible) {
    if (overlayState.mode === "mapped-group") {
      await showMappedNumberplates();
    } else {
      await applyNumberplateOverlay(true);
    }
  }
  if (!panelWasOpen) setLiveryPreviewNumberplatePanelVisible(false);
  $("liveryPreviewStatus").textContent = overlayState.visible
    ? "Numberplate preview refreshed from My Liveries."
    : "Numberplate data refreshed. Apply the numberplate overlay to preview the changes.";
  return true;
}

async function hydrateLiveryProjectTrashOption(skinKey) {
  if (!skinKey || !window.pywebview?.api?.inspect_livery_project_for_skin) return null;
  const result = await window.pywebview.api.inspect_livery_project_for_skin({ skinKey });
  if (!result.ok) return null;
  const data = result.data || {};
  if (!data.exists) return data;
  const fileCount = Number(data.fileCount || 0);
  const workFileCount = Number(data.workFileCount || 0);
  data.summary = workFileCount
    ? `Project folder found (${fileCount} files, ${workFileCount} work file${workFileCount === 1 ? "" : "s"}).`
    : `Project folder found (${fileCount} files).`;
  return data;
}

async function startLiveryPreviewWatcher() {
  window.clearTimeout(state.liveryPreviewWatchTimer);
  const path = $("preview3dTexturePath")?.value.trim() || "";
  const watchPath = state.liveryPreviewProjectFolder || path;
  if (!watchPath) return false;
  setLiveryPreviewWatchBadge("Starting watcher", "loading");
  const result = await window.pywebview.api.start_livery_preview_watcher({
    path: watchPath,
    allowedNames: [
      "EXT_SKIN_C.png",
      "EXT_Skin_D.png",
      "EXT_Skin_D_2.png",
      "EXT_Skin_Drift_D.png",
      "EXT_Skin_Drift_AO.png",
      "ext_skin_ao.png",
      "EXT_Skin_M.png",
      "EXT_Skin_R.png",
      "EXT_Skin_CC.png",
      "EXT_Skin_CR.png",
      "EXT_Skin_S.png",
      "EXT_Banner_C.png",
    ],
  });
  if (!result.ok) {
    setLiveryPreviewWatchBadge("Watcher failed", "error");
    $("liveryPreviewStatus").textContent = `Watcher failed:\n${result.message || ""}`;
    return false;
  }
  state.liveryPreviewLastChangeId = Number(result.data?.change_id || 0);
  setLiveryPreviewWatchBadge("Watching livery PNGs", "watching");
  scheduleLiveryPreviewWatcherPoll();
  return true;
}

async function stopLiveryPreviewWatcher() {
  window.clearTimeout(state.liveryPreviewWatchTimer);
  state.liveryPreviewWatchTimer = 0;
  if (window.pywebview?.api?.stop_livery_preview_watcher) {
    await window.pywebview.api.stop_livery_preview_watcher();
  }
}

function scheduleLiveryPreviewWatcherPoll() {
  window.clearTimeout(state.liveryPreviewWatchTimer);
  if ($("liveryPreviewWorkspace")?.hidden) return;
  state.liveryPreviewWatchTimer = window.setTimeout(() => pollLiveryPreviewWatcher(), 600);
}

async function pollLiveryPreviewWatcher() {
  try {
    const result = await window.pywebview.api.get_livery_preview_watcher_status();
    if (!result.ok) {
      setLiveryPreviewWatchBadge("Watcher error", "error");
      $("liveryPreviewStatus").textContent = `Watcher status failed:\n${result.message || ""}`;
      scheduleLiveryPreviewWatcherPoll();
      return;
    }
    const status = result.data || {};
    const changeId = Number(status.change_id || 0);
    if (changeId && changeId !== state.liveryPreviewLastChangeId) {
      state.liveryPreviewLastChangeId = changeId;
      setLiveryPreviewWatchBadge("Reloading changed PNGs", "reloading");
      $("liveryPreviewStatus").textContent = "Detected stable PNG update. Reloading texture...";
      const { viewer, materialMode } = await reloadLiveryPreviewViewerTextureWithMaterialMode();
      setLiveryPreviewWatchBadge(`Reloaded ${new Date().toLocaleTimeString()}`, "watching");
      $("liveryPreviewStatus").textContent = [
        "Live texture reloaded",
        `Change: ${changeId}`,
        `Preview mode: ${liveryPreviewModeLabel()}`,
        `Texture: ${(status.last_paths || [status.path]).filter(Boolean).join(", ") || "-"}`,
        `Material: ${viewer.materialName || "EXT_SKIN"}`,
        ...liveryPreviewAoStatusLines(viewer),
        `Textured materials: ${viewer.appliedMaterials}`,
        ...(materialMode ? liveryPreviewMaterialModeLegend(materialMode) : []),
        "Return to My Liveries to update the game files.",
      ].join("\n");
      await markLiveryPreviewGameUpdateRequired("LiveryProjects texture changed", "preview3d_texture");
    } else if (status.last_error) {
      setLiveryPreviewWatchBadge("Watcher warning", "error");
      $("liveryPreviewStatus").textContent = `Watcher warning:\n${status.last_error}`;
    } else if (status.running) {
      setLiveryPreviewWatchBadge("Watching livery PNGs", "watching");
    }
  } catch (error) {
    setLiveryPreviewWatchBadge("Watcher error", "error");
    $("liveryPreviewStatus").textContent = `Watcher error:\n${error?.message || error}`;
  }
  scheduleLiveryPreviewWatcherPoll();
}

async function reloadPreview3dTexture(options = {}) {
  const reloadViewer = options.reloadViewer !== false;
  const workspaceOpen = $("liveryPreviewWorkspace") && !$("liveryPreviewWorkspace").hidden;
  const payload = workspaceOpen ? payloadPreview3dProject() : null;
  const path = payload?.texturePath || $("preview3dTexturePath")?.value.trim() || "";
  const image = $("preview3dTextureImage");
  const empty = $("preview3dTextureEmpty");
  if (!path || !image) {
    if (empty) empty.style.display = "";
    return false;
  }
  const result = await window.pywebview.api.load_image_preview(path);
  if (!showResult(result, "Texture loaded", "load_image_preview")) return false;
  image.src = result.data.data_url;
  image.style.display = "block";
  if (empty) empty.style.display = "none";
  if (reloadViewer && window.LiveryLabPreview3d?.reloadTexture && $("preview3dModelPath")?.value.trim()) {
    try {
      await reloadLiveryPreviewViewerTextureWithMaterialMode();
    } catch (error) {
      log(error?.message || String(error), true);
    }
  }
  return true;
}

async function inspectPreview3dProject(showMessages = true) {
  if (!showMessages && liveryPreviewWorkspaceActive()) return null;
  const result = await window.pywebview.api.inspect_preview3d_project(payloadPreview3dProject());
  if (showMessages && !showResult(result, "3D Preview inspected", "inspect_preview3d_project")) return null;
  if (!result.ok) {
    renderPreview3dStatus({ warnings: [result.message || "Inspect failed"] });
    return null;
  }
  const data = result.data || {};
  renderPreview3dStatus(data);
  if (data.texture?.mtime_ms && state.preview3dLastMtimeMs !== data.texture.mtime_ms) {
    state.preview3dLastMtimeMs = data.texture.mtime_ms;
    await reloadPreview3dTexture();
  }
  schedulePreview3dWatcher();
  return data;
}

function schedulePreview3dWatcher() {
  window.clearTimeout(state.preview3dTimer);
  if (liveryPreviewWorkspaceActive()) return;
  const project = payloadPreview3dProject();
  if (!project.autoRefresh || !project.texturePath) return;
  state.preview3dTimer = window.setTimeout(() => {
    inspectPreview3dProject(false);
  }, project.watchIntervalMs || 1000);
}

async function loadPreview3dProject() {
  if (!window.pywebview?.api?.load_preview3d_project) return;
  if (liveryPreviewWorkspaceActive()) return;
  const result = await window.pywebview.api.load_preview3d_project();
  if (!result.ok) return;
  applyPreview3dProject(result.data);
  renderPreview3dStatus(result.data);
  if (result.data?.texture?.exists) {
    state.preview3dLastMtimeMs = result.data.texture.mtime_ms || null;
    await reloadPreview3dTexture({ reloadViewer: false });
  }
  schedulePreview3dWatcher();
}

async function savePreview3dProject() {
  const result = await call("save_preview3d_project", payloadPreview3dProject());
  if (!result.ok) return;
  const data = result.data?.project || null;
  if (data) {
    applyPreview3dProject(data);
    renderPreview3dStatus(data);
  }
  await reloadPreview3dTexture();
  await loadPreview3dViewer();
  schedulePreview3dWatcher();
}

async function applyPathDefaults(target, path) {
  if (target === "texturePath") {
    setDerivedPath("mipsPath", path, ".texturemips", true);
    await setConvertedDerivedPath("ddsOut", "textures", path, ".dds", true);
    await setConvertedDerivedPath("pngOut", "textures", path, ".png", true);
  } else if (target === "mipsPath") {
    setDerivedPath("texturePath", path, ".texture", false);
    await setConvertedDerivedPath("ddsOut", "textures", path, ".dds", true);
    await setConvertedDerivedPath("pngOut", "textures", path, ".png", true);
  } else if (target === "meshPath") {
    await setConvertedDerivedPath("meshObjOut", "meshes", path, ".obj", true);
  } else if (target === "rimTargetPath") {
    setDerivedPath("rimOutPath", path, "_swap_test.rimmesh", true);
  } else if (target === "uvMeshPath") {
    await setConvertedDerivedPath("uvExportPath", "uv", path, ".png", true);
  } else if (target === "packagePath") {
    const root = normalizeRootPath($("rootPath").value || state.root || "");
    if (root) setPath("packageOutDir", root);
    else setDerivedPath("packageOutDir", path, "", true);
  } else if (target === "homePackagePath") {
    const root = normalizeRootPath($("homeRootPath").value || $("rootPath").value || state.root || "");
    if (root) setPath("homePackageOutDir", root);
    else setDerivedPath("homePackageOutDir", path, "", true);
  } else if (target === "backTexture") {
    const source = $("pngIn")?.value.trim() || $("ddsIn")?.value.trim() || path;
    await setConvertedDerivedPath("mipsOut", "textures", path, ".texturemips", true, source);
  } else if (target === "ddsIn") {
    const carSource = $("backTexture")?.value.trim() || path;
    await setConvertedDerivedPath("mipsOut", "textures", carSource, ".texturemips", true, path);
  } else if (target === "pngIn") {
    const carSource = $("backTexture")?.value.trim() || path;
    await setConvertedDerivedPath("mipsOut", "textures", carSource, ".texturemips", true, path);
  } else if (target === "materialPath") {
    await setMaterialOutputDefaults(path, true);
  } else if (target === "skinCardesign") {
    await autofillSkinTemplateFields(false);
  }
  if (target === "homePackagePath" || target === "homePackageOutDir") {
    syncHomePackageStatus();
  }
  renderWorkflowCompatibility();
}

function syncRootFields(root) {
  const normalized = normalizeRootPath(root);
  const previousRoot = state.root;
  const rootChanged = previousRoot && normalized !== previousRoot;
  if ($("rootPath")) $("rootPath").value = normalized;
  if ($("homeRootPath")) $("homeRootPath").value = normalized;
  state.root = normalized;
  if (rootChanged) {
    state.officialRuntimePreparedRoot = "";
    state.officialRuntimeSafetyStatus = null;
    state.carCompatibility = null;
    state.carCompatibilityCacheStatus = null;
    state.carCompatibilityProgress = null;
    renderCarCompatibility();
  }
  syncHomePackageDefaultsForRoot(normalized, previousRoot);
  syncHomePackageStatus();
  scheduleRootStatusCheck(normalized);
  scheduleStorageHealthCheck(normalized);
  updateHomeRootSavePulse();
}

async function saveDefaultRoot(root) {
  const normalized = normalizeRootPath(root);
  syncRootFields(normalized);
  const result = await window.pywebview.api.save_default_root(normalized);
  showResult(result, t("save_default_root"), "save_default_root");
  if (result?.ok) {
    state.savedRoot = normalized;
    updateHomeRootSavePulse();
    applySetupGateStatus();
    await refreshRuntimeProfile({ silent: true, root: normalized });
    if (currentWorkflowScope() === "advanced") {
      await loadCachedCarCompatibility();
    } else if (currentWorkflowScope() === "official") {
      await refreshOfficialCarResourcesStatus({ silent: true, root: normalized });
      await refreshOfficialExternalLiveries({ silent: true });
    }
  }
  return result;
}

function updateHomeRootSavePulse() {
  const input = $("homeRootPath");
  const button = $("saveHomeRoot");
  if (!input || !button) return;
  const typedRoot = normalizeRootPath(input.value);
  const savedRoot = normalizeRootPath(state.savedRoot);
  button.classList.toggle("home-root-save-pulse", Boolean(typedRoot && typedRoot !== savedRoot));
}

function updateSetupActionPulses(gate = state.setupGate || evaluateSetupGate()) {
  const scope = currentWorkflowScope();
  const readyByStep = Object.fromEntries((gate.steps || []).map((step) => [step.key, Boolean(step.ready)]));
  const longPathsReady = Boolean(readyByStep["long-paths"]);
  const rootReady = Boolean(readyByStep.root);
  const contentReady = Boolean(readyByStep.content);
  const storageReady = Boolean(readyByStep.storage);
  const carsReady = Boolean(readyByStep.cars);
  const officialExtractReady = Boolean(readyByStep["official-extract"]);
  const packPath = $("homePackagePath")?.value.trim() || "";
  const outDir = $("homePackageOutDir")?.value.trim() || $("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || "";

  $("extractHomePackage")?.classList.toggle(
    "setup-action-pulse",
    Boolean(longPathsReady && scope === "advanced" && rootReady && !contentReady && packPath && outDir && !state.packageExtractModalOpen)
  );
  $("runHomeStorageHealth")?.classList.toggle(
    "setup-action-pulse",
    Boolean(longPathsReady && scope === "advanced" && rootReady && contentReady && !storageReady && !state.storageHealthLoading)
  );
  $("homeAnalyzeCars")?.classList.toggle(
    "setup-action-pulse",
    Boolean(longPathsReady && scope === "advanced" && rootReady && contentReady && storageReady && !carsReady && !state.carCompatibilityLoading)
  );
  $("officialExtractCarResources")?.classList.toggle(
    "setup-action-pulse",
    Boolean(longPathsReady && scope === "official" && rootReady && setupGateOfficialRuntimeReady() && !officialExtractReady && !state.officialCarResourcesLoading && !state.packageExtractModalOpen)
  );
  $("prepareOfficialRuntime")?.classList.toggle(
    "setup-action-pulse",
    Boolean(longPathsReady && scope === "official" && rootReady && !setupGateOfficialRuntimeReady() && !state.runtimeSwitchBusy)
  );
}

function updateWorkflowSetupCopy() {
  const scope = currentWorkflowScope();
  if ($("homeStorageStepNumber")) $("homeStorageStepNumber").textContent = "03";
  const setupIntro = $("homeSetupIntro");
  const rootTitle = $("homeRootStepTitle");
  const rootFieldLabel = $("homeRootFieldLabel");
  const rootDescription = $("homeRootStepDescription");
  const storageDescription = $("homeStorageStepDescription");
  if (scope === "official") {
    if (setupIntro) {
      setupIntro.textContent = "Complete the Windows requirement and these three setup steps before creating, importing or activating Official External Liveries. A full game-content extraction is not required.";
    }
    if (rootTitle) rootTitle.textContent = "AC EVO Game Folder";
    if (rootFieldLabel) rootFieldLabel.textContent = "AC EVO Game Folder";
    if (rootDescription) {
      rootDescription.textContent = "Select the AC EVO installation folder that contains the AC EVO content package. LiveryLab also recognizes a valid renamed content.kspkg and keeps generated files in LiveryLab-managed storage.";
    }
    if (storageDescription) {
      storageDescription.textContent = "Verify LiveryLab-managed folders, local official caches and texture conversion tools.";
    }
  } else {
    if (setupIntro) {
      setupIntro.textContent = "Complete these setup steps before creating or activating liveries.";
    }
    if (rootTitle) rootTitle.textContent = "AC EVO Game Root";
    if (rootFieldLabel) rootFieldLabel.textContent = "AC EVO Game Root";
    if (rootDescription) {
      rootDescription.textContent = "Choose the AC EVO root folder LiveryLab should read from and write to.";
    }
    if (storageDescription) {
      storageDescription.textContent = "Verify app folders, game write access and texture conversion tools.";
    }
  }
}

function officialCarResourcesReady(data = state.officialCarResourcesStatus) {
  return Boolean(data?.ready || data?.derivedReady);
}

function officialRawCacheAvailable(data = state.officialCarResourcesStatus) {
  return Boolean(data?.rawReady || (data?.manifestExists && data?.rawContentCarsExists && Number(data?.rawFileCount || 0) > 0));
}

function renderOfficialCarResourcesStatus(data = state.officialCarResourcesStatus) {
  updateWorkflowSetupCopy();
  const root = normalizeRootPath($("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || state.root || "");
  const savedRoot = normalizeRootPath(state.savedRoot || "");
  const rootReady = Boolean(root && savedRoot && root === savedRoot && state.rootStatus?.exists && setupGateOfficialRuntimeReady());
  const button = $("officialExtractCarResources");
  if (button) {
    button.disabled = Boolean(!rootReady || state.officialCarResourcesLoading || state.packageExtractModalOpen);
    button.textContent = state.officialCarResourcesLoading ? "Preparing..." : "Prepare Preview Resources";
  }
  const target = $("officialExtractStatus");
  if (!target) return;
  let className = "root-status is-warning";
  let text = "Select and save the AC EVO Game Folder first.";
  if (!rootReady) {
    text = root ? "Save and verify the AC EVO Game Folder before preparing preview resources." : "Select and save the AC EVO Game Folder first.";
  } else if (state.officialCarResourcesLoading) {
    text = "Reading required car resources and preparing local previews...";
  } else if (!data) {
    className = "root-status is-unknown";
    text = "Official preview resources have not been prepared yet.";
  } else if (data.packageError) {
    className = "root-status is-error";
    text = data.packageError;
  } else if (data.sourcePackageExists === false) {
    className = "root-status is-error";
    text = "No valid AC EVO content package was found in the selected AC EVO Game Folder.";
  } else if (data.manifestError) {
    className = "root-status is-error";
    text = `Official car resource manifest is unreadable: ${data.manifestError}`;
  } else if (data.refreshRequired) {
    const reason = String(data.refreshReason || "").trim();
    text = reason === "preview asset installation path changed"
      ? "The LiveryLab installation folder changed. Prepare the Official preview resources again."
      : "Official preview resources must be prepared again for this LiveryLab version.";
  } else if (officialCarResourcesReady(data)) {
    className = "root-status is-ok";
    const expected = Number(data?.manifest?.derivedPreparation?.carCount || data?.manifest?.carCount || data?.derivedCarCount || 0);
    const readyCount = Number(data?.derivedReadyCarCount || data?.derivedCarCount || 0);
    const countText = expected ? `${readyCount.toLocaleString()}/${expected.toLocaleString()} cars` : `${readyCount.toLocaleString()} cars`;
    text = `Official preview resources ready (${countText}).`;
  } else if (officialRawCacheAvailable(data)) {
    text = "Required source files are cached locally. Run Prepare Preview Resources to complete setup.";
  } else {
    text = "Official preview resources have not been prepared yet.";
  }
  target.className = className;
  target.textContent = text;
  target.title = data?.manifestPath || data?.rawContentCarsRoot || "";
  renderOfficialCreatorCars(data);
}

function advancedCreatorCars(data = state.carCompatibility) {
  const rows = Array.isArray(data?.advancedCreatorCars) ? data.advancedCreatorCars : [];
  const seen = new Set();
  return rows
    .map((row) => {
      const base = row && typeof row === "object" ? row : {};
      const templates = advancedCreatorTemplatesForCar(base);
      const readyTemplates = templates.filter((template) => template.ready);
      const selected = readyTemplates.find((template) => String(template.id || "") === String(base.selectedTemplateId || ""))
        || readyTemplates.find((template) => template.thumbnailExists)
        || readyTemplates[0]
        || templates.find((template) => template.thumbnailExists)
        || templates[0]
        || null;
      return {
        ...base,
        templates,
        ready: Boolean(readyTemplates.length),
        templateCount: templates.length,
        readyTemplateCount: readyTemplates.length,
        unsupportedTemplateCount: Math.max(0, templates.length - readyTemplates.length),
        thumbnailPath: selected?.thumbnailPath || "",
        thumbnailExists: Boolean(selected?.thumbnailExists),
        selectedTemplateId: selected?.id || "",
      };
    })
    .filter((row) => {
      const carId = String(row.carId || "").trim();
      if (!carId || !Array.isArray(row.templates) || !row.templates.length || seen.has(carId.toLowerCase())) return false;
      seen.add(carId.toLowerCase());
      return true;
    })
    .sort((a, b) => advancedCreatorCarLabel(a).localeCompare(advancedCreatorCarLabel(b)) || String(a.carId || "").localeCompare(String(b.carId || "")));
}

function advancedCreatorFavoriteCarIdSet() {
  return new Set((Array.isArray(state.advancedCreatorFavoriteCarIds) ? state.advancedCreatorFavoriteCarIds : [])
    .map((carId) => String(carId || "").trim().toLowerCase())
    .filter(Boolean));
}

function advancedCreatorCarIsFavorite(car = {}) {
  return advancedCreatorFavoriteCarIdSet().has(String(car.carId || "").trim().toLowerCase());
}

function advancedCreatorCarLabel(car = {}) {
  const explicit = String(car.displayName || "").trim();
  if (explicit) return explicit;
  const carId = String(car.carId || "").trim();
  const withoutPrefix = carId.replace(/^ks_/, "");
  return withoutPrefix
    .split("_")
    .filter(Boolean)
    .map((part) => (part.length <= 3 && /[a-z]/i.test(part) ? part.toUpperCase() : part.slice(0, 1).toUpperCase() + part.slice(1)))
    .join(" ") || carId || "Unknown car";
}

function advancedCreatorCarType(car = {}) {
  const explicit = String(car.carType || car.type || "").trim().toLowerCase();
  if (explicit === "race" || explicit === "race_car" || explicit === "race-car" || explicit === "racing") return "race";
  if (explicit === "road" || explicit === "road_car" || explicit === "road-car" || explicit === "street") return "road";
  const haystack = [
    car.carId,
    car.displayName,
    ...(Array.isArray(car.templates) ? car.templates.flatMap((template) => [
      template?.label,
      template?.visualPreset,
      template?.mechanicalPreset,
    ]) : []),
  ].map((value) => String(value || "").toLowerCase()).join(" ");
  if (/\b(gt2|gt3|gt4|gte|lmp|lmh|challenge|competizione|dtm|formula|f1|indy|lms|touring|race|racing)\b/.test(haystack)) {
    return "race";
  }
  return "road";
}

function advancedCreatorCarTypeLabel(type = "") {
  return advancedCreatorCarType({ carType: type }) === "race" ? "Race cars" : "Road cars";
}

function advancedCreatorCarBrand(car = {}) {
  return officialCreatorCarBrand(car);
}

function advancedCreatorCarMatchesSearch(car = {}, query = "") {
  const q = String(query || "").trim().toLowerCase();
  if (!q) return true;
  return [
    car.carId,
    car.displayName,
    advancedCreatorCarLabel(car),
    advancedCreatorCarBrand(car),
    advancedCreatorCarTypeLabel(advancedCreatorCarType(car)),
    ...(Array.isArray(car.templates) ? car.templates.flatMap((template) => [
      template?.label,
      template?.cardesign,
      template?.visualPreset,
      template?.mechanicalPreset,
    ]) : []),
  ].some((value) => String(value || "").toLowerCase().includes(q));
}

function advancedCreatorStatusPillsHtml(car = {}) {
  const type = advancedCreatorCarType(car);
  const readyCount = Number(car.readyTemplateCount || 0);
  const totalCount = Number(car.templateCount || 0);
  const pills = [
    { label: advancedCreatorCarTypeLabel(type).replace(" cars", ""), className: type === "race" ? "is-race" : "is-road" },
    { label: car.ready ? "Ready" : "Missing", className: car.ready ? "is-ready" : "is-warning" },
  ];
  if (totalCount > 0) pills.push({ label: `${readyCount}/${totalCount} templates`, className: readyCount ? "is-variants" : "is-warning" });
  if (!car.thumbnailExists) pills.push({ label: "No thumb", className: "is-warning" });
  return `<div class="advanced-car-thumb-pills">${pills.map((pill) => `<span class="advanced-car-thumb-pill ${pill.className}">${escapeHtml(pill.label)}</span>`).join("")}</div>`;
}

function advancedCreatorCardHtml(car = {}, selectedId = "") {
  const carId = String(car.carId || "");
  const thumbnailPath = car.thumbnailExists ? String(car.thumbnailPath || "") : "";
  const active = carId === selectedId;
  const favorite = advancedCreatorCarIsFavorite(car);
  return `
    <div class="advanced-car-card${active ? " active" : ""}" role="button" tabindex="0" aria-disabled="false" data-advanced-creator-car-id="${escapeHtml(carId)}">
      <button class="advanced-car-favorite-toggle ${favorite ? "is-active" : ""}" type="button" aria-label="${favorite ? "Remove favorite" : "Add favorite"}" data-advanced-creator-favorite-car-id="${escapeHtml(carId)}">${favorite ? "&#9733;" : "&#9734;"}</button>
      <div class="advanced-car-thumb-frame">
        <img class="advanced-car-image" alt="" src="${mySkinPlaceholderSrc()}" data-advanced-car-thumb-path="${escapeHtml(thumbnailPath)}" />
        ${advancedCreatorStatusPillsHtml(car)}
      </div>
      <div class="advanced-car-info">
        <strong>${escapeHtml(advancedCreatorCarLabel(car))}</strong>
        <small>${escapeHtml(carId)}</small>
      </div>
    </div>
  `;
}

function advancedCreatorCarGroups(cars = []) {
  const sortCars = (rows) => [...rows]
    .sort((a, b) => advancedCreatorCarLabel(a).localeCompare(advancedCreatorCarLabel(b)) || String(a.carId || "").localeCompare(String(b.carId || "")));
  const groups = [];
  const favoriteRows = sortCars(cars.filter((car) => advancedCreatorCarIsFavorite(car)));
  if (favoriteRows.length) groups.push({ id: "favorites", title: "Favorites", rows: favoriteRows });
  if (state.advancedCreatorSortMode === "carType") {
    const raceRows = sortCars(cars.filter((car) => advancedCreatorCarType(car) === "race"));
    const roadRows = sortCars(cars.filter((car) => advancedCreatorCarType(car) !== "race"));
    if (raceRows.length) groups.push({ id: "race", title: "Race cars", rows: raceRows });
    if (roadRows.length) groups.push({ id: "road", title: "Road cars", rows: roadRows });
  } else if (state.advancedCreatorSortMode === "carBrand") {
    const brandRows = new Map();
    cars.forEach((car) => {
      const brand = advancedCreatorCarBrand(car);
      if (!brandRows.has(brand)) brandRows.set(brand, []);
      brandRows.get(brand).push(car);
    });
    [...brandRows.entries()]
      .sort(([left], [right]) => left.localeCompare(right))
      .forEach(([brand, rows]) => {
        groups.push({
          id: `brand_${brand.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "")}`,
          title: brand,
          rows: sortCars(rows),
        });
      });
  } else {
    groups.push({ id: "all", title: "All cars", rows: sortCars(cars) });
  }
  return groups;
}

function renderAdvancedCreatorSortControls() {
  document.querySelectorAll("[data-advanced-creator-sort]").forEach((button) => {
    const active = String(button.dataset.advancedCreatorSort || "") === state.advancedCreatorSortMode;
    button.classList.toggle("active", active);
    button.setAttribute("aria-pressed", active ? "true" : "false");
  });
}

function templatePreviewWorkflowKey(workflow = "") {
  const clean = String(workflow || "").trim().toLowerCase();
  return clean === "official" ? "official" : "advanced";
}

function templatePreviewStatusKey(workflow = "", carId = "") {
  return `${templatePreviewWorkflowKey(workflow)}:${String(carId || "").trim().toLowerCase()}`;
}

function templatePreviewStatusElement(workflow = "") {
  return $(templatePreviewWorkflowKey(workflow) === "official" ? "officialTemplatePreviewStatus" : "advancedTemplatePreviewStatus");
}

function setTemplatePreviewStatusVisible(workflow = "", visible = false) {
  const el = templatePreviewStatusElement(workflow);
  if (!el) return;
  el.hidden = !visible;
}

function templatePreviewSourceLabel(source = "") {
  const clean = String(source || "").trim();
  if (clean === "official_preview_assets") return "Official preview assets";
  if (clean === "local_preview_assets") return "Advanced preview assets";
  if (clean === "game_root") return "Game content";
  return clean || "No source";
}

function fallbackTemplatePreviewStatus(workflow = "", car = null) {
  const key = templatePreviewWorkflowKey(workflow);
  if (!car) {
    return {
      ready: false,
      idle: true,
      title: "Select a car",
      message: `Preview readiness will be checked for the ${key === "official" ? "Official" : "Advanced"} workflow.`,
    };
  }
  if (key === "official") {
    const ready = Boolean(car.ready && car.glbExists !== false);
    return {
      ready,
      fallback: true,
      source: ready ? "official_preview_assets" : "",
      model: { path: String(car.glbPath || "") },
      warnings: ready
        ? ["Using existing official car resource status until the template preview API is available."]
        : ["Official preview assets are not ready for this car."],
    };
  }
  return {
    ready: false,
    apiUnavailable: true,
    warnings: ["Template preview API is not available in this runtime. Rebuild required for Advanced 3D status."],
  };
}

function renderTemplatePreviewStatus(workflow = "", status = null, car = null) {
  const el = templatePreviewStatusElement(workflow);
  if (!el) return;
  if (el.hidden) return;
  const fallback = fallbackTemplatePreviewStatus(workflow, car);
  const data = status && typeof status === "object" ? status : fallback;
  const warnings = Array.isArray(data.warnings) ? data.warnings.filter(Boolean) : [];
  const source = templatePreviewSourceLabel(data.source || data.prepareSource || "");
  const modelPath = String(data.model?.path || "").trim();
  let mode = "is-warning";
  let title = "3D Preview Not Ready";
  let message = warnings[0] || "No converted GLB is ready for this workflow.";
  if (data.loading) {
    mode = "is-loading";
    title = "Checking 3D Preview";
    message = "Reading preview asset status...";
  } else if (data.idle) {
    mode = "is-idle";
    title = data.title || "Select a car";
    message = data.message || "Preview readiness will be checked after selecting a car.";
  } else if (data.ready) {
    mode = "is-ready";
    title = "3D Preview Ready";
    message = modelPath ? `${source}: ${modelPath}` : `${source} are ready.`;
  } else if (data.apiUnavailable) {
    mode = "is-warning";
    title = "Rebuild Required";
    message = warnings[0] || "This runtime does not expose the preview status API yet.";
  } else if (data.canPrepare) {
    mode = "is-warning";
    title = "Needs Preparation";
    message = "No workflow-specific GLB is ready yet. Game content is available for preparation.";
  }
  el.className = `creator-preview-asset-status ${mode}`;
  el.innerHTML = `
    <span>3D Preview Assets</span>
    <strong>${escapeHtml(title)}</strong>
    <small title="${escapeHtml(message)}">${escapeHtml(message)}</small>
  `;
}

async function refreshTemplatePreviewStatus(workflow = "", carId = "", car = null) {
  const key = templatePreviewStatusKey(workflow, carId);
  if (!String(carId || "").trim()) {
    renderTemplatePreviewStatus(workflow, null, null);
    return null;
  }
  if (typeof window.pywebview?.api?.inspect_livery_template_preview_assets !== "function") {
    const fallback = fallbackTemplatePreviewStatus(workflow, car);
    state.liveryTemplatePreviewStatuses[key] = fallback;
    renderTemplatePreviewStatus(workflow, fallback, car);
    return fallback;
  }
  const requestId = (state.liveryTemplatePreviewRequestIds[key] || 0) + 1;
  state.liveryTemplatePreviewRequestIds[key] = requestId;
  state.liveryTemplatePreviewStatuses[key] = { loading: true };
  renderTemplatePreviewStatus(workflow, state.liveryTemplatePreviewStatuses[key], car);
  let status = null;
  try {
    const result = await window.pywebview.api.inspect_livery_template_preview_assets({
      carId,
      workflow: templatePreviewWorkflowKey(workflow),
      root: normalizeRootPath($("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || state.root || ""),
    });
    status = result?.ok ? (result.data || {}) : {
      ready: false,
      warnings: [result?.message || "Template preview status could not be checked."],
    };
  } catch (error) {
    status = {
      ready: false,
      warnings: [error?.message || String(error) || "Template preview status check failed."],
    };
  }
  if (state.liveryTemplatePreviewRequestIds[key] !== requestId) return state.liveryTemplatePreviewStatuses[key] || null;
  state.liveryTemplatePreviewStatuses[key] = status;
  if (state.liveryTemplatePreviewSelectedKeys[templatePreviewWorkflowKey(workflow)] === key) {
    renderTemplatePreviewStatus(workflow, status, car);
  }
  return status;
}

function updateTemplatePreviewSelection(workflow = "", car = null) {
  const carId = String(car?.carId || "").trim();
  const key = carId ? templatePreviewStatusKey(workflow, carId) : "";
  const workflowKey = templatePreviewWorkflowKey(workflow);
  state.liveryTemplatePreviewSelectedKeys[workflowKey] = key;
  setTemplatePreviewStatusVisible(workflow, false);
  if (!carId) {
    renderTemplatePreviewStatus(workflow, null, null);
    return;
  }
  const cached = state.liveryTemplatePreviewStatuses[key];
  renderTemplatePreviewStatus(workflow, cached || fallbackTemplatePreviewStatus(workflow, car), car);
}

function creatorTemplateCatalogKey(workflow = "", carId = "", templateId = "") {
  const workflowKey = templatePreviewWorkflowKey(workflow);
  const carKey = String(carId || "").trim().toLowerCase();
  const templateKey = workflowKey === "advanced" ? String(templateId || "").trim().toLowerCase() : "";
  return templateKey ? `${workflowKey}:${carKey}:${templateKey}` : `${workflowKey}:${carKey}`;
}

function creatorTemplateCatalogTemplateId(workflow = "", car = null) {
  if (templatePreviewWorkflowKey(workflow) !== "advanced") return "";
  return String(selectedAdvancedCreatorTemplate(car)?.id || "").trim();
}

function creatorTemplateLibraryElements(workflow = "") {
  const key = templatePreviewWorkflowKey(workflow);
  if (key !== "advanced") return { root: null, list: null };
  return {
    root: $("advancedCreatorTemplateLibrary"),
    list: $("advancedCreatorTemplateLibraryOptions"),
  };
}

function creatorTemplateSelectionName(workflow = "") {
  return `${templatePreviewWorkflowKey(workflow)}CreatorTemplateLibrary`;
}

function creatorBlankTemplateOptionHtml(workflow = "", selectedId = "blank") {
  const selected = String(selectedId || "blank") === "blank";
  return `
    <label class="creator-option-card creator-template-option${selected ? " active" : ""}">
      <input type="radio" name="${escapeHtml(creatorTemplateSelectionName(workflow))}" value="blank"${selected ? " checked" : ""} />
      <span class="creator-option-control" aria-hidden="true"></span>
      <span class="creator-option-copy">
        <strong>Blank livery</strong>
        <span class="creator-option-meta">
          <span><b>Type</b>Normal creator project</span>
          <span><b>Output</b>Starts with a clean blank livery</span>
        </span>
      </span>
    </label>
  `;
}

function creatorStartPointOptionHtml(workflow = "", startPoint = {}, selectedId = "blank") {
  const id = String(startPoint.id || startPoint.mode || "");
  if (!id || id === "blank") return "";
  const selected = id === String(selectedId || "");
  const disabled = startPoint.enabled === false || startPoint.available === false;
  const rows = [
    ["Type", "Start point"],
    ["Source", String(startPoint.source || "game content").replace(/_/g, " ")],
  ];
  const textureCount = Number(startPoint.readyTextureCount || 0);
  if (textureCount) rows.push(["Textures", `${textureCount} ready`]);
  const warning = Array.isArray(startPoint.warnings) ? String(startPoint.warnings[0] || "").trim() : "";
  if (disabled && warning) rows.push(["Status", warning]);
  const description = String(startPoint.description || "").trim();
  if (description) rows.push(["Note", description]);
  return `
    <label class="creator-option-card creator-template-option${selected ? " active" : ""}${disabled ? " is-disabled" : ""}">
      <input type="radio" name="${escapeHtml(creatorTemplateSelectionName(workflow))}" value="${escapeHtml(id)}"${selected ? " checked" : ""}${disabled ? " disabled" : ""} />
      <span class="creator-option-control" aria-hidden="true"></span>
      <span class="creator-option-copy">
        <strong>${escapeHtml(startPoint.label || id)}</strong>
        <span class="creator-option-meta">
          ${rows.map(([key, value]) => `<span><b>${escapeHtml(key)}</b>${escapeHtml(value)}</span>`).join("")}
        </span>
      </span>
    </label>
  `;
}

function renderCreatorTemplateLibrary(workflow = "", catalog = null, car = null) {
  const { root, list } = creatorTemplateLibraryElements(workflow);
  if (!root || !list) return;
  const workflowKey = templatePreviewWorkflowKey(workflow);
  const carId = String(car?.carId || "").trim();
  if (!carId) {
    setTemplatePreviewStatusVisible(workflow, false);
    root.hidden = false;
    list.innerHTML = `<div class="creator-template-empty">Select a car to load start points.</div>`;
    return;
  }
  const startPoints = Array.isArray(catalog?.startPoints)
    ? catalog.startPoints.filter((item) => item && typeof item === "object" && String(item.id || item.mode || "") !== "blank")
    : [];
  const hasStartPointWorkflow = startPoints.some((item) => item.enabled !== false && item.available !== false);
  setTemplatePreviewStatusVisible(workflow, hasStartPointWorkflow);
  if (hasStartPointWorkflow) {
    const statusKey = templatePreviewStatusKey(workflow, carId);
    const cached = state.liveryTemplatePreviewStatuses[statusKey];
    renderTemplatePreviewStatus(workflow, cached || fallbackTemplatePreviewStatus(workflow, car), car);
    if (!cached && !catalog?.loading) {
      refreshTemplatePreviewStatus(workflow, carId, car);
    }
  }
  const rawSelectedId = String(state.liveryTemplateSelectedIds[workflowKey] || "blank");
  const startPointIds = new Set(startPoints.filter((item) => item.enabled !== false && item.available !== false).map((item) => String(item.id || item.mode || "")));
  const selectedId = rawSelectedId === "blank"
    || startPointIds.has(rawSelectedId)
      ? rawSelectedId
      : "blank";
  const loading = Boolean(catalog?.loading);
  const apiUnavailable = Boolean(catalog?.apiUnavailable);
  const warnings = Array.isArray(catalog?.warnings) ? catalog.warnings.filter(Boolean) : [];
  const intro = loading
    ? `<div class="creator-template-empty">Loading start points...</div>`
    : apiUnavailable
      ? `<div class="creator-template-empty is-warning">Start point API is not available in this runtime. Rebuild required.</div>`
      : warnings.length
        ? `<div class="creator-template-empty is-warning">${escapeHtml(warnings[0])}</div>`
        : "";
  list.innerHTML = [
    intro,
    creatorBlankTemplateOptionHtml(workflow, selectedId),
    ...startPoints.map((startPoint) => creatorStartPointOptionHtml(workflow, startPoint, selectedId)),
  ].join("");
}

async function refreshCreatorTemplateLibrary(workflow = "", carId = "", car = null) {
  const key = creatorTemplateCatalogKey(workflow, carId, creatorTemplateCatalogTemplateId(workflow, car));
  if (!String(carId || "").trim()) {
    renderCreatorTemplateLibrary(workflow, null, null);
    return null;
  }
  const hasStartPointApi = typeof window.pywebview?.api?.list_livery_start_points === "function";
  if (!hasStartPointApi) {
    const unavailable = {
      startPoints: [],
      apiUnavailable: true,
      warnings: ["Start point API is not available in this runtime. Rebuild required."],
    };
    state.liveryTemplateCatalogs[key] = unavailable;
    renderCreatorTemplateLibrary(workflow, unavailable, car);
    return unavailable;
  }
  const requestId = (state.liveryTemplateCatalogRequestIds[key] || 0) + 1;
  state.liveryTemplateCatalogRequestIds[key] = requestId;
  state.liveryTemplateCatalogs[key] = { loading: true, startPoints: [] };
  renderCreatorTemplateLibrary(workflow, state.liveryTemplateCatalogs[key], car);
  let catalog = null;
  try {
    const warnings = [];
    const startPointResult = await window.pywebview.api.list_livery_start_points({
      carId,
      workflow: templatePreviewWorkflowKey(workflow),
      root: normalizeRootPath($("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || state.root || ""),
      template: templatePreviewWorkflowKey(workflow) === "advanced" ? selectedAdvancedCreatorTemplate(car) : null,
    });
    if (startPointResult && !startPointResult.ok) warnings.push(startPointResult.message || "Start points could not be loaded.");
    catalog = {
      startPoints: startPointResult?.ok && Array.isArray(startPointResult.data?.startPoints) ? startPointResult.data.startPoints : [],
      warnings,
    };
  } catch (error) {
    catalog = {
      startPoints: [],
      warnings: [error?.message || String(error) || "Start point catalog check failed."],
    };
  }
  if (state.liveryTemplateCatalogRequestIds[key] !== requestId) return state.liveryTemplateCatalogs[key] || null;
  state.liveryTemplateCatalogs[key] = catalog;
  const workflowKey = templatePreviewWorkflowKey(workflow);
  if (state.liveryTemplateCatalogSelectedKeys[workflowKey] === key) {
    const startPoints = Array.isArray(catalog.startPoints) ? catalog.startPoints : [];
    const startPointIds = new Set(startPoints.filter((item) => item?.enabled !== false && item?.available !== false).map((item) => String(item?.id || item?.mode || "")));
    const selectedId = String(state.liveryTemplateSelectedIds[workflowKey] || "blank");
    if (selectedId !== "blank" && !startPointIds.has(selectedId)) {
      state.liveryTemplateSelectedIds[workflowKey] = "blank";
    }
    renderCreatorTemplateLibrary(workflow, catalog, car);
  }
  return catalog;
}

function updateCreatorTemplateLibrarySelection(workflow = "", car = null) {
  const workflowKey = templatePreviewWorkflowKey(workflow);
  const carId = String(car?.carId || "").trim();
  const key = carId ? creatorTemplateCatalogKey(workflow, carId, creatorTemplateCatalogTemplateId(workflow, car)) : "";
  const previousKey = state.liveryTemplateCatalogSelectedKeys[workflowKey] || "";
  state.liveryTemplateCatalogSelectedKeys[workflowKey] = key;
  if (key !== previousKey) {
    state.liveryTemplateSelectedIds[workflowKey] = "blank";
  }
  state.liveryTemplateSelectedIds[workflowKey] = state.liveryTemplateSelectedIds[workflowKey] || "blank";
  if (!carId) {
    state.liveryTemplateSelectedIds[workflowKey] = "blank";
    renderCreatorTemplateLibrary(workflow, null, null);
    return;
  }
  const cached = state.liveryTemplateCatalogs[key];
  renderCreatorTemplateLibrary(workflow, cached || { loading: true, startPoints: [] }, car);
  if (!cached) {
    refreshCreatorTemplateLibrary(workflow, carId, car);
  }
}

function selectedCreatorTemplatePayload(workflow = "") {
  const workflowKey = templatePreviewWorkflowKey(workflow);
  const selectedId = String(state.liveryTemplateSelectedIds[workflowKey] || "blank").trim() || "blank";
  if (selectedId === "blank") {
    return {
      mode: "blank",
      templateId: "",
      source: "",
    };
  }
  const catalog = state.liveryTemplateCatalogs[state.liveryTemplateCatalogSelectedKeys[workflowKey]] || {};
  const startPoints = Array.isArray(catalog.startPoints) ? catalog.startPoints : [];
  const startPoint = startPoints.find((item) => String(item?.id || item?.mode || "") === selectedId);
  if (startPoint) {
    return {
      mode: String(startPoint.mode || startPoint.id || selectedId),
      startPointId: selectedId,
      name: String(startPoint.label || selectedId),
      carId: String(startPoint.carId || ""),
      materialPath: String(startPoint.materialPath || ""),
      textures: Array.isArray(startPoint.textures) ? startPoint.textures : [],
    };
  }
  return {
    mode: "blank",
    templateId: "",
    source: "",
  };
}

function advancedCreatorTemplatesForCar(car = {}) {
  const liveryLabKeys = liveryLabCardesignPathKeys();
  const isBlockedAppTemplatePath = (value) => {
    const normalized = normalizeBrowserFavoritePath(value);
    return normalized.includes("\\liverylibrary\\")
      || normalized.includes("\\liveryprojects\\")
      || normalized.includes("\\officialliverylibrary\\")
      || normalized.includes("\\officialliveryprojects\\");
  };
  return (Array.isArray(car?.templates) ? car.templates : [])
    .map((row) => (row && typeof row === "object" ? row : {}))
    .filter((row) => {
      if (!String(row.id || row.cardesignPath || row.cardesign || "").trim()) return false;
      const cardesignPath = String(row.cardesignPath || "");
      const cardesignRel = String(row.cardesign || "");
      if (isBlockedAppTemplatePath(cardesignPath) || isBlockedAppTemplatePath(cardesignRel)) return false;
      if (liveryLabKeys.has(normalizeBrowserFavoritePath(cardesignPath))) return false;
      if (liveryLabKeys.has(normalizeBrowserFavoritePath(cardesignRel))) return false;
      return true;
    });
}

function selectedAdvancedCreatorCar() {
  const selected = String(state.advancedCreatorSelectedCarId || "").trim().toLowerCase();
  if (!selected) return null;
  return advancedCreatorCars().find((car) => String(car.carId || "").trim().toLowerCase() === selected) || null;
}

function selectedAdvancedCreatorTemplate(car = selectedAdvancedCreatorCar()) {
  const templates = advancedCreatorTemplatesForCar(car).filter((template) => template.ready);
  if (!templates.length) return null;
  const selectedId = String(state.advancedCreatorSelectedTemplateId || "").trim();
  return templates.find((template) => String(template.id || "") === selectedId)
    || templates.find((template) => String(template.id || "") === String(car?.selectedTemplateId || ""))
    || templates.find((template) => template.thumbnailExists)
    || templates[0]
    || null;
}

function advancedPathStem(value = "") {
  const file = String(value || "").split(/[\\/]/).pop() || "";
  return file.replace(/\.[^.]+$/, "").replace(/[_-]+/g, " ").trim();
}

function advancedTemplateMeta(template = {}) {
  const parts = [];
  const cardesign = String(template.cardesign || template.cardesignPath || "").split(/[\\/]/).pop();
  const mechanical = advancedPathStem(template.mechanicalPreset || "");
  const visual = advancedPathStem(template.visualPreset || "");
  if (cardesign) parts.push(`Cardesign: ${cardesign}`);
  if (mechanical) parts.push(`Mechanical: ${mechanical}`);
  if (visual) parts.push(`Visual: ${visual}`);
  if (!template.thumbnailExists) parts.push("No thumbnail");
  return parts.join(" | ");
}

function advancedTemplateOptionHtml(template = {}, selectedId = "") {
  const id = String(template.id || "");
  const label = String(template.label || template.cardesign || id || "Template");
  const active = id === String(selectedId || "");
  const thumbnailPath = template.thumbnailExists ? String(template.thumbnailPath || "") : "";
  const rows = [
    ["Cardesign", String(template.cardesign || template.cardesignPath || "").split(/[\\/]/).pop()],
    ["Mechanical", advancedPathStem(template.mechanicalPreset || "")],
    ["Visual", advancedPathStem(template.visualPreset || "")],
  ].filter((row) => String(row[1] || "").trim());
  if (!template.thumbnailExists) rows.push(["Thumbnail", "Missing"]);
  return `
    <label class="creator-option-card has-thumb${active ? " active" : ""}">
      <input type="radio" name="advancedCreatorTemplate" value="${escapeHtml(id)}"${active ? " checked" : ""} />
      <span class="creator-option-control" aria-hidden="true"></span>
      <span class="creator-option-thumb" aria-hidden="true">
        <img alt="" src="${mySkinPlaceholderSrc()}" data-advanced-car-thumb-path="${escapeHtml(thumbnailPath)}" />
      </span>
      <span class="creator-option-copy">
        <strong>${escapeHtml(label)}</strong>
        <span class="creator-option-meta">
          ${rows.map(([key, value]) => `<span><b>${escapeHtml(key)}</b>${escapeHtml(value)}</span>`).join("")}
        </span>
      </span>
    </label>
  `;
}

function renderAdvancedCreatorTemplateOptions(car = null) {
  const row = $("advancedCreatorTemplateRow");
  const list = $("advancedCreatorTemplateOptions");
  if (!row || !list) return;
  const selectedCar = car || selectedAdvancedCreatorCar();
  const templates = advancedCreatorTemplatesForCar(selectedCar).filter((template) => template.ready);
  row.hidden = templates.length === 0;
  if (!templates.length) {
    list.innerHTML = "";
    state.advancedCreatorSelectedTemplateId = "";
    return;
  }
  const selected = selectedAdvancedCreatorTemplate(selectedCar) || templates[0];
  state.advancedCreatorSelectedTemplateId = String(selected?.id || "");
  list.innerHTML = templates.map((template) => advancedTemplateOptionHtml(template, state.advancedCreatorSelectedTemplateId)).join("");
  hydrateAdvancedCreatorThumbnails(list);
  syncAdvancedCreatorTemplateFields(selected);
}

function syncAdvancedCreatorTemplateFields(template = null) {
  if (!template) return;
  const fields = {
    skinCardesign: template.cardesignPath || template.cardesign || "",
    skinDesign: template.design || "",
    skinActor: template.actor || "",
    skinVisual: template.visualPreset || "",
    skinMechanical: template.mechanicalPreset || "",
    skinCarFinalState: template.carFinalState || "",
  };
  Object.entries(fields).forEach(([id, value]) => {
    const field = $(id);
    if (!field) return;
    field.value = String(value || "");
    updatePathFileName(id);
  });
  renderWorkflowCompatibility();
}

function applyAdvancedCreatorCarSelection(car = null) {
  const selected = car || selectedAdvancedCreatorCar();
  const carId = String(selected?.carId || state.advancedCreatorSelectedCarId || "").trim();
  if ($("advancedSelectedCarTitle")) $("advancedSelectedCarTitle").textContent = selected ? advancedCreatorCarLabel(selected) : "Choose a car";
  if ($("advancedSelectedCarId")) $("advancedSelectedCarId").textContent = carId || "-";
  state.advancedCreatorSelectedCarId = carId;
  renderAdvancedCreatorTemplateOptions(selected);
  updateTemplatePreviewSelection("advanced", selected);
  updateCreatorTemplateLibrarySelection("advanced", selected);
}

function renderAdvancedCreatorCars(data = state.carCompatibility) {
  const grid = $("advancedCreatorCarGrid");
  const status = $("advancedCreatorCarStatus");
  if (!grid || !status) return;
  renderAdvancedCreatorSortControls();
  const cars = advancedCreatorCars(data);
  const readyCars = cars.filter((car) => car.ready);
  if (!data) {
    grid.innerHTML = "";
    status.className = "home-health-summary-pill is-pending";
    status.textContent = "Run Car Analysis first.";
    return;
  }
  if (!cars.length) {
    grid.innerHTML = "";
    status.className = "home-health-summary-pill is-warning";
    status.textContent = "No Advanced creator catalog found. Run Analyze Cars.";
    state.advancedCreatorSelectedCarId = "";
    applyAdvancedCreatorCarSelection(null);
    return;
  }
  if (!readyCars.length) {
    grid.innerHTML = "";
    status.className = "home-health-summary-pill is-warning";
    status.textContent = "No ready Advanced templates found.";
    state.advancedCreatorSelectedCarId = "";
    applyAdvancedCreatorCarSelection(null);
    return;
  }
  let selectedId = String(state.advancedCreatorSelectedCarId || "").trim();
  if (!readyCars.some((car) => String(car.carId || "") === selectedId)) {
    selectedId = String(readyCars[0]?.carId || "");
  }
  state.advancedCreatorSelectedCarId = selectedId;
  const selected = readyCars.find((car) => String(car.carId || "") === selectedId) || null;
  applyAdvancedCreatorCarSelection(selected);
  const query = String(state.advancedCreatorSearch || $("advancedCreatorCarSearch")?.value || "").trim();
  const visibleCars = readyCars.filter((car) => advancedCreatorCarMatchesSearch(car, query));
  if (query && !visibleCars.some((car) => String(car.carId || "") === selectedId)) {
    const nextVisible = visibleCars[0] || null;
    if (nextVisible) {
      selectedId = String(nextVisible.carId || "");
      state.advancedCreatorSelectedCarId = selectedId;
      applyAdvancedCreatorCarSelection(nextVisible);
    }
  }
  const groups = advancedCreatorCarGroups(visibleCars);
  grid.innerHTML = groups.map((group) => `
    <section class="advanced-car-group" data-advanced-creator-group="${escapeHtml(group.id)}">
      <div class="advanced-car-group-head">
        <strong>${escapeHtml(group.title)} [${group.rows.length.toLocaleString()}]</strong>
      </div>
      <div class="advanced-car-group-body">
        ${group.rows.map((car) => advancedCreatorCardHtml(car, selectedId)).join("")}
      </div>
    </section>
  `).join("") || `<div class="advanced-car-empty">No Advanced cars match "${escapeHtml(query)}".</div>`;
  hydrateAdvancedCreatorThumbnails(grid);
  status.className = "home-health-summary-pill is-ok";
  status.textContent = query
    ? `${visibleCars.length.toLocaleString()} of ${readyCars.length.toLocaleString()} Advanced cars.`
    : `${readyCars.length.toLocaleString()} Advanced cars ready.`;
}

async function refreshAdvancedCreatorCars({ force = false } = {}) {
  if (force || !state.carCompatibility) {
    await loadCarCompatibility(Boolean(force));
  } else {
    renderAdvancedCreatorCars(state.carCompatibility);
  }
  return advancedCreatorCars();
}

async function selectAdvancedCreatorCar(carId) {
  const cleanId = String(carId || "").trim();
  if (!cleanId) return;
  const car = advancedCreatorCars().find((row) => String(row.carId || "") === cleanId);
  if (!car || !car.ready) return;
  state.advancedCreatorSelectedCarId = cleanId;
  const selectedTemplate = selectedAdvancedCreatorTemplate(car);
  state.advancedCreatorSelectedTemplateId = String(selectedTemplate?.id || "");
  applyAdvancedCreatorCarSelection(car);
  renderAdvancedCreatorCars(state.carCompatibility);
  if ($("skinCardesign")?.value.trim()) await autofillSkinTemplateFields(false);
}

async function setAdvancedCreatorCarFavorite(carId, favorite) {
  const cleanId = String(carId || "").trim();
  if (!cleanId) return;
  const current = advancedCreatorFavoriteCarIdSet();
  if (favorite) current.add(cleanId.toLowerCase());
  else current.delete(cleanId.toLowerCase());
  const byKey = new Map();
  advancedCreatorCars().forEach((car) => {
    const id = String(car.carId || "").trim();
    if (id) byKey.set(id.toLowerCase(), id);
  });
  byKey.set(cleanId.toLowerCase(), cleanId);
  state.advancedCreatorFavoriteCarIds = [...current].map((key) => byKey.get(key) || key).sort((a, b) => a.localeCompare(b));
  renderAdvancedCreatorCars(state.carCompatibility);
  if (typeof window.pywebview?.api?.set_advanced_creator_car_favorite !== "function") return;
  const result = await call("set_advanced_creator_car_favorite", { carId: cleanId, favorite });
  if (!result.ok) {
    if (typeof window.pywebview?.api?.get_advanced_creator_car_favorites === "function") {
      const reload = await window.pywebview.api.get_advanced_creator_car_favorites();
      if (reload?.ok) state.advancedCreatorFavoriteCarIds = Array.isArray(reload.data?.favorites) ? reload.data.favorites : [];
    }
    renderAdvancedCreatorCars(state.carCompatibility);
    return;
  }
  state.advancedCreatorFavoriteCarIds = Array.isArray(result.data?.favorites) ? result.data.favorites : state.advancedCreatorFavoriteCarIds;
  renderAdvancedCreatorCars(state.carCompatibility);
}

function officialCreatorCars(data = state.officialCarResourcesStatus) {
  const rows = Array.isArray(data?.creatorCars)
    ? data.creatorCars
    : (Array.isArray(data?.derivedStatuses) ? data.derivedStatuses : []);
  const seen = new Set();
  return rows
    .map((row) => (row && typeof row === "object" ? row : {}))
    .filter((row) => {
      const carId = String(row.carId || "").trim();
      if (!carId || seen.has(carId.toLowerCase())) return false;
      seen.add(carId.toLowerCase());
      return true;
    })
    .sort((a, b) => officialCreatorCarLabel(a).localeCompare(officialCreatorCarLabel(b)) || String(a.carId || "").localeCompare(String(b.carId || "")));
}

function officialCreatorFavoriteCarIdSet() {
  return new Set((Array.isArray(state.officialCreatorFavoriteCarIds) ? state.officialCreatorFavoriteCarIds : [])
    .map((carId) => String(carId || "").trim().toLowerCase())
    .filter(Boolean));
}

function officialCreatorCarIsFavorite(car = {}) {
  return officialCreatorFavoriteCarIdSet().has(String(car.carId || "").trim().toLowerCase());
}

function officialCreatorCarType(car = {}) {
  const explicit = String(car.carType || car.type || "").trim().toLowerCase();
  if (explicit === "race" || explicit === "race_car" || explicit === "race-car" || explicit === "racing") return "race";
  if (explicit === "road" || explicit === "road_car" || explicit === "road-car" || explicit === "street") return "road";
  const carId = String(car.carId || "").trim().toLowerCase();
  const knownRaceCarIds = new Set([
    "ks_audi_r8_lms_gt3_evo_2",
    "ks_audi_r8_lms_gt4_evo",
    "ks_bmw_m2_cs_racing",
    "ks_bmw_m4_gt3",
    "ks_caterham_academy",
    "ks_ferrari_296_gt3",
    "ks_ferrari_488_challenge_evo",
    "ks_ferrari_f2004",
    "ks_ferrari_f40_lm",
    "ks_ferrari_sf_25",
    "ks_ford_mustang_gt3",
    "ks_lamborghini_huracan_st_evo2",
    "ks_maserati_mc20_gt2",
    "ks_mazda_mx5_nd_cup",
    "ks_mercedes_amg_gt2",
    "ks_porsche_718_cayman_gt4_cs_mr",
    "ks_porsche_935",
    "ks_porsche_991ii_gt2_rs_clubsport_evo",
    "ks_porsche_992_gt3_cup",
    "ks_porsche_992_gt3_r_rennsport",
  ]);
  const knownRoadCarIds = new Set([
    "ks_lamborghini_huracan_sto",
    "ks_lotus_emira",
    "ks_lotus_exige_v6_cup",
    "ks_porsche_718_cayman_gt4_rs",
    "ks_porsche_992_gt3_rs",
  ]);
  if (knownRaceCarIds.has(carId)) return "race";
  if (knownRoadCarIds.has(carId)) return "road";
  const haystack = [
    car.carId,
    car.displayName,
    ...(Array.isArray(car.vehicleVariants) ? car.vehicleVariants.flatMap((variant) => [
      variant?.label,
      variant?.displayLabel,
      variant?.mechanicalPresetName,
      variant?.mechanicalPresetShortName,
      variant?.visualPresetName,
      variant?.visualPresetPresentation,
    ]) : []),
  ].map((value) => String(value || "").toLowerCase()).join(" ");
  if (/\b(gt2|gt3|gt4|gte|lmp|lmh|challenge|competizione|dtm|formula|f1|indy|lms|touring|race|racing)\b/.test(haystack)) {
    return "race";
  }
  return "road";
}

function officialCreatorCarTypeLabel(type = "") {
  return officialCreatorCarType({ carType: type }) === "race" ? "Race cars" : "Road cars";
}

const OFFICIAL_CREATOR_CAR_BRANDS = [
  ["alfa_romeo", "Alfa Romeo"],
  ["mercedes", "Mercedes-Benz"],
  ["volkswagen", "Volkswagen"],
  ["lamborghini", "Lamborghini"],
  ["chevrolet", "Chevrolet"],
  ["caterham", "Caterham"],
  ["maserati", "Maserati"],
  ["hyundai", "Hyundai"],
  ["porsche", "Porsche"],
  ["ferrari", "Ferrari"],
  ["dallara", "Dallara"],
  ["renault", "Renault"],
  ["peugeot", "Peugeot"],
  ["abarth", "Abarth"],
  ["alpine", "Alpine"],
  ["audi", "Audi"],
  ["bmw", "BMW"],
  ["datsun", "Datsun"],
  ["ford", "Ford"],
  ["honda", "Honda"],
  ["ktm", "KTM"],
  ["lancia", "Lancia"],
  ["lotus", "Lotus"],
  ["mazda", "Mazda"],
  ["mini", "MINI"],
  ["toyota", "Toyota"],
];

function officialCreatorCarBrand(car = {}) {
  const carId = String(car.carId || "").trim().toLowerCase().replace(/^ks_/, "");
  const match = OFFICIAL_CREATOR_CAR_BRANDS.find(([prefix]) => carId === prefix || carId.startsWith(`${prefix}_`));
  if (match) return match[1];
  const fallback = carId.split("_").filter(Boolean)[0] || "Other";
  return fallback.slice(0, 1).toUpperCase() + fallback.slice(1);
}

function officialCreatorCarMatchesSearch(car = {}, query = "") {
  const q = String(query || "").trim().toLowerCase();
  if (!q) return true;
  return [
    car.carId,
    car.displayName,
    officialCreatorCarLabel(car),
    officialCreatorCarBrand(car),
    officialCreatorCarTypeLabel(officialCreatorCarType(car)),
    ...(Array.isArray(car.vehicleVariants) ? car.vehicleVariants.flatMap((variant) => [
      variant?.label,
      variant?.displayLabel,
      variant?.mechanicalPresetName,
      variant?.mechanicalPresetShortName,
      variant?.visualPresetName,
      variant?.visualPresetPresentation,
    ]) : []),
  ].some((value) => String(value || "").toLowerCase().includes(q));
}

function officialCreatorStatusPillsHtml(car = {}) {
  const type = officialCreatorCarType(car);
  const variantCount = Number(car.officialExternalCompatibleVariantCount ?? (Array.isArray(car.vehicleVariants) ? officialVehicleVariantsForCar(car).length : 0) ?? 0);
  const pills = [
    { label: officialCreatorCarTypeLabel(type).replace(" cars", ""), className: type === "race" ? "is-race" : "is-road" },
    { label: car.ready ? "Ready" : "Missing", className: car.ready ? "is-ready" : "is-warning" },
  ];
  if (variantCount > 0) pills.push({ label: `${variantCount} variants`, className: "is-variants" });
  if (!car.thumbnailExists) pills.push({ label: "No thumb", className: "is-warning" });
  return `<div class="official-car-thumb-pills">${pills.map((pill) => `<span class="official-car-thumb-pill ${pill.className}">${escapeHtml(pill.label)}</span>`).join("")}</div>`;
}

function officialCreatorCardHtml(car = {}, selectedId = "") {
  const carId = String(car.carId || "");
  const thumbnailPath = car.thumbnailExists ? String(car.thumbnailPath || "") : "";
  const active = carId === selectedId;
  const favorite = officialCreatorCarIsFavorite(car);
  return `
    <div class="official-car-card${active ? " active" : ""}" role="button" tabindex="0" aria-disabled="false" data-official-creator-car-id="${escapeHtml(carId)}">
      <button class="official-car-favorite-toggle ${favorite ? "is-active" : ""}" type="button" aria-label="${favorite ? "Remove favorite" : "Add favorite"}" data-official-creator-favorite-car-id="${escapeHtml(carId)}">${favorite ? "&#9733;" : "&#9734;"}</button>
      <div class="official-car-thumb-frame">
        <img class="official-car-image" alt="" src="${mySkinPlaceholderSrc()}" data-official-car-thumb-path="${escapeHtml(thumbnailPath)}" />
        ${officialCreatorStatusPillsHtml(car)}
      </div>
      <div class="official-car-info">
        <strong>${escapeHtml(officialCreatorCarLabel(car))}</strong>
        <small>${escapeHtml(carId)}</small>
      </div>
    </div>
  `;
}

function officialCreatorCarGroups(cars = []) {
  const sortCars = (rows) => [...rows]
    .sort((a, b) => officialCreatorCarLabel(a).localeCompare(officialCreatorCarLabel(b)) || String(a.carId || "").localeCompare(String(b.carId || "")));
  const groups = [];
  const favoriteRows = sortCars(cars.filter((car) => officialCreatorCarIsFavorite(car)));
  if (favoriteRows.length) {
    groups.push({ id: "favorites", title: "Favorites", rows: favoriteRows });
  }
  if (state.officialCreatorSortMode === "carType") {
    const raceRows = sortCars(cars.filter((car) => officialCreatorCarType(car) === "race"));
    const roadRows = sortCars(cars.filter((car) => officialCreatorCarType(car) !== "race"));
    if (raceRows.length) groups.push({ id: "race", title: "Race cars", rows: raceRows });
    if (roadRows.length) groups.push({ id: "road", title: "Road cars", rows: roadRows });
  } else if (state.officialCreatorSortMode === "carBrand") {
    const brandRows = new Map();
    cars.forEach((car) => {
      const brand = officialCreatorCarBrand(car);
      if (!brandRows.has(brand)) brandRows.set(brand, []);
      brandRows.get(brand).push(car);
    });
    [...brandRows.entries()]
      .sort(([left], [right]) => left.localeCompare(right))
      .forEach(([brand, rows]) => {
        groups.push({
          id: `brand_${brand.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "")}`,
          title: brand,
          rows: sortCars(rows),
        });
      });
  } else {
    groups.push({ id: "all", title: "All cars", rows: sortCars(cars) });
  }
  return groups;
}

function renderOfficialCreatorSortControls() {
  document.querySelectorAll("[data-official-creator-sort]").forEach((button) => {
    const active = String(button.dataset.officialCreatorSort || "") === state.officialCreatorSortMode;
    button.classList.toggle("active", active);
    button.setAttribute("aria-pressed", active ? "true" : "false");
  });
}

function officialCreatorCarLabel(car = {}) {
  const explicit = String(car.displayName || "").trim();
  if (explicit) return explicit;
  const carId = String(car.carId || "").trim();
  const withoutPrefix = carId.replace(/^ks_/, "");
  return withoutPrefix
    .split("_")
    .filter(Boolean)
    .map((part) => (part.length <= 3 && /[a-z]/i.test(part) ? part.toUpperCase() : part.slice(0, 1).toUpperCase() + part.slice(1)))
    .join(" ") || carId || "Unknown car";
}

function officialVehicleVariantsForCar(car = {}) {
  const variants = Array.isArray(car?.vehicleVariants) ? car.vehicleVariants : [];
  return variants
    .map((row) => (row && typeof row === "object" ? row : {}))
    .filter((row) => row.officialExternalCompatible !== false)
    .filter((row) => String(row.id || row.label || row.mechanicalPreset || row.visualPreset || "").trim());
}

function officialVariantPathStem(value = "") {
  const file = String(value || "").split(/[\\/]/).pop() || "";
  return file.replace(/\.[^.]+$/, "").replace(/[_-]+/g, " ").trim();
}

function officialVehicleVariantLabel(option = {}) {
  const display = String(option.displayLabel || "").trim();
  if (display) return display;
  const explicit = String(option.label || "").trim();
  if (explicit) return explicit;
  const mechanical = String(option.mechanicalPresetShortName || option.mechanicalPresetName || "").trim();
  if (mechanical) return mechanical;
  const visual = String(option.visualPresetPresentation || option.visualPresetName || "").trim();
  if (visual) return visual;
  const fallback = officialVariantPathStem(option.mechanicalPreset || option.visualPreset || "");
  return fallback ? fallback.replace(/\b\w/g, (match) => match.toUpperCase()) : "Default";
}

function selectedOfficialVehicleVariant() {
  const car = selectedOfficialCreatorCar();
  const variants = officialVehicleVariantsForCar(car);
  if (!variants.length) return null;
  const selectedId = String(state.officialCreatorSelectedVariantId || "").trim();
  return officialVehicleVariantById(variants, selectedId) || variants[0] || null;
}

function officialVehicleVariantById(variants = [], selectedId = "") {
  const id = String(selectedId || "").trim();
  if (!id) return null;
  return variants.find((row) => {
    if (!row || typeof row !== "object") return false;
    if (String(row.id || "") === id) return true;
    const aliases = Array.isArray(row.sourceVariantIds) ? row.sourceVariantIds : [];
    return aliases.some((aliasId) => String(aliasId || "") === id);
  }) || null;
}

function officialVehicleVariantThumbnailPath(option = {}, car = null) {
  const direct = option.thumbnailPath || option.thumbnail?.path || option.previewThumbnailPath || option.imagePath || "";
  if (String(direct || "").trim()) return String(direct || "").trim();
  return car?.thumbnailExists ? String(car.thumbnailPath || "").trim() : "";
}

function officialVehicleVariantOptionHtml(option = {}, selectedId = "", car = null) {
  const id = String(option.id || "");
  const active = id === String(selectedId || "");
  const thumbnailPath = officialVehicleVariantThumbnailPath(option, car);
  const rows = [
    ["Mechanical", officialVariantPathStem(option.mechanicalPreset || "")],
    ["Visual", officialVariantPathStem(option.visualPreset || "")],
    ["State", officialVariantPathStem(option.carFinalState || "")],
  ].filter((row) => String(row[1] || "").trim());
  return `
    <label class="creator-option-card official-variant-option${thumbnailPath ? " has-thumb" : ""}${active ? " active" : ""}">
      <input type="radio" name="officialVehicleVariant" value="${escapeHtml(id)}"${active ? " checked" : ""} />
      ${thumbnailPath ? `
        <span class="creator-option-thumb" aria-hidden="true">
          <img alt="" src="${mySkinPlaceholderSrc()}" data-official-car-thumb-path="${escapeHtml(thumbnailPath)}" />
        </span>
      ` : ""}
      <span class="creator-option-copy">
        <strong>${escapeHtml(officialVehicleVariantLabel(option))}</strong>
        <span class="creator-option-meta">
          ${rows.map(([key, value]) => `<span><b>${escapeHtml(key)}</b>${escapeHtml(value)}</span>`).join("")}
        </span>
      </span>
    </label>
  `;
}

function renderOfficialVehicleVariantOptions(car = null) {
  const row = $("officialVehicleVariantRow");
  const list = $("officialVehicleVariantOptions");
  if (!row || !list) return;
  const selectedCar = car || selectedOfficialCreatorCar();
  const variants = officialVehicleVariantsForCar(selectedCar);
  row.hidden = variants.length === 0;
  if (!variants.length) {
    list.innerHTML = "";
    state.officialCreatorSelectedVariantId = "";
    return;
  }
  let selectedId = String(state.officialCreatorSelectedVariantId || "").trim();
  const defaultId = String(selectedCar?.defaultVehicleVariantId || "").trim();
  const selectedVariant = officialVehicleVariantById(variants, selectedId) || officialVehicleVariantById(variants, defaultId);
  if (selectedVariant) {
    selectedId = String(selectedVariant.id || "");
  } else {
    selectedId = String(variants[0]?.id || "");
  }
  state.officialCreatorSelectedVariantId = selectedId;
  list.innerHTML = variants.map((option) => officialVehicleVariantOptionHtml(option, selectedId, selectedCar)).join("");
  hydrateOfficialCreatorThumbnails(list);
}

function selectedOfficialCreatorCar() {
  const selected = String(state.officialCreatorSelectedCarId || $("officialProjectCarModel")?.value || "").trim().toLowerCase();
  if (!selected) return null;
  return officialCreatorCars().find((car) => String(car.carId || "").trim().toLowerCase() === selected) || null;
}

function applyOfficialCreatorCarSelection(car = null) {
  const selected = car || selectedOfficialCreatorCar();
  const carId = String(selected?.carId || state.officialCreatorSelectedCarId || "").trim();
  if ($("officialProjectCarModel")) $("officialProjectCarModel").value = carId;
  if ($("officialSelectedCarTitle")) $("officialSelectedCarTitle").textContent = selected ? officialCreatorCarLabel(selected) : "Choose a car";
  if ($("officialSelectedCarId")) $("officialSelectedCarId").textContent = carId || "-";
  state.officialCreatorSelectedCarId = carId;
  renderOfficialVehicleVariantOptions(selected);
  updateTemplatePreviewSelection("official", selected);
  updateSkinIdPreview();
}

function renderOfficialCreatorCars(data = state.officialCarResourcesStatus) {
  const grid = $("officialCreatorCarGrid");
  const status = $("officialCreatorCarStatus");
  if (!grid || !status) return;
  renderOfficialCreatorSortControls();
  const cars = officialCreatorCars(data);
  const readyCars = cars.filter((car) => car.ready && Number(car.officialExternalCompatibleVariantCount || 0) > 0);
  if (!data) {
    grid.innerHTML = "";
    status.className = "home-health-summary-pill is-pending";
    status.textContent = "Official preview assets have not been checked yet.";
    return;
  }
  if (!cars.length) {
    grid.innerHTML = "";
    status.className = "home-health-summary-pill is-warning";
    status.textContent = "No prepared official cars found. Run setup extraction first.";
    state.officialCreatorSelectedCarId = "";
    applyOfficialCreatorCarSelection(null);
    return;
  }
  if (!readyCars.length) {
    grid.innerHTML = "";
    status.className = "home-health-summary-pill is-warning";
    status.textContent = "Prepared cars exist, but none are ready for project creation.";
    state.officialCreatorSelectedCarId = "";
    applyOfficialCreatorCarSelection(null);
    return;
  }
  let selectedId = String(state.officialCreatorSelectedCarId || $("officialProjectCarModel")?.value || "").trim();
  if (!readyCars.some((car) => String(car.carId || "") === selectedId)) {
    selectedId = String(readyCars[0]?.carId || "");
  }
  state.officialCreatorSelectedCarId = selectedId;
  const selected = readyCars.find((car) => String(car.carId || "") === selectedId) || null;
  applyOfficialCreatorCarSelection(selected);
  const query = String(state.officialCreatorSearch || $("officialCreatorCarSearch")?.value || "").trim();
  const visibleCars = readyCars.filter((car) => officialCreatorCarMatchesSearch(car, query));
  if (query && !visibleCars.some((car) => String(car.carId || "") === selectedId)) {
    const nextVisible = visibleCars[0] || null;
    if (nextVisible) {
      selectedId = String(nextVisible.carId || "");
      state.officialCreatorSelectedCarId = selectedId;
      applyOfficialCreatorCarSelection(nextVisible);
    }
  }
  const groups = officialCreatorCarGroups(visibleCars);
  grid.innerHTML = groups.map((group) => `
    <section class="official-car-group" data-official-creator-group="${escapeHtml(group.id)}">
      <div class="official-car-group-head">
        <strong>${escapeHtml(group.title)} [${group.rows.length.toLocaleString()}]</strong>
      </div>
      <div class="official-car-group-body">
        ${group.rows.map((car) => officialCreatorCardHtml(car, selectedId)).join("")}
      </div>
    </section>
  `).join("") || `<div class="official-car-empty">No official cars match "${escapeHtml(query)}".</div>`;
  hydrateOfficialCreatorThumbnails(grid);
  status.className = "home-health-summary-pill is-ok";
  status.textContent = query
    ? `${visibleCars.length.toLocaleString()} of ${readyCars.length.toLocaleString()} official cars.`
    : `${readyCars.length.toLocaleString()} official cars ready.`;
}

async function refreshOfficialCreatorCars({ force = false } = {}) {
  if (force || !state.officialCarResourcesStatus) {
    await refreshOfficialCarResourcesStatus({ silent: true });
  } else {
    renderOfficialCreatorCars(state.officialCarResourcesStatus);
  }
  return officialCreatorCars();
}

function selectOfficialCreatorCar(carId) {
  const cleanId = String(carId || "").trim();
  if (!cleanId) return;
  const car = officialCreatorCars().find((row) => String(row.carId || "") === cleanId);
  if (!car || !car.ready) return;
  state.officialCreatorSelectedCarId = cleanId;
  applyOfficialCreatorCarSelection(car);
  renderOfficialCreatorCars(state.officialCarResourcesStatus);
}

async function setOfficialCreatorCarFavorite(carId, favorite) {
  const cleanId = String(carId || "").trim();
  if (!cleanId) return;
  const current = officialCreatorFavoriteCarIdSet();
  if (favorite) current.add(cleanId.toLowerCase());
  else current.delete(cleanId.toLowerCase());
  const byKey = new Map();
  officialCreatorCars().forEach((car) => {
    const id = String(car.carId || "").trim();
    if (id) byKey.set(id.toLowerCase(), id);
  });
  byKey.set(cleanId.toLowerCase(), cleanId);
  state.officialCreatorFavoriteCarIds = [...current].map((key) => byKey.get(key) || key).sort((a, b) => a.localeCompare(b));
  renderOfficialCreatorCars(state.officialCarResourcesStatus);
  if (typeof window.pywebview?.api?.set_official_creator_car_favorite !== "function") return;
  const result = await call("set_official_creator_car_favorite", { carId: cleanId, favorite });
  if (!result.ok) {
    if (typeof window.pywebview?.api?.get_official_creator_car_favorites === "function") {
      const reload = await window.pywebview.api.get_official_creator_car_favorites();
      if (reload?.ok) state.officialCreatorFavoriteCarIds = Array.isArray(reload.data?.favorites) ? reload.data.favorites : [];
    }
    renderOfficialCreatorCars(state.officialCarResourcesStatus);
    return;
  }
  state.officialCreatorFavoriteCarIds = Array.isArray(result.data?.favorites) ? result.data.favorites : state.officialCreatorFavoriteCarIds;
  renderOfficialCreatorCars(state.officialCarResourcesStatus);
}

async function refreshOfficialCarResourcesStatus({ silent = true, root = "" } = {}) {
  if (typeof window.pywebview?.api?.get_official_car_resources_status !== "function") return null;
  const normalized = normalizeRootPath(root || $("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || state.root || "");
  const requestId = ++state.officialCarResourcesRequestId;
  if (!silent) {
    state.officialCarResourcesLoading = true;
    renderOfficialCarResourcesStatus(state.officialCarResourcesStatus);
  }
  try {
    const result = await window.pywebview.api.get_official_car_resources_status({ root: normalized });
    if (requestId !== state.officialCarResourcesRequestId) return state.officialCarResourcesStatus;
    state.officialCarResourcesStatus = result?.ok ? (result.data || null) : null;
    if (!result?.ok && !silent && result?.message) log(result.message, true);
  } catch (error) {
    if (requestId !== state.officialCarResourcesRequestId) return state.officialCarResourcesStatus;
    state.officialCarResourcesStatus = {
      ready: false,
      manifestError: error?.message || String(error) || "Status check failed",
    };
  } finally {
    if (requestId === state.officialCarResourcesRequestId) {
      state.officialCarResourcesLoading = false;
      renderOfficialCarResourcesStatus(state.officialCarResourcesStatus);
      applySetupGateStatus();
    }
  }
  return state.officialCarResourcesStatus;
}

async function runOfficialCarResourceExtract() {
  if (!requireLongPathsSetupReady()) return;
  const root = normalizeRootPath($("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || state.root || "");
  if (!root) {
    renderOfficialCarResourcesStatus(state.officialCarResourcesStatus);
    log("Select and save the AC EVO Game Folder first.", true);
    return;
  }
  const confirmed = await showConfirmModal({
    type: "warning",
    title: "Prepare Official Preview Resources",
    subtitle: "Local LiveryLab Setup",
    message: "Read the required car resources from content.kspkg and prepare reusable local previews now?",
    details: [
      "Only the resources required by LiveryLab are read from the AC EVO package.",
      "The complete game content is not extracted.",
      "The AC EVO installation is not modified.",
      "Generated preview resources are stored in LiveryLab-managed folders.",
      "Temporary source files are removed after preparation completes successfully.",
    ].join("\n"),
    confirmLabel: "Prepare Resources",
    cancelLabel: "Cancel",
  });
  if (!confirmed) return;
  state.officialCarResourcesLoading = true;
  renderOfficialCarResourcesStatus(state.officialCarResourcesStatus);
  resetPackageProgress();
  showPackageExtractModal({ official: true });
  let result = null;
  try {
    result = await window.pywebview.api.extract_official_car_resources({ root });
  } catch (error) {
    result = { ok: false, message: error?.message || String(error), error };
  }
  hidePackageExtractModal();
  state.officialCarResourcesLoading = false;
  if (result?.cancelled) {
    log(result.message || "Official preview asset setup cancelled", true);
    showStatusModal({
      type: "info",
      title: "Official Preview Resources",
      subtitle: "Preparation Cancelled",
      message: result.message || "Preparation was cancelled. Temporary source files remain in the local LiveryLab cache so you can continue this step later.",
    });
    renderOfficialCarResourcesStatus(state.officialCarResourcesStatus);
    return result;
  }
  if (result?.ok && typeof result.message === "string") {
    result.message = result.message.replace(/^Official car resources extracted/, "Official preview resources prepared");
  }
  showResult(result, "extract_official_car_resources", "extract_official_car_resources");
  if (result?.ok) {
    state.officialCarResourcesStatus = result.data?.status || result.data || null;
    setInspectOutputText(result.message || result.data?.summary || "");
    await refreshOfficialCarResourcesStatus({ silent: true, root });
  } else {
    renderOfficialCarResourcesStatus(state.officialCarResourcesStatus);
  }
  resetPackageProgress();
  applySetupGateStatus();
  return result;
}

function setHomeStepStatus(id, label, className = "is-pending") {
  const target = $(id);
  if (!target) return;
  target.className = `home-step-status ${className}`;
  target.textContent = label;
}

function setupGateRootReady() {
  const root = normalizeRootPath(state.root || $("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || "");
  if (!root || root !== normalizeRootPath(state.savedRoot) || !state.rootStatus) return false;
  if (state.rootStatus.contentPackageMixed || state.rootStatus.contentPackageAmbiguous) return false;
  return Boolean(state.rootStatus.exists && (state.rootStatus.contentCarsExists || state.rootStatus.contentPackageAvailable || state.rootStatus.contentPackageExists));
}

function setupGateLongPathsReady() {
  const longPaths = state.storageHealth?.longPaths;
  if (!longPaths || typeof longPaths !== "object") return false;
  if (typeof longPaths.ready === "boolean") return longPaths.ready;
  return longPaths.required !== true && longPaths.enabled === true;
}

const LONG_PATHS_SETUP_ACTION_IDS = new Set([
  "extractHomePackage",
  "disableHomeContentPackage",
  "prepareOfficialRuntime",
  "officialExtractCarResources",
  "homeAnalyzeCars",
]);

function syncLongPathsSetupActions() {
  const blocked = !setupGateLongPathsReady();
  LONG_PATHS_SETUP_ACTION_IDS.forEach((id) => {
    const button = $(id);
    if (!button) return;
    if (blocked) {
      button.dataset.longPathsBlocked = "true";
      button.disabled = true;
      button.title = "Windows Long Path Support is required before this setup action can run.";
      return;
    }
    if (button.dataset.longPathsBlocked === "true") {
      delete button.dataset.longPathsBlocked;
      button.disabled = false;
      if (button.title === "Windows Long Path Support is required before this setup action can run.") button.removeAttribute("title");
    }
  });
}

function renderLongPathsSetupRequirement() {
  const status = $("longPathsSetupStatus");
  const message = $("longPathsSetupMessage");
  const actions = $("longPathsSetupActions");
  const longPaths = state.storageHealth?.longPaths;
  const loading = state.storageHealthLoading || !longPaths;
  const ready = setupGateLongPathsReady();
  if (status) {
    status.className = `home-step-status ${loading ? "is-warning" : ready ? "is-ok" : "is-error"}`;
    status.textContent = loading ? "Checking" : ready ? "Ready" : "Required";
  }
  if (message) {
    message.className = `root-status ${loading ? "is-warning" : ready ? "is-ok" : "is-error"}`;
    if (loading) {
      message.textContent = "Checking Windows Long Path Support...";
    } else if (ready) {
      message.textContent = longPaths.runtime
        ? `Ready: the running LiveryLab process passed the ${Number(longPaths.probePathLength || 0)}-character path test.`
        : "Windows Long Paths is enabled. Runtime verification will be available after the next application build.";
    } else {
      message.textContent = longPaths.message || "Windows Long Path Support is required before setup can continue.";
    }
  }
  if (actions) actions.hidden = loading || ready;
  syncLongPathsSetupActions();
}

async function ensureLongPathsSetupReady() {
  if (setupGateLongPathsReady()) return true;
  await refreshStorageHealth({ silent: true, root: $("homeRootPath")?.value.trim() || state.root || "" });
  if (setupGateLongPathsReady()) return true;
  showLongPathsSetupNotice(state.storageHealth, { force: true });
  return false;
}

function requireLongPathsSetupReady() {
  if (setupGateLongPathsReady()) return true;
  showLongPathsSetupNotice(state.storageHealth, { force: true });
  return false;
}

function setupGateContentReady(scope = currentWorkflowScope()) {
  const contentExtracted = Boolean(state.rootStatus?.contentCarsExists || state.contentPackageStatus?.contentCarsExists);
  if (!contentExtracted) return false;
  if (normalizeWorkflowScope(scope) === "official") return true;
  if (!state.contentPackageStatus) return false;
  return !state.contentPackageStatus.activeExists;
}

function setupGateStorageReady() {
  if (state.storageHealthLoading || !state.storageHealth) return false;
  return storageHealthRows(state.storageHealth).every((row) => {
    if (row.key === "gameRoot") return true;
    return storageHealthState(row).className !== "is-error";
  });
}

function setupGateCarsReady() {
  return Boolean(state.carAnalysisBypassed || (state.carCompatibility && !state.carCompatibilityLoading && !state.carCompatibilityCacheStatus?.updateNeeded));
}

function setupGateOfficialExtractReady() {
  return officialCarResourcesReady();
}

function evaluateSetupGate() {
  const scope = currentWorkflowScope();
  if (scope === "advanced") {
    return {
      ready: [setupGateLongPathsReady(), setupGateRootReady(), setupGateContentReady(scope), setupGateStorageReady(), setupGateCarsReady()].every(Boolean),
      steps: [
        { key: "long-paths", number: "REQ", label: "Windows Long Paths", ready: setupGateLongPathsReady() },
        { key: "root", number: "01", label: "Document Root", ready: setupGateRootReady() },
        { key: "content", number: "02", label: "Game Resources", ready: setupGateContentReady(scope) },
        { key: "storage", number: "03", label: "Storage Health", ready: setupGateStorageReady() },
        { key: "cars", number: "04", label: "Car Analysis", ready: setupGateCarsReady() },
      ],
    };
  }
  const steps = [
    { key: "long-paths", number: "REQ", label: "Windows Long Paths", ready: setupGateLongPathsReady() },
    { key: "root", number: "01", label: "AC EVO Game Folder", ready: setupGateRootReady() },
    { key: "official-runtime", number: "02", label: "Official Runtime", ready: setupGateOfficialRuntimeReady() },
    { key: "official-extract", number: "03", label: "Official Preview Resources", ready: setupGateOfficialExtractReady() },
  ];
  return {
    ready: steps.every((step) => step.ready),
    steps,
  };
}

function isSetupGateLocked() {
  return !state.setupGate?.ready;
}

function updateSetupGateCards(gate) {
  (gate.steps || []).forEach((step) => {
    const card = document.querySelector(`[data-setup-step="${step.key}"]`);
    if (!card) return;
    card.classList.toggle("is-complete", Boolean(step.ready));
    card.classList.toggle("is-blocked", !step.ready);
  });
  const officialRuntimeReady = setupGateOfficialRuntimeReady();
  setHomeStepStatus("officialRuntimeStepStatus", officialRuntimeReady ? "Ready" : "Next step", officialRuntimeReady ? "is-ok" : "is-warning");
  setHomeStepStatus("officialExtractStepStatus", setupGateOfficialExtractReady() ? "Ready" : "Next step", setupGateOfficialExtractReady() ? "is-ok" : "is-warning");
  renderLongPathsSetupRequirement();
  renderOfficialRuntimeStatus();
}

function updateSetupGateNavigation(gate) {
  document.querySelectorAll(".nav-button").forEach((button) => {
    const tab = button.dataset.tab || button.dataset.navLabel || "";
    const locked = Boolean(tab && !gate.ready && !SETUP_GATE_ALLOWED_TABS.has(tab));
    button.classList.toggle("is-setup-locked", locked);
    button.setAttribute("aria-disabled", locked ? "true" : "false");
    if (button.tagName === "BUTTON") button.disabled = locked;
    if (locked) {
      button.title = "Complete the required setup steps to unlock this area.";
    } else if (button.title === "Complete the required setup steps to unlock this area." || button.title === "Complete setup steps 01-04 to unlock this area.") {
      button.removeAttribute("title");
    }
  });
}

function renderSetupGateStatus(gate) {
  const target = $("setupGateStatus");
  if (!target) return;
  if (gate.ready) {
    target.className = "root-status home-setup-gate is-ok";
    target.textContent = currentWorkflowScope() === "official"
      ? "Official workflow setup complete. You can now create, import and activate Official External Liveries."
      : "Setup complete. All app areas are unlocked.";
    return;
  }
  const pending = (gate.steps || [])
    .filter((step) => !step.ready)
    .map((step) => `${step.number} ${step.label}`);
  target.className = "root-status home-setup-gate is-warning";
  target.textContent = currentWorkflowScope() === "official"
    ? `Complete the Official workflow setup: ${pending.join(", ") || "pending checks"}.`
    : `Complete setup to unlock the app: ${pending.join(", ") || "pending checks"}.`;
}

function applySetupGateStatus() {
  const gate = evaluateSetupGate();
  state.setupGate = gate;
  updateSetupGateCards(gate);
  updateSetupGateNavigation(gate);
  renderSetupGateStatus(gate);
  updateSetupActionPulses(gate);
  if (state.workflowChoiceComplete && !state.workflowStartupBusy && typeof window.LiveryLabLegacyMigration?.handleSetupGate === "function") {
    Promise.resolve(window.LiveryLabLegacyMigration.handleSetupGate(currentWorkflowScope(), gate.ready)).catch(() => {});
  }
}

function applyStartupLandingTab() {
  applySetupGateStatus();
  setTab(state.setupGate?.ready ? "library" : "home");
}

function showSetupGateNotice() {
  applySetupGateStatus();
  const target = $("setupGateStatus");
  if (target) {
    target.className = "root-status home-setup-gate is-warning";
    target.scrollIntoView({ behavior: "smooth", block: "center" });
  }
}

function setHomePackageStatus(label, className = "is-pending") {
  setHomeStepStatus("homePackageStatus", label, className);
  applySetupGateStatus();
}

function multiplayerSafetyPrerequisites(root = "") {
  const normalized = normalizeRootPath(root || $("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || state.root || "");
  const packageState = state.contentPackageStatus;
  const contentExtracted = Boolean(state.rootStatus?.contentCarsExists || packageState?.contentCarsExists);
  return {
    root: normalized,
    rootKnown: Boolean(normalized),
    contentExtracted,
    packageKnown: Boolean(packageState),
    packageActive: Boolean(packageState?.activeExists),
    ready: Boolean(normalized && contentExtracted && packageState && !packageState.activeExists),
  };
}

function renderMultiplayerSafetyStatus(data = state.multiplayerSafetyStatus) {
  const target = $("homeMultiplayerSafetyStatus");
  if (!target) return;
  const prereq = multiplayerSafetyPrerequisites();
  let className = "root-status is-unknown";
  let text = "LiveryLab Safety: waiting for game resources";
  if (!prereq.rootKnown) {
    text = "LiveryLab Safety: choose a root first";
  } else if (!prereq.contentExtracted) {
    text = "LiveryLab Safety: waiting for extracted content";
  } else if (!prereq.packageKnown) {
    text = "LiveryLab Safety: checking package state";
  } else if (prereq.packageActive) {
    className = "root-status is-warning";
    text = "LiveryLab Safety: waiting until content.kspkg is disabled";
  } else if (state.liveryLabSafetyBypassed) {
    className = "root-status is-ok";
    text = "LiveryLab Safety: disabled for testing";
  } else if (state.multiplayerSafetyRepairing) {
    className = "root-status is-warning";
    text = "LiveryLab Safety: installing required resources";
  } else if (!data) {
    text = "LiveryLab Safety: will install when needed";
  } else if (data.installed) {
    className = "root-status is-ok";
    text = "LiveryLab Safety: installed";
  } else if (data.blocked) {
    className = "root-status is-error";
    text = `LiveryLab Safety: blocked - ${data.blockReason || data.label || "repair required"}`;
  } else {
    className = "root-status is-warning";
    text = `LiveryLab Safety: ${data.label || "will install when needed"}`;
  }
  target.className = className;
  target.textContent = text;
  target.title = data?.manifestPath || data?.root || prereq.root || "";
}

async function refreshMultiplayerSafetyStatus(root = "") {
  const prereq = multiplayerSafetyPrerequisites(root);
  if (!prereq.ready) {
    state.multiplayerSafetyStatus = null;
    renderMultiplayerSafetyStatus(null);
    syncHomePackageStatus();
    return null;
  }
  const requestId = ++state.multiplayerSafetyStatusRequestId;
  renderMultiplayerSafetyStatus(null);
  try {
    const result = await window.pywebview.api.get_multiplayer_safety_status({ root: prereq.root });
    if (requestId !== state.multiplayerSafetyStatusRequestId) return state.multiplayerSafetyStatus;
    state.multiplayerSafetyStatus = result?.ok ? (result.data || null) : null;
  } catch (error) {
    if (requestId !== state.multiplayerSafetyStatusRequestId) return state.multiplayerSafetyStatus;
    state.multiplayerSafetyStatus = {
      installed: false,
      label: error?.message || String(error) || "Status check failed",
    };
  }
  renderMultiplayerSafetyStatus(state.multiplayerSafetyStatus);
  syncHomePackageStatus();
  return state.multiplayerSafetyStatus;
}

async function repairMultiplayerSafety(root = "") {
  const prereq = multiplayerSafetyPrerequisites(root);
  if (!prereq.ready) {
    renderMultiplayerSafetyStatus(state.multiplayerSafetyStatus);
    return state.multiplayerSafetyStatus;
  }
  if (state.liveryLabSafetyBypassed) {
    state.multiplayerSafetyStatus = { bypassed: true, installed: false, label: "Bypassed" };
    renderMultiplayerSafetyStatus(state.multiplayerSafetyStatus);
    return state.multiplayerSafetyStatus;
  }
  if (state.multiplayerSafetyRepairPromise) return state.multiplayerSafetyRepairPromise;
  state.multiplayerSafetyRepairing = true;
  renderMultiplayerSafetyStatus(state.multiplayerSafetyStatus);
  syncHomePackageStatus();
  log(`${t("log_running")} repair_multiplayer_safety_hook...`);
  state.multiplayerSafetyRepairPromise = window.pywebview.api.repair_multiplayer_safety_hook({ root: prereq.root })
    .then((result) => {
      if (result?.ok) {
        state.multiplayerSafetyStatus = result.data?.status || null;
        if (result.message) log(result.message);
      } else {
        const message = firstMessageLine(result?.message, "LiveryLab Safety repair failed");
        state.multiplayerSafetyStatus = { installed: false, label: message };
        log(message, true);
      }
      return state.multiplayerSafetyStatus;
    })
    .catch((error) => {
      const message = error?.message || String(error) || "LiveryLab Safety repair failed";
      state.multiplayerSafetyStatus = { installed: false, label: message };
      log(message, true);
      return state.multiplayerSafetyStatus;
    })
    .finally(() => {
      state.multiplayerSafetyRepairing = false;
      state.multiplayerSafetyRepairPromise = null;
      renderMultiplayerSafetyStatus(state.multiplayerSafetyStatus);
      syncHomePackageStatus();
    });
  return state.multiplayerSafetyRepairPromise;
}

async function ensureMultiplayerSafetyReady(root = "") {
  const status = await refreshMultiplayerSafetyStatus(root);
  if (state.liveryLabSafetyBypassed || status?.bypassed || status?.installed || status?.blocked) return status;
  return repairMultiplayerSafety(root);
}

async function syncSafetyBypassAfterProtectedWrite(result, root = "") {
  const safety = result?.data?.safetyRefresh || {};
  const repaired = safety?.status === "refreshed" || safety?.installed === true;
  if (!repaired) return;
  state.liveryLabSafetyBypassed = false;
  const resolvedRoot = normalizeRootPath(root || result?.data?.item?.game_root || state.root || $("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || "");
  if (resolvedRoot) {
    await refreshMultiplayerSafetyStatus(resolvedRoot);
  } else {
    renderMultiplayerSafetyStatus(state.multiplayerSafetyStatus);
    syncHomePackageStatus();
  }
  applySetupGateStatus();
}

async function toggleLiveryLabSafetyFlow(event = null) {
  if (event && !event.altKey) return;
  event?.preventDefault();
  event?.stopPropagation();
  const root = normalizeRootPath($("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || state.root || "");
  if (!root) {
    showResult({ ok: false, error: "Select the AC EVO root folder before toggling LiveryLab Safety." }, "LiveryLab Safety unavailable", "get_multiplayer_safety_status");
    return;
  }
  const enableBypass = !state.liveryLabSafetyBypassed;
  const result = await showConfirmModal({
    type: enableBypass ? "warning" : "info",
    title: enableBypass ? "Bypass LiveryLab Safety" : "Enable LiveryLab Safety",
    subtitle: "Hidden maintenance action",
    message: enableBypass
      ? "Disable and bypass LiveryLab Safety for this AC EVO root?"
      : "Enable LiveryLab Safety again for this AC EVO root?",
    details: [
      enableBypass
        ? "This removes existing LiveryLab Safety hooks, scripts, manifest and related localization entries, then prevents this app session from reinstalling or refreshing them. Restart LiveryLab to return to normal behavior."
        : "This clears the session bypass and installs or repairs LiveryLab Safety for the selected root.",
      "",
      `AC EVO root:\n${root}`,
    ].join("\n"),
    confirmLabel: enableBypass ? "Bypass Safety" : "Enable Safety",
    cancelLabel: "Cancel",
    loadingLabel: enableBypass ? "Disabling..." : "Enabling...",
    onConfirm: () => {
      if (window.pywebview.api.set_liverylab_safety_bypass) {
        return window.pywebview.api.set_liverylab_safety_bypass({ root, enabled: enableBypass });
      }
      return enableBypass
        ? window.pywebview.api.remove_liverylab_safety_hook({ root })
        : window.pywebview.api.repair_multiplayer_safety_hook({ root });
    },
  });
  if (!result) return;
  showResult(result, enableBypass ? "LiveryLab Safety bypassed" : "LiveryLab Safety enabled", "set_liverylab_safety_bypass");
  if (result.ok) {
    state.liveryLabSafetyBypassed = window.pywebview.api.set_liverylab_safety_bypass
      ? Boolean(result.data?.enabled)
      : enableBypass;
    state.multiplayerSafetyStatus = result.data?.status || null;
    renderMultiplayerSafetyStatus(state.multiplayerSafetyStatus);
    syncHomePackageStatus();
    applySetupGateStatus();
  } else {
    await refreshMultiplayerSafetyStatus(root);
  }
}

function syncHomePackageStatus() {
  const packPath = $("homePackagePath")?.value.trim() || "";
  const outDir = $("homePackageOutDir")?.value.trim() || $("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || "";
  const rootKnown = Boolean($("homeRootPath")?.value.trim() || $("rootPath")?.value.trim());
  const packageState = state.contentPackageStatus;
  const contentExtracted = Boolean(state.rootStatus?.contentCarsExists || packageState?.contentCarsExists);
  if (currentWorkflowScope() === "official" && contentExtracted) {
    setHomePackageStatus("Preview ready", "is-ok");
    return;
  }
  if (currentWorkflowScope() === "advanced" && packageState?.activeExists && advancedContentNeedsFullExtraction()) {
    setHomePackageStatus("Re-extract required", "is-error");
    return;
  }
  if (contentExtracted && packageState?.activeExists) {
    setHomePackageStatus("Package active", "is-warning");
    return;
  }
  if (contentExtracted && !packageState) {
    setHomePackageStatus("Checking", "is-warning");
    return;
  }
  if (contentExtracted) {
    setHomePackageStatus("Ready", "is-ok");
    return;
  }
  if (!packPath && !outDir) {
    setHomePackageStatus("Waiting", "is-pending");
  } else if (rootKnown && !packPath) {
    setHomePackageStatus("Not extracted", "is-warning");
  } else if (!packPath) {
    setHomePackageStatus("Choose package", "is-warning");
  } else if (!outDir) {
    setHomePackageStatus("Choose output", "is-warning");
  } else {
    setHomePackageStatus("Ready", "is-ok");
  }
}

function renderContentPackageStatus(data = null) {
  const target = $("homeContentPackageStatus");
  const button = $("disableHomeContentPackage");
  if (!target) return;
  const officialMode = currentWorkflowScope() === "official";
  const rootKnown = Boolean($("homeRootPath")?.value.trim() || $("rootPath")?.value.trim());
  const contentExtracted = Boolean(state.rootStatus?.contentCarsExists || data?.contentCarsExists);
  if (button) {
    button.disabled = false;
    button.title = "";
  }
  if (!rootKnown) {
    target.className = "root-status is-unknown";
    target.textContent = "content.kspkg status: choose a root first";
    if (button) button.hidden = true;
    return;
  }
  if (!data) {
    target.className = "root-status is-unknown";
    target.textContent = "content.kspkg status: checking...";
    if (button) button.hidden = true;
    return;
  }
  target.title = "";
  if (data.mixedPackageState || (data.activeExists && data.disabledExists)) {
    target.className = "root-status is-error";
    target.textContent = officialMode
      ? "Active and disabled content packages detected. Steam may have updated AC EVO while the package was disabled. Select Official again for guided manual cleanup. LiveryLab will not delete, rename or move either file."
      : "Active and disabled content packages detected. Steam may have updated AC EVO while the package was disabled. Select Advanced again for guided manual cleanup before running the full extraction. LiveryLab will not delete, rename or move either file.";
    const active = data.activePackage || {};
    const disabled = Array.isArray(data.disabledPackages) ? data.disabledPackages : [];
    target.title = [
      `Active: ${active.path || data.activePath || "-"} | ${formatUpdateBackupBytes(active.size || 0)} | modified ${active.modifiedAt || "unknown"}`,
      ...disabled.map((item) => `Renamed: ${item.path || "-"} | ${formatUpdateBackupBytes(item.size || 0)} | modified ${item.modifiedAt || "unknown"}`),
    ].join("\n");
    if (button) button.hidden = true;
    return;
  }
  if (officialMode) {
    if (data.disabledAmbiguous) {
      target.className = "root-status is-error";
      target.textContent = "Multiple valid renamed AC EVO content packages were found. Keep only the package that should be restored.";
    } else if (data.activeExists) {
      target.className = "root-status is-ok";
      target.textContent = "content.kspkg is active and ready for the Official runtime.";
    } else if (data.disabledExists) {
      target.className = "root-status is-warning";
      target.textContent = `Valid renamed content package found: ${fileNameOf(data.disabledPath || "")}. It can be restored when preparing the Official runtime.`;
    } else {
      target.className = contentExtracted ? "root-status is-warning" : "root-status is-error";
      target.textContent = "No valid AC EVO content package was found.";
    }
    if (button) button.hidden = true;
    return;
  }
  if (data.activeExists && contentExtracted && advancedContentNeedsFullExtraction()) {
    const setup = currentAdvancedContentSetup();
    target.className = "root-status is-error";
    target.textContent = "Updated content.kspkg detected. Run Extract all again before enabling the Advanced runtime.";
    target.title = String(setup?.reason || "The extracted Advanced content no longer matches the active package.");
    if (button) {
      button.hidden = false;
      button.disabled = true;
      button.title = "Run Extract all again before disabling content.kspkg.";
    }
    return;
  }
  if (data.activeExists) {
    target.className = "root-status is-warning";
    target.textContent = contentExtracted
      ? "content.kspkg is still active. Disable or rename it so AC EVO uses the extracted content folder."
      : "content.kspkg is active. Extract the package first, then disable or rename it.";
    if (button) button.hidden = !contentExtracted;
  } else if (data.disabledExists) {
    target.className = "root-status is-ok";
    target.textContent = `content.kspkg disabled or renamed: ${fileNameOf(data.disabledPath || "") || "renamed by LiveryLab"}`;
    if (button) button.hidden = true;
  } else {
    target.className = contentExtracted ? "root-status is-ok" : "root-status is-warning";
    target.textContent = contentExtracted
      ? "No active content.kspkg found. Extracted content can be used by the game."
      : "No active content.kspkg found. Extract the package before scanning or editing assets.";
    if (button) button.hidden = true;
  }
}

async function updateContentPackageStatus(root = "") {
  const normalized = normalizeRootPath(root || $("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || "");
  if (!normalized) {
    state.contentPackageStatus = null;
    state.multiplayerSafetyStatus = null;
    state.officialRuntimeSafetyStatus = null;
    renderContentPackageStatus(null);
    renderMultiplayerSafetyStatus(null);
    renderOfficialRuntimeStatus();
    syncHomePackageStatus();
    return null;
  }
  const requestId = ++state.contentPackageStatusRequestId;
  renderContentPackageStatus(null);
  try {
    const result = await window.pywebview.api.get_content_package_state(normalized);
    if (requestId !== state.contentPackageStatusRequestId) return state.contentPackageStatus;
    if (!result.ok) {
      state.contentPackageStatus = null;
      renderContentPackageStatus(null);
      syncHomePackageStatus();
      return null;
    }
    state.contentPackageStatus = result.data || null;
    syncHomePackagePathFromStatus(state.contentPackageStatus);
    renderContentPackageStatus(state.contentPackageStatus);
    syncHomePackageStatus();
    await refreshMultiplayerSafetyStatus(normalized);
    renderOfficialRuntimeStatus();
    applySetupGateStatus();
    return state.contentPackageStatus;
  } catch (error) {
    if (requestId !== state.contentPackageStatusRequestId) return state.contentPackageStatus;
    state.contentPackageStatus = null;
    state.multiplayerSafetyStatus = null;
    renderContentPackageStatus(null);
    renderMultiplayerSafetyStatus(null);
    renderOfficialRuntimeStatus();
    syncHomePackageStatus();
    return null;
  }
}

function renderRootStatus(data = null) {
  state.rootStatus = data;
  const targets = [$("homeRootStatus"), $("settingsRootStatus")].filter(Boolean);
  const officialScope = currentWorkflowScope() === "official";
  let className = "root-status is-unknown";
  let text = "Root writable: -";
  let homeClassName = officialScope ? "root-status is-unknown" : className;
  let homeText = officialScope ? "AC EVO game folder: not checked" : text;
  let title = "";
  if (data) {
    const warnings = Array.isArray(data.warnings) ? data.warnings : [];
    const rootWritable = Boolean(data.rootWritable ?? data.writeReady);
    const packageExtractReady = Boolean(data.packageExtractReady);
    const rootPlausible = Boolean(data.exists && (data.contentCarsExists || data.contentPackageAvailable || data.contentPackageExists));
    if (data.writeReady) {
      className = warnings.length ? "root-status is-warning" : "root-status is-ok";
    } else if (packageExtractReady || rootPlausible) {
      className = "root-status is-warning";
    } else {
      className = "root-status is-error";
    }
    text = rootWritable ? "Root writable: yes" : "Root writable: no";
    const rootExists = Boolean(data.exists);
    const stepLabel = data.writeReady
      ? (warnings.length ? "Warnings" : "Writable")
      : (rootPlausible ? "Ready" : (packageExtractReady ? "Extract Ready" : (rootExists ? "Needs content" : "Blocked")));
    const stepClass = data.writeReady
      ? (warnings.length ? "is-warning" : "is-ok")
      : (rootPlausible ? "is-ok" : (packageExtractReady || rootExists ? "is-warning" : "is-error"));
    setHomeStepStatus("homeRootStepStatus", stepLabel, stepClass);
    if (data.summary) text += ` - ${data.summary}`;
    if (warnings.length) text += ` - ${warnings.join(" ")}`;
    if (officialScope) {
      const packageConflict = Boolean(data.contentPackageMixed || data.contentPackageAmbiguous);
      const officialFolderReady = Boolean(!packageConflict && data.exists && (data.contentPackageAvailable || data.contentPackageExists || data.contentCarsExists));
      homeClassName = officialFolderReady ? "root-status is-ok" : (data.exists ? "root-status is-warning" : "root-status is-error");
      if (packageConflict) {
        homeClassName = "root-status is-error";
        homeText = data.contentPackageMixed
          ? "Active and disabled AC EVO content packages detected. Resolve the package conflict below."
          : "Multiple valid renamed AC EVO content packages detected. Resolve the package conflict below.";
      } else {
        homeText = officialFolderReady
          ? "AC EVO game folder ready"
          : (data.exists ? "Selected folder does not contain a valid AC EVO content package." : "AC EVO game folder not found.");
      }
      setHomeStepStatus(
        "homeRootStepStatus",
        officialFolderReady ? "Ready" : (packageConflict ? "Package conflict" : (data.exists ? "Needs content package" : "Blocked")),
        officialFolderReady ? "is-ok" : (packageConflict ? "is-error" : (data.exists ? "is-warning" : "is-error"))
      );
    } else {
      homeClassName = className;
      homeText = text;
    }
    title = [
      data.root || "",
      ...(data.checks || []).map((row) => `${row.key}: ${row.writable ? "writable" : "not writable"} (${row.message || "-"})`),
    ].filter(Boolean).join("\n");
  } else {
    setHomeStepStatus(
      "homeRootStepStatus",
      state.root ? "Checking" : (officialScope ? "Folder not selected" : "Not set"),
      state.root ? "is-warning" : "is-pending"
    );
  }
  if (!targets.length) {
    applySetupGateStatus();
    return;
  }
  targets.forEach((target) => {
    const homeOfficialTarget = officialScope && target.id === "homeRootStatus";
    target.className = homeOfficialTarget ? homeClassName : className;
    target.textContent = homeOfficialTarget ? homeText : text;
    target.title = title;
  });
  syncHomePackageStatus();
  renderContentPackageStatus(state.contentPackageStatus);
  renderMultiplayerSafetyStatus(state.multiplayerSafetyStatus);
  applySetupGateStatus();
}

function scheduleRootStatusCheck(root = state.root) {
  window.clearTimeout(state.rootStatusTimer);
  state.rootStatusTimer = window.setTimeout(() => updateRootStatus(root), 180);
}

async function updateRootStatus(root = state.root) {
  const normalized = normalizeRootPath(root || "");
  if (!normalized) {
    renderRootStatus(null);
    return null;
  }
  const requestId = ++state.rootStatusRequestId;
  const targets = [$("homeRootStatus"), $("settingsRootStatus")].filter(Boolean);
  setHomeStepStatus("homeRootStepStatus", "Checking", "is-warning");
  targets.forEach((target) => {
    target.className = "root-status is-unknown";
    target.textContent = currentWorkflowScope() === "official" && target.id === "homeRootStatus"
      ? "Checking AC EVO game folder..."
      : "Root writable: checking...";
    target.title = normalized;
  });
  try {
    const result = await window.pywebview.api.check_root_status(normalized);
    if (requestId !== state.rootStatusRequestId) return state.rootStatus;
    if (!result.ok) {
      const data = { root: normalized, writeReady: false, summary: result.message || "Root check failed", warnings: [], checks: [] };
      renderRootStatus(data);
      await updateContentPackageStatus(normalized);
      return data;
    }
    renderRootStatus(result.data || null);
    await updateContentPackageStatus(normalized);
    return result.data || null;
  } catch (error) {
    if (requestId !== state.rootStatusRequestId) return state.rootStatus;
    const data = { root: normalized, writeReady: false, summary: error?.message || String(error), warnings: [], checks: [] };
    renderRootStatus(data);
    await updateContentPackageStatus(normalized);
    return data;
  }
}

function storageHealthState(row = {}) {
  if (row.key === "longPaths") {
    const ready = typeof row.ready === "boolean" ? row.ready : (row.required !== true && row.enabled === true);
    if (ready) return { className: "is-ok", label: "Ready" };
    return {
      className: "is-error",
      label: row.available === false ? "Check unavailable" : "Required",
    };
  }
  if (row.key === "gameRoot") {
    if (!row.root) return { className: "is-warning", label: "Not selected" };
    return row.writeReady ? { className: "is-ok", label: "Writable" } : { className: "is-error", label: "Blocked" };
  }
  if (row.key === "texconv") {
    return row.exists ? { className: "is-ok", label: "Found" } : { className: "is-error", label: "Missing" };
  }
  if (!row.exists) return { className: "is-error", label: "Missing" };
  return row.writable ? { className: "is-ok", label: "Writable" } : { className: "is-error", label: "Blocked" };
}

function appendStorageHealthRow(list, row = {}) {
  const status = storageHealthState(row);
  const item = document.createElement("div");
  item.className = `home-health-row ${status.className}`;
  const badge = document.createElement("span");
  badge.className = "home-health-badge";
  badge.textContent = status.className === "is-ok" ? "OK" : status.className === "is-warning" ? "?" : "!";
  const body = document.createElement("div");
  body.className = "home-health-body";
  const title = document.createElement("div");
  title.className = "home-health-title";
  const label = document.createElement("strong");
  label.textContent = row.label || row.key || "Path";
  const stateLabel = document.createElement("span");
  stateLabel.textContent = status.label;
  title.append(label, stateLabel);
  const role = document.createElement("p");
  role.textContent = row.role || row.summary || row.message || "";
  const path = document.createElement("code");
  path.textContent = row.path || row.root || "-";
  const detail = document.createElement("small");
  detail.textContent = row.summary || row.message || "";
  body.append(title, role, path, detail);
  item.append(badge, body);
  list.appendChild(item);
}

function storageHealthRows(data = null) {
  if (!data) return [];
  const folders = Array.isArray(data.folders) ? data.folders : [];
  return [
    ...folders,
    {
      key: "gameRoot",
      label: "ACE Game Root",
      role: "Only required for Activate in Game, Remove from Game and other game-file writes.",
      ...(data.gameRoot || {}),
    },
    data.texconv || { key: "texconv", label: "texconv.exe", exists: false, message: "Not found" },
  ];
}

function showLongPathsSetupNotice(data = null, { force = false } = {}) {
  const longPaths = data?.longPaths;
  const ready = longPaths && (typeof longPaths.ready === "boolean" ? longPaths.ready : (longPaths.required !== true && longPaths.enabled === true));
  if (!longPaths || ready || (state.longPathsNoticeShown && !force)) return;
  state.longPathsNoticeShown = true;
  const runtime = longPaths.runtime && typeof longPaths.runtime === "object" ? longPaths.runtime : {};
  const registry = longPaths.registry && typeof longPaths.registry === "object" ? longPaths.registry : {};
  showStatusModal({
    type: "error",
    title: "Windows Long Path Support Required",
    subtitle: "Setup is blocked for both workflows",
    messageHtml: [
      "<span>LiveryLab could not complete a real long-path file test. Official and Advanced setup remain locked until Windows Long Paths is available to the running app.</span>",
      '<span class="home-step-actions">',
      '<button type="button" data-long-paths-action="folder">Open BAT Location</button>',
      '<button type="button" data-long-paths-action="google">Search on Google</button>',
      '<button class="primary" type="button" data-long-paths-action="recheck">Check Again</button>',
      "</span>",
    ].join(""),
    details: [
      "Required fix:",
      "1. Close LiveryLab.",
      "2. Run Enable_Long_Windows_Paths.bat as administrator.",
      "3. Restart Windows.",
      "4. Start LiveryLab again.",
      "",
      "Manual alternative:",
      "If you do not want to use the included BAT file, search online for how to enable Win32 long paths in Windows, apply the setting manually, and restart Windows.",
      "",
      `LongPathsEnabled: ${registry.enabled ? "enabled" : registry.available === false ? "unavailable" : "disabled"}`,
      `Runtime path test: ${runtime.available ? "passed" : "failed"}`,
      runtime.probeError ? `Runtime error: ${runtime.probeError}` : "",
    ].join("\n"),
    technicalDetails: runtime.probeCleanupError || longPaths.detail || "",
  });
}

async function handleLongPathsSetupAction(action = "") {
  const clean = String(action || "").trim().toLowerCase();
  if (clean === "recheck") {
    state.longPathsNoticeShown = false;
    await refreshStorageHealth({ silent: true, root: $("homeRootPath")?.value.trim() || state.root || "" });
    if (setupGateLongPathsReady()) closeStatusModal();
    return;
  }
  if (!["folder", "google"].includes(clean)) return;
  if (typeof window.pywebview?.api?.open_long_paths_help !== "function") {
    showStatusModal({
      type: "error",
      title: "Windows Long Path Support",
      subtitle: "Help action unavailable",
      error: "This runtime does not expose the Long Paths help API. A rebuild is required.",
    });
    return;
  }
  const result = await window.pywebview.api.open_long_paths_help(clean);
  if (!result?.ok) {
    showResult(result || { ok: false, message: "Long Paths help could not be opened." }, "Long Paths help failed", "open_long_paths_help");
  } else {
    log(result.message || "Long Paths help opened");
  }
}

function renderStorageHealthSummary(data = null) {
  const summary = $("homeStorageHealthSummary");
  if (!summary) return;
  summary.innerHTML = "";
  if (state.storageHealthLoading) {
    summary.innerHTML = '<span class="home-health-summary-pill is-warning">Checking...</span>';
    setHomeStepStatus("homeStorageStepStatus", "Checking", "is-warning");
    return;
  }
  if (!data) {
    summary.innerHTML = '<span class="home-health-summary-pill is-pending">Run health check</span>';
    setHomeStepStatus("homeStorageStepStatus", "Not checked", "is-pending");
    return;
  }
  const rows = storageHealthRows(data);
  const counts = rows.reduce((acc, row) => {
    const status = storageHealthState(row);
    if (status.className === "is-ok") acc.ok += 1;
    else if (status.className === "is-warning") acc.warning += 1;
    else acc.error += 1;
    return acc;
  }, { ok: 0, warning: 0, error: 0 });
  const overallClass = counts.error ? "is-error" : (counts.warning ? "is-warning" : "is-ok");
  const overallLabel = counts.error ? `${counts.error} issue${counts.error === 1 ? "" : "s"}` : (counts.warning ? `${counts.warning} warning${counts.warning === 1 ? "" : "s"}` : "All checks OK");
  setHomeStepStatus("homeStorageStepStatus", counts.error ? "Issues" : (counts.warning ? "Warnings" : "OK"), overallClass);
  [
    [overallLabel, overallClass],
    [`OK: ${counts.ok}`, "is-ok"],
    [`Warnings: ${counts.warning}`, counts.warning ? "is-warning" : "is-pending"],
    [`Issues: ${counts.error}`, counts.error ? "is-error" : "is-pending"],
  ].forEach(([label, className]) => {
    const pill = document.createElement("span");
    pill.className = `home-health-summary-pill ${className}`;
    pill.textContent = label;
    summary.appendChild(pill);
  });
}

function renderStorageHealthWarnings(data = null) {
  const list = $("homeStorageHealthWarnings");
  if (!list) return;
  list.innerHTML = "";
  list.className = "home-health-list home-health-warnings";
  if (state.storageHealthLoading || !data) {
    list.hidden = true;
    return;
  }
  const warningRows = storageHealthRows(data).filter((row) => storageHealthState(row).className === "is-warning");
  list.hidden = !warningRows.length;
  warningRows.forEach((row) => appendStorageHealthRow(list, row));
}

function renderStorageHealth(data = null) {
  state.storageHealth = data;
  renderLongPathsSetupRequirement();
  renderStorageHealthSummary(data);
  renderStorageHealthWarnings(data);
  const list = $("homeStorageHealthList");
  if (!list) return;
  list.innerHTML = "";
  list.className = "home-health-list";
  if (state.storageHealthLoading) {
    list.classList.add("is-idle");
    const empty = document.createElement("div");
    empty.className = "home-health-empty";
    empty.textContent = t("storage_health_checking");
    list.appendChild(empty);
    applySetupGateStatus();
    return;
  }
  if (!data) {
    list.classList.add("is-idle");
    const empty = document.createElement("div");
    empty.className = "home-health-empty";
    empty.textContent = t("storage_health_pending");
    list.appendChild(empty);
    applySetupGateStatus();
    return;
  }
  storageHealthRows(data)
    .filter((row) => storageHealthState(row).className !== "is-warning")
    .forEach((row) => appendStorageHealthRow(list, row));
  applySetupGateStatus();
}

async function refreshStorageHealth({ silent = false, root = "" } = {}) {
  const requestId = ++state.storageHealthRequestId;
  state.storageHealthLoading = true;
  renderStorageHealth(state.storageHealth);
  let result = null;
  try {
    const normalizedRoot = normalizeRootPath(root || $("homeRootPath")?.value || state.root || "");
    result = await window.pywebview.api.get_storage_health(normalizedRoot);
    if (requestId !== state.storageHealthRequestId) return result;
    if (!result?.ok) {
      state.storageHealth = null;
      if (!silent) showResult(result, "Storage health check failed", "get_storage_health");
      return result;
    }
    state.storageHealth = result.data || null;
    if (!silent) showResult(result, "Storage health checked", "get_storage_health");
    showLongPathsSetupNotice(state.storageHealth);
    return result;
  } catch (error) {
    if (requestId !== state.storageHealthRequestId) return null;
    state.storageHealth = null;
    if (!silent) {
      showStatusModal({
        type: "error",
        title: t("storage_health"),
        subtitle: "Health check failed",
        error: error?.message || String(error),
      });
    } else {
      log(`Storage health check failed: ${error?.message || String(error)}`, true);
    }
    return null;
  } finally {
    if (requestId === state.storageHealthRequestId) {
      state.storageHealthLoading = false;
      renderStorageHealth(state.storageHealth);
    }
  }
}

function scheduleStorageHealthCheck(root = state.root) {
  window.clearTimeout(state.storageHealthTimer);
  state.storageHealthTimer = window.setTimeout(() => refreshStorageHealth({ silent: true, root }), 260);
}

async function runStorageHealthCheck() {
  setHomeStepStatus("homeStorageStepStatus", "Checking", "is-warning");
  const summary = $("homeStorageHealthSummary");
  if (summary) summary.innerHTML = '<span class="home-health-summary-pill is-warning">Checking...</span>';
  const warnings = $("homeStorageHealthWarnings");
  if (warnings) {
    warnings.innerHTML = "";
    warnings.hidden = true;
  }
  const list = $("homeStorageHealthList");
  if (list) {
    list.innerHTML = `<div class="home-health-empty">${t("storage_health_checking")}</div>`;
    list.className = "home-health-list is-idle";
  }
  try {
    const root = normalizeRootPath($("homeRootPath")?.value || state.root || "");
    await refreshStorageHealth({ silent: false, root });
  } catch (error) {
    showStatusModal({
      type: "error",
      title: t("storage_health"),
      subtitle: "Health check failed",
      error: error?.message || String(error),
    });
  }
}

function runtimeProfileLabel(runtime = "") {
  const key = String(runtime || "unknown").trim().toLowerCase();
  if (key === "official") return "Official mode ready";
  if (key === "advanced") return "Advanced/extracted mode";
  if (key === "mixed") return "Mixed state";
  return "Unknown";
}

function runtimeProfileClass(data = {}) {
  const runtime = String(data.runtime || "unknown").trim().toLowerCase();
  if (runtime === "official" || runtime === "advanced") return "is-ok";
  if (runtime === "mixed") return "is-error";
  return (Array.isArray(data.blockers) && data.blockers.length) ? "is-error" : "is-warning";
}

function runtimeProfileMatchesRoot(profile = null, root = "") {
  if (!profile || typeof profile !== "object") return false;
  const expected = normalizeRootPath(root || state.root || "");
  if (!expected) return true;
  return normalizeRootPath(profile.gameRoot || "") === expected;
}

function appendRuntimeProfileRow(list, { label = "Signal", status = "Info", className = "is-warning", message = "", path = "" } = {}) {
  const item = document.createElement("div");
  item.className = `home-health-row ${className}`;
  item.innerHTML = `
    <span class="home-health-badge">${className === "is-ok" ? "OK" : className === "is-error" ? "!" : "?"}</span>
    <div class="home-health-body">
      <div class="home-health-title"><strong>${escapeHtml(label)}</strong><span>${escapeHtml(status)}</span></div>
      <p>${escapeHtml(message || "")}</p>
      ${path ? `<code>${escapeHtml(path)}</code>` : ""}
    </div>
  `;
  list.appendChild(item);
}

function renderRuntimeProfile(data = state.runtimeProfile) {
  state.runtimeProfile = data;
  const summary = $("runtimeProfileSummary");
  const warnings = $("runtimeProfileWarnings");
  const details = $("runtimeProfileDetails");

  if (summary) {
    summary.innerHTML = "";
    if (state.runtimeProfileLoading) {
      summary.innerHTML = '<span class="home-health-summary-pill is-warning">Checking...</span>';
    } else if (!data) {
      summary.innerHTML = '<span class="home-health-summary-pill is-pending">Not checked</span>';
    } else {
      const runtimeClass = runtimeProfileClass(data);
      [
        [runtimeProfileLabel(data.runtime), runtimeClass],
        [data.readyForOfficialGameWrites ? "Official writes ready" : "Official writes blocked", data.readyForOfficialGameWrites ? "is-ok" : "is-warning"],
        [data.readyForAdvancedGameWrites ? "Advanced writes ready" : "Advanced writes blocked", data.readyForAdvancedGameWrites ? "is-ok" : "is-warning"],
        [data.requiresRuntimeSwitch ? "Switch needed" : "No switch needed", data.requiresRuntimeSwitch ? "is-error" : "is-ok"],
      ].forEach(([label, className]) => {
        const pill = document.createElement("span");
        pill.className = `home-health-summary-pill ${className}`;
        pill.textContent = label;
        summary.appendChild(pill);
      });
    }
  }

  if (warnings) {
    warnings.innerHTML = "";
    warnings.className = "home-health-list home-health-warnings";
    const blockers = data && Array.isArray(data.blockers) ? data.blockers : [];
    const warningRows = data && Array.isArray(data.warnings) ? data.warnings : [];
    warnings.hidden = !blockers.length && !warningRows.length;
    blockers.forEach((message) => appendRuntimeProfileRow(warnings, {
      label: "Runtime blocker",
      status: "Blocked",
      className: "is-error",
      message,
    }));
    warningRows.forEach((message) => appendRuntimeProfileRow(warnings, {
      label: "Runtime warning",
      status: "Warning",
      className: "is-warning",
      message,
    }));
  }

  if (!details) return;
  details.innerHTML = "";
  details.className = "home-health-list";
  if (state.runtimeProfileLoading) {
    details.classList.add("is-idle");
    details.innerHTML = '<div class="home-health-empty">Checking runtime profile...</div>';
    return;
  }
  if (!data) {
    details.classList.add("is-idle");
    details.innerHTML = '<div class="home-health-empty">Refresh runtime to inspect package, official folder and extracted-content signals.</div>';
    return;
  }

  const pkg = data.package && typeof data.package === "object" ? data.package : {};
  const official = data.official && typeof data.official === "object" ? data.official : {};
  const savedCars = data.savedCars && typeof data.savedCars === "object" ? data.savedCars : {};
  const advanced = data.advanced && typeof data.advanced === "object" ? data.advanced : {};
  const reasons = Array.isArray(data.reasons) ? data.reasons : [];
  appendRuntimeProfileRow(details, {
    label: "Package state",
    status: pkg.activeExists ? "content.kspkg active" : (pkg.disabledExists ? "package disabled" : "package unknown"),
    className: pkg.activeExists || pkg.disabledExists ? "is-ok" : "is-warning",
    message: `active=${pkg.activeExists === true}, disabled=${pkg.disabledExists === true}, loose content/cars=${pkg.contentCarsExists === true}`,
    path: data.gameRoot || "",
  });
  appendRuntimeProfileRow(details, {
    label: "Official folders",
    status: official.externalLiveriesRootExists ? "ExternalLiveries found" : "ExternalLiveries missing",
    className: official.externalLiveriesRootExists ? "is-ok" : "is-warning",
    message: `external liveries=${Number(official.externalLiveryCount || 0)}, saved cars roots=${Number(official.savedCarsRootCount || 0)}, custom saved cars=${Number(official.customSavedCarFileCount || 0)}`,
    path: official.externalLiveriesRoot || official.savedGamesAceRoot || "",
  });
  appendRuntimeProfileRow(details, {
    label: "SavedCars",
    status: Number(savedCars.rootCount || 0) ? "Profile SavedCars found" : "No SavedCars root",
    className: Number(savedCars.rootCount || 0) ? "is-ok" : "is-warning",
    message: `files=${Number(savedCars.fileCount || 0)}, custom=${Number(savedCars.customFileCount || 0)}, hidden garage=${Number(savedCars.hiddenGarageCount || 0)}`,
    path: (savedCars.roots || []).join(" | "),
  });
  appendRuntimeProfileRow(details, {
    label: "Advanced artifacts",
    status: Number(advanced.contentCarsCount || 0) ? "Loose content/cars found" : "No loose content/cars",
    className: Number(advanced.contentCarsCount || 0) ? "is-ok" : "is-warning",
    message: `cars=${Number(advanced.contentCarsCount || 0)}, custom grid bridge=${advanced.customGridsBridgePresent === true}, UI hook=${advanced.uiHookPresent === true}`,
  });
  if (reasons.length) {
    appendRuntimeProfileRow(details, {
      label: "Classification reasons",
      status: "Signals",
      className: "is-ok",
      message: reasons.join(" "),
    });
  }
}

async function refreshRuntimeProfile({ silent = false, root = "" } = {}) {
  const requestId = ++state.runtimeProfileRequestId;
  state.runtimeProfileLoading = true;
  renderRuntimeProfile(state.runtimeProfile);
  try {
    if (typeof window.pywebview?.api?.inspect_runtime_profile !== "function") {
      throw new Error("Runtime profile API is not available.");
    }
    const normalizedRoot = normalizeRootPath(root || $("homeRootPath")?.value || $("rootPath")?.value || state.root || "");
    const result = await window.pywebview.api.inspect_runtime_profile({ root: normalizedRoot });
    if (requestId !== state.runtimeProfileRequestId) return result;
    if (!result?.ok) {
      state.runtimeProfile = null;
      if (!silent) showResult(result, "Runtime profile check failed", "inspect_runtime_profile");
      return result;
    }
    state.runtimeProfile = result.data || null;
    if (state.mySkinsLoaded && state.mySkinSelectedKey) {
      renderMySkins();
    }
    if (!silent) showResult(result, "Runtime profile checked", "inspect_runtime_profile");
    return result;
  } catch (error) {
    if (requestId !== state.runtimeProfileRequestId) return null;
    state.runtimeProfile = null;
    if (!silent) {
      showStatusModal({
        type: "error",
        title: "Runtime Profile",
        subtitle: "Check failed",
        error: error?.message || String(error),
      });
    } else {
      log(`Runtime profile check failed: ${error?.message || String(error)}`, true);
    }
    return null;
  } finally {
    if (requestId === state.runtimeProfileRequestId) {
      state.runtimeProfileLoading = false;
      renderRuntimeProfile(state.runtimeProfile);
    }
  }
}

function officialExternalLiveryStatus(item = {}) {
  if (!item.valid) return { className: "is-error", badge: "!", label: "Invalid" };
  if ((item.errors || []).length) return { className: "is-error", badge: "!", label: "Issues" };
  if (Number(item.missingTextureReferenceCount || 0) > 0 || (item.warnings || []).length) {
    return { className: "is-warning", badge: "?", label: "Warnings" };
  }
  return { className: "is-ok", badge: "OK", label: "Valid" };
}

function renderOfficialExternalLiveries(data = null) {
  state.officialExternalLiveries = data;
  const summary = $("officialLiveriesSummary");
  const paths = $("officialLiveriesPaths");
  const warnings = $("officialLiveriesWarnings");
  const list = $("officialLiveriesList");

  if (summary) {
    summary.innerHTML = "";
    if (state.officialExternalLiveriesLoading) {
      summary.innerHTML = '<span class="home-health-summary-pill is-warning">Scanning...</span>';
    } else if (!data) {
      summary.innerHTML = '<span class="home-health-summary-pill is-pending">Run scan</span>';
    } else {
      const info = data.summary && typeof data.summary === "object" ? data.summary : {};
      [
        [`Folders: ${info.count || 0}`, "is-pending"],
        [`Valid: ${info.validCount || 0}`, Number(info.validCount || 0) ? "is-ok" : "is-pending"],
        [`Linked cars: ${info.linkedSavedCarCount || 0}`, Number(info.linkedSavedCarCount || 0) ? "is-ok" : "is-pending"],
        [`Invalid: ${info.invalidCount || 0}`, Number(info.invalidCount || 0) ? "is-error" : "is-pending"],
      ].forEach(([label, className]) => {
        const pill = document.createElement("span");
        pill.className = `home-health-summary-pill ${className}`;
        pill.textContent = label;
        summary.appendChild(pill);
      });
    }
  }

  if (paths) {
    const roots = state.officialStorageRoots && typeof state.officialStorageRoots === "object" ? state.officialStorageRoots : {};
    const pathRows = [
      ["ExternalLiveries", data?.externalLiveriesRoot || ""],
      ["Saved Games ACE", data?.savedGamesAceRoot || ""],
      ["OfficialLiveryLibrary", roots.officialLiveryLibraryRoot || ""],
      ["Official PreviewAssets", roots.officialPreviewAssetsRoot || ""],
      ["OfficialLiveryProjects", roots.officialLiveryProjectsRoot || ""],
      ["OfficialLiveryPackageExport", roots.officialLiveryPackageExportRoot || ""],
    ].filter((row) => String(row[1] || "").trim());
    paths.innerHTML = pathRows.map(([label, value]) => `
      <div><strong>${escapeHtml(label)}</strong><code>${escapeHtml(value || "-")}</code></div>
    `).join("");
  }

  if (warnings) {
    warnings.innerHTML = "";
    warnings.className = "home-health-list home-health-warnings";
    const rows = data && Array.isArray(data.warnings) ? data.warnings : [];
    warnings.hidden = !rows.length;
    rows.forEach((message) => {
      const item = document.createElement("div");
      item.className = "home-health-row is-warning";
      item.innerHTML = `
        <span class="home-health-badge">?</span>
        <div class="home-health-body">
          <div class="home-health-title"><strong>Official livery scan</strong><span>Warning</span></div>
          <p>${escapeHtml(message)}</p>
        </div>
      `;
      warnings.appendChild(item);
    });
  }

  if (!list) return;
  list.innerHTML = "";
  list.className = "home-health-list official-liveries-list";
  if (state.officialExternalLiveriesLoading) {
    list.classList.add("is-idle");
    list.innerHTML = '<div class="home-health-empty">Scanning official external livery folders...</div>';
    return;
  }
  if (!data) {
    list.classList.add("is-idle");
    list.innerHTML = '<div class="home-health-empty">Run the scan to inspect official external livery folders.</div>';
    return;
  }
  const items = Array.isArray(data.items) ? data.items : [];
  if (!items.length) {
    list.classList.add("is-idle");
    list.innerHTML = '<div class="home-health-empty">No official external livery folders were found.</div>';
    return;
  }
  items.forEach((item) => {
    const status = officialExternalLiveryStatus(item);
    const descriptor = item.descriptor && typeof item.descriptor === "object" ? item.descriptor : {};
    const savedCar = item.savedCar && typeof item.savedCar === "object" ? item.savedCar : {};
    const textureSet = descriptor.textureSet && typeof descriptor.textureSet === "object"
      ? descriptor.textureSet
      : (item.textureSet && typeof item.textureSet === "object" ? item.textureSet : {});
    const messages = [...(item.errors || []), ...(item.warnings || [])].slice(0, 4);
    const row = document.createElement("article");
    row.className = `home-health-row official-livery-row ${status.className}`;
    row.innerHTML = `
      <div class="home-health-badge">${escapeHtml(status.badge)}</div>
      <div class="home-health-body">
        <strong>${escapeHtml(descriptor.displayName || item.displayName || item.folderName || "External livery")}</strong>
        <p>${escapeHtml(status.label)} | saved car ${savedCar.found || descriptor.savedCarFound ? "found" : "not found"} | source PNG ${Number(textureSet.sourceTextureCount || 0)} | refs ${Number(textureSet.referencedTextureCount || 0)}</p>
        <small>${escapeHtml(descriptor.id || "")}</small>
        <small>${escapeHtml(descriptor.externalLiveryFolder || item.path || "")}</small>
        <small>car_guid: ${escapeHtml(descriptor.carGuid || item.carGuid || "-")}</small>
        <small>batches: ${escapeHtml((descriptor.materialBatchNames || item.materialBatchNames || []).join(", ") || "-")}</small>
        ${messages.length ? `<ul class="official-livery-message-list">${messages.map((message) => `<li>${escapeHtml(message)}</li>`).join("")}</ul>` : ""}
      </div>
    `;
    list.appendChild(row);
  });
}

async function refreshOfficialExternalLiveries({ silent = false } = {}) {
  const requestId = ++state.officialExternalLiveriesRequestId;
  state.officialExternalLiveriesLoading = true;
  renderOfficialExternalLiveries(state.officialExternalLiveries);
  try {
    if (typeof window.pywebview?.api?.discover_official_external_liveries !== "function") {
      throw new Error("Official external livery scan API is not available.");
    }
    const result = await window.pywebview.api.discover_official_external_liveries({ includeInvalid: true });
    if (requestId !== state.officialExternalLiveriesRequestId) return result;
    if (!result?.ok) {
      state.officialExternalLiveries = null;
      if (!silent) showResult(result, "Official external livery scan failed", "discover_official_external_liveries");
      return result;
    }
    state.officialExternalLiveries = result.data || null;
    if (!silent) showResult(result, "Official external livery scan complete", "discover_official_external_liveries");
    return result;
  } catch (error) {
    if (requestId !== state.officialExternalLiveriesRequestId) return null;
    state.officialExternalLiveries = null;
    if (!silent) {
      showStatusModal({
        type: "error",
        title: "Official External Liveries",
        subtitle: "Scan failed",
        error: error?.message || String(error),
      });
    } else {
      log(`Official external livery scan failed: ${error?.message || String(error)}`, true);
    }
    return null;
  } finally {
    if (requestId === state.officialExternalLiveriesRequestId) {
      state.officialExternalLiveriesLoading = false;
      renderOfficialExternalLiveries(state.officialExternalLiveries);
    }
  }
}

function formatUpdateBackupBytes(bytes = 0) {
  const value = Number(bytes || 0);
  if (!Number.isFinite(value) || value <= 0) return "0 B";
  const units = ["B", "KB", "MB", "GB"];
  let current = value;
  let unit = 0;
  while (current >= 1024 && unit < units.length - 1) {
    current /= 1024;
    unit += 1;
  }
  return `${current.toFixed(unit ? 1 : 0)} ${units[unit]}`;
}

function updateBackupStatus(message, type = "info") {
  const node = $("updateBackupStatus");
  if (!node) return;
  node.textContent = message || "";
  node.classList.toggle("is-ok", type === "success");
  node.classList.toggle("is-error", type === "error");
  node.classList.toggle("is-unknown", type !== "success" && type !== "error");
}

function defaultUpdateBackupName() {
  const stamp = new Date().toISOString().slice(0, 19).replaceAll(":", "").replace("T", "_");
  return `LiveryLab_UpdateBackup_${stamp}.zip`;
}

function updateBackupReportLines(data = {}) {
  const manifest = data.manifest && typeof data.manifest === "object" ? data.manifest : {};
  const report = manifest.report && typeof manifest.report === "object" ? manifest.report : data;
  const lines = [];
  if (data.path) lines.push(`File: ${data.path}`);
  if (manifest.appVersion) lines.push(`Source version: ${manifest.appVersion}`);
  if (data.fileCount !== undefined) lines.push(`Files in package: ${data.fileCount}`);
  if (data.includedFiles !== undefined) lines.push(`Included files: ${data.includedFiles}`);
  if (data.importedFiles !== undefined) lines.push(`Imported files: ${data.importedFiles}`);
  if (data.totalBytes !== undefined) lines.push(`Package content: ${formatUpdateBackupBytes(data.totalBytes)}`);
  if (data.includedBytes !== undefined) lines.push(`Included size: ${formatUpdateBackupBytes(data.includedBytes)}`);
  if (data.importedBytes !== undefined) lines.push(`Imported size: ${formatUpdateBackupBytes(data.importedBytes)}`);
  if (report.skippedProjectFiles) lines.push(`Skipped project files: ${report.skippedProjectFiles} (${formatUpdateBackupBytes(report.skippedProjectBytes)})`);
  if (data.skippedFiles) lines.push(`Skipped unsupported package entries: ${data.skippedFiles}`);
  if (data.backupFolder) lines.push(`Previous local data backup: ${data.backupFolder}`);
  const samples = report.skippedSamples || data.skippedSamples || [];
  if (Array.isArray(samples) && samples.length) {
    lines.push("", "Skipped samples:");
    samples.slice(0, 12).forEach((item) => {
      const path = item?.path || "";
      const reason = item?.reason ? ` (${item.reason})` : "";
      const size = item?.size ? ` - ${formatUpdateBackupBytes(item.size)}` : "";
      lines.push(`- ${path}${size}${reason}`);
    });
  }
  return lines;
}

async function createUpdateBackup() {
  const result = await window.pywebview.api.choose_path(
    "save",
    ["LiveryLab Update Backup (*.zip)", "ZIP (*.zip)", "All files (*.*)"],
    defaultUpdateBackupName(),
    state.mySkinExportDir || "",
  );
  if (!showResult(result, t("log_path_selected")) || !result.data?.path) return;
  await withButtonWorking("createUpdateBackup", "Creating...", async () => {
    updateBackupStatus("Creating update backup...");
    const created = await window.pywebview.api.create_update_backup(result.data.path);
    if (!created?.ok) {
      updateBackupStatus(firstMessageLine(created?.message, "Update backup failed."), "error");
      showResult(created, "Update backup failed", "create_update_backup");
      return;
    }
    const data = created.data || {};
    updateBackupStatus(`Created update backup with ${data.includedFiles || 0} files.`, "success");
    showStatusModal({
      type: "success",
      title: "Update Backup",
      subtitle: "Backup created",
      message: "The update backup was created. Editable project source files were not included.",
      details: updateBackupReportLines(data).join("\n"),
      openFolderPath: data.folder || "",
    });
  });
}

async function importUpdateBackup() {
  const picked = await window.pywebview.api.choose_path(
    "file",
    ["LiveryLab Update Backup (*.zip)", "ZIP (*.zip)", "All files (*.*)"],
    "",
    state.mySkinExportDir || "",
  );
  if (!showResult(picked, t("log_path_selected")) || !picked.data?.path) return;
  const inspected = await window.pywebview.api.inspect_update_backup(picked.data.path);
  if (!inspected?.ok) {
    updateBackupStatus(displayErrorFromResult("inspect_update_backup", inspected, "Update backup could not be inspected.").summary, "error");
    showResult(inspected, "Update backup could not be inspected", "inspect_update_backup");
    return;
  }
  const details = [
    ...updateBackupReportLines(inspected.data || {}),
    "",
    "Import will replace local settings, local skins, numberplate templates, driver profiles, custom grids and tyre brands from the package.",
    "Current local data is backed up before import. LiveryProjects work files are not touched.",
  ].join("\n");
  const confirmed = await showConfirmModal({
    type: "warning",
    title: "Import Update Backup",
    subtitle: "Import app data from package",
    message: "This imports settings and installed LiveryLab data from the selected update backup.",
    details,
    confirmLabel: "Import Backup",
    loadingLabel: "Importing...",
    onConfirm: async () => {
      const imported = await window.pywebview.api.import_update_backup(picked.data.path);
      if (!imported?.ok) {
        throw new Error(imported?.message || "Update backup import failed.");
      }
      return imported;
    },
    resolveActionResult: true,
  });
  if (!confirmed || confirmed === "cancel") return;
  if (!confirmed.ok) {
    updateBackupStatus(displayErrorFromResult("import_update_backup", confirmed, "Update backup import failed.").summary, "error");
    showResult(confirmed, "Update backup import failed", "import_update_backup");
    return;
  }
  updateBackupStatus(`Imported ${confirmed.data?.importedFiles || 0} files from update backup.`, "success");
  showStatusModal({
    type: "success",
    title: "Update Backup",
    subtitle: "Backup imported",
    message: "The update backup was imported. Restart LiveryLab if settings or library data do not refresh immediately.",
    details: updateBackupReportLines(confirmed.data || {}).join("\n"),
    openFolderPath: confirmed.data?.backupFolder || "",
  });
}

function formatGameWritePreflightDetails({
  status = null,
  writes = [],
  backupNote = "Existing game files are backed up before overwrite when the workflow can safely create a backup.",
  extraDetails = "",
} = {}) {
  const lines = [
    `Root: ${status?.root || state.root || "-"}`,
    `Root writable: ${(status?.rootWritable ?? status?.writeReady) ? "yes" : "no"}`,
    `Loose content ready: ${(status?.looseContentReady ?? status?.writeReady) ? "yes" : "no"}`,
    `Package extraction ready: ${status?.packageExtractReady ? "yes" : "no"}`,
  ];
  if (status?.summary) lines.push(`Status: ${status.summary}`);
  const warnings = Array.isArray(status?.warnings) ? status.warnings.filter(Boolean) : [];
  if (warnings.length) lines.push("", "Warnings:", ...warnings.map((row) => `- ${row}`));
  const writeRows = (Array.isArray(writes) ? writes : []).map((row) => String(row || "").trim()).filter(Boolean);
  if (writeRows.length) lines.push("", "Planned game writes:", ...writeRows.map((row) => `- ${row}`));
  if (backupNote) lines.push("", "Backups:", backupNote);
  const checks = Array.isArray(status?.checks) ? status.checks : [];
  if (checks.length) {
    lines.push("", "Root checks:");
    checks.forEach((row) => {
      lines.push(`- ${row.key || "path"}: ${row.writable ? "writable" : "not writable"} (${row.message || "-"})`);
    });
  }
  if (String(extraDetails || "").trim()) lines.push("", String(extraDetails).trim());
  return lines.join("\n");
}

function gameWriteBlockedMessage(status = {}) {
  const summary = String(status?.summary || "The selected AC EVO installation folder is not writable.").trim();
  const warnings = Array.isArray(status?.warnings) ? status.warnings.map((row) => String(row || "")) : [];
  const combined = `${summary}\n${warnings.join("\n")}`;
  const accessDenied = /access denied|zugriff verweigert|not writable/i.test(combined);
  const programFiles = Boolean(status?.underProgramFiles) || warnings.some((row) => /program files/i.test(row));
  if (programFiles || accessDenied) {
    return [
      summary,
      "",
      "Windows is blocking writes to the selected AC EVO installation folder.",
      "Start LiveryLab as administrator, or use an AC EVO installation folder with normal write permissions.",
      "",
      "Creating local liveries works without admin rights. Only Activate in Game, Remove from Game and other game-file writes need these permissions.",
    ].join("\n");
  }
  return summary;
}

async function confirmGameWriteAction({
  type = "warning",
  title = "Write game files",
  message = "This action writes to the selected ACE game folder.",
  confirmLabel = "Continue",
  writes = [],
  backupNote = "",
  extraDetails = "",
  root = "",
  loadingLabel = "",
  option = null,
  resolveOptionState = false,
  allowPackageExtractMissingContent = false,
  runtimeGuard = null,
  onConfirm = null,
} = {}) {
  if (runtimeGuard) {
    const guardDetails = Array.isArray(runtimeGuard.details) ? runtimeGuard.details.filter(Boolean) : [];
    if (runtimeGuard.ready !== true) {
      showStatusModal({
        type: "error",
        title,
        subtitle: "Game write blocked",
        error: runtimeGuard.message || "The current runtime blocks this game write.",
        details: guardDetails.join("\n"),
      });
      return false;
    }
    const details = [
      `Runtime: ${runtimeGuard.label || "Official workflow"}`,
      "Official workflow ready: yes",
    ];
    if (guardDetails.length) details.push("", "Runtime details:", ...guardDetails.map((row) => `- ${row}`));
    const writeRows = (Array.isArray(writes) ? writes : []).map((row) => String(row || "").trim()).filter(Boolean);
    if (writeRows.length) details.push("", "Planned writes:", ...writeRows.map((row) => `- ${row}`));
    if (backupNote) details.push("", "Backups:", backupNote);
    if (String(extraDetails || "").trim()) details.push("", String(extraDetails).trim());
    return showConfirmModal({
      type,
      title,
      message,
      details: details.join("\n"),
      confirmLabel,
      loadingLabel,
      option,
      resolveOptionState,
      onConfirm,
    });
  }
  const normalized = normalizeRootPath(root || state.root || $("rootPath")?.value.trim() || "");
  if (!normalized) {
    showStatusModal({
      type: "error",
      title,
      subtitle: "Game write blocked",
      error: "No AC EVO installation folder is selected.",
    });
    return false;
  }
  const status = await updateRootStatus(normalized);
  const details = formatGameWritePreflightDetails({ status, writes, backupNote, extraDetails });
  const ready = allowPackageExtractMissingContent ? status?.packageExtractReady : status?.writeReady;
  if (!ready) {
    showStatusModal({
      type: "error",
      title,
      subtitle: "Game write blocked",
      error: gameWriteBlockedMessage(status),
      details,
    });
    return false;
  }
  return showConfirmModal({
    type,
    title,
    message,
    details,
    confirmLabel,
    loadingLabel,
    option,
    resolveOptionState,
    onConfirm,
  });
}

function pathLooksInsideRoot(path, root = state.root) {
  const value = String(path || "").trim();
  const rootValue = String(root || "").trim();
  if (!value || !rootValue) return false;
  const normalize = (text) => text.replace(/\\/g, "/").replace(/\/+/g, "/").replace(/\/$/, "").toLowerCase();
  const pathKey = normalize(value);
  const appStorageMatch = (state.appStorageRoots || [])
    .map((item) => normalize(item))
    .filter(Boolean)
    .some((storageKey) => pathKey === storageKey || pathKey.startsWith(`${storageKey}/`));
  if (appStorageMatch) return false;
  const rootKey = normalize(rootValue);
  if (pathKey === rootKey || pathKey.startsWith(`${rootKey}/`)) return true;
  return /^(content|system)\//i.test(pathKey);
}

async function confirmFreeOutputIfInGameRoot({
  target = "",
  title = "Write output",
  message = "Write this output file or folder?",
  writes = [],
  backupNote = "Existing output files are backed up when the workflow supports backups.",
  extraDetails = "",
  confirmLabel = "Continue",
  loadingLabel = "",
  allowPackageExtractMissingContent = false,
  onConfirm = null,
} = {}) {
  if (!pathLooksInsideRoot(target)) {
    if (typeof onConfirm === "function") return onConfirm();
    return true;
  }
  return confirmGameWriteAction({
    type: "warning",
    title,
    message,
    writes,
    backupNote,
    extraDetails: [extraDetails, `Target:\n${target}`].filter(Boolean).join("\n\n"),
    confirmLabel,
    loadingLabel,
    allowPackageExtractMissingContent,
    onConfirm,
  });
}

async function runPackageExtraction({ packPath = "", outDir = "", selection = "", root = "", selectedOnly = false } = {}) {
  const apiName = selectedOnly ? "extract_selected_pack_entries" : "extract_pack_file";
  log(`${t("log_running")} ${apiName}...`);
  showPackageExtractModal();
  let result = null;
  try {
    if (selectedOnly) {
      result = await window.pywebview.api.extract_selected_pack_entries(packPath, outDir, selection, root);
    } else {
      result = await window.pywebview.api.extract_pack_file(packPath, outDir, root);
    }
  } catch (error) {
    result = { ok: false, message: error?.message || String(error), error };
  }
  hidePackageExtractModal();
  if (result?.cancelled) {
    log(result.message || "Package extraction cancelled", true);
    showStatusModal({
      type: "info",
      title: "Package Extractor",
      subtitle: "Extraction cancelled",
      message: result.message || "Package extraction was cancelled. Already extracted files remain in the output folder.",
    });
    return result;
  }
  showResult(result, apiName, apiName);
  return result;
}

async function runHomePackageExtraction() {
  if (!requireLongPathsSetupReady()) return;
  const packPath = $("homePackagePath").value.trim();
  const outDir = $("homePackageOutDir").value.trim() || $("homeRootPath").value.trim() || $("rootPath").value.trim();
  if (!packPath || !outDir) {
    syncHomePackageStatus();
    log("Please set the .kspkg and output folder", true);
    return;
  }
  $("homePackageOutDir").value = outDir;
  setHomePackageStatus("Ready", "is-ok");
  const confirmed = await showConfirmModal({
    type: "warning",
    title: "Extract game resources",
    subtitle: "Long running setup step",
    message: "Extract all files from this .kspkg into the selected AC EVO root?",
    details: [
      "This can take around 15-20 minutes depending on your computer.",
      "Progress is measured by processed data and continues updating while large files are extracted.",
      "The dialog also shows current throughput and an estimated remaining time.",
      "",
      `Target:\n${outDir}`,
      "",
      "Package extraction writes ACE files directly and may overwrite matching extracted files.",
    ].join("\n"),
    confirmLabel: "Extract all",
    cancelLabel: "Cancel",
  });
  if (!confirmed) return;
  setHomePackageStatus("Extracting", "is-warning");
  resetPackageProgress();
  const result = await runPackageExtraction({ packPath, outDir, root: $("rootPath").value.trim() });
  if (!result.ok) {
    setHomePackageStatus("Failed", "is-error");
    return;
  }
  setInspectOutputText(result.message || "");
  setHomePackageStatus("Extracted", "is-ok");
  await updateRootStatus($("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || outDir);
  resetPackageProgress();
  const packageState = await updateContentPackageStatus($("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || outDir);
  if (packageState?.activeExists && packageState?.contentCarsExists) {
    closeStatusModal();
    const prepared = await prepareWorkflowRuntime("advanced");
    if (prepared?.changed) {
      await updateRootStatus($("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || outDir);
      await refreshRuntimeProfile({ silent: true, root: $("rootPath")?.value.trim() || outDir });
    }
  }
}

window.LiveryLabEvoUpdateHost = {
  async extractUpdatedContent({ root = "", packagePath = "" } = {}) {
    const normalizedRoot = normalizeRootPath(root || state.root || $("rootPath")?.value.trim() || "");
    const packPath = String(packagePath || "").trim();
    if (!normalizedRoot || !packPath) return { ok: false, message: "Updated package or AC EVO root is missing." };
    const confirmed = await showConfirmModal({
      type: "warning",
      title: "Extract Updated AC EVO Content",
      subtitle: "Full Advanced extraction",
      message: "Extract the updated content.kspkg into the cleaned AC EVO folder?",
      details: "This uses the normal full extraction process and can take around 15-20 minutes on the real game package.",
      confirmLabel: "Extract Updated Content",
      cancelLabel: "Cancel",
    });
    if (!confirmed) return { ok: false, cancelled: true, message: "Extraction cancelled." };
    resetPackageProgress();
    const result = await runPackageExtraction({ packPath, outDir: normalizedRoot, root: normalizedRoot });
    if (result?.ok) {
      await updateRootStatus(normalizedRoot);
      await updateContentPackageStatus(normalizedRoot);
    }
    return result;
  },
  async prepareRuntime(scope = "advanced") {
    return prepareWorkflowRuntime(normalizeWorkflowScope(scope));
  },
  async restoreActiveLiveries(items = []) {
    const restored = [];
    for (const item of Array.isArray(items) ? items : []) {
      const row = { ...item, restored: false, error: "" };
      try {
        const result = await window.pywebview.api.set_my_skin_active({ skinKey: item.skinKey, active: true });
        row.restored = Boolean(result?.ok);
        row.error = result?.ok ? "" : String(result?.message || "Restore failed.");
      } catch (error) {
        row.error = error?.message || String(error);
      }
      restored.push(row);
    }
    await loadMySkins();
    return restored;
  },
  async enterWorkflow(scope = "advanced") {
    return chooseWorkflow(normalizeWorkflowScope(scope));
  },
};

async function disableContentPackageFlow({ promptOnly = false } = {}) {
  if (!requireLongPathsSetupReady()) return false;
  const root = normalizeRootPath($("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || "");
  if (!root) {
    showStatusModal({
      type: "error",
      title: "Disable or rename content.kspkg",
      subtitle: "AC EVO installation folder missing",
      error: "Choose and save the AC EVO installation folder first.",
    });
    return false;
  }
  const currentState = await updateContentPackageStatus(root);
  if (!currentState?.activeExists) {
    showStatusModal({
      type: "success",
      title: "Disable or rename content.kspkg",
      subtitle: "Package already inactive",
      message: "No active content.kspkg was found in the selected AC EVO installation folder.",
    });
    return true;
  }
  if (!currentState.contentCarsExists) {
    showStatusModal({
      type: "error",
      title: "Disable or rename content.kspkg",
      subtitle: "Extract content first",
      error: "The extracted content folder was not found. Extract content.kspkg before disabling the package.",
    });
    return false;
  }
  const preflightResult = await preflightWorkflowRuntime("advanced");
  if (!preflightResult?.ok) {
    showResult(preflightResult || { ok: false, error: "Runtime preflight failed" }, "Runtime switch preflight failed", "preflight_runtime_switch");
    return false;
  }
  if (preflightResult?.ok) {
    state.runtimeSwitchPreflight = preflightResult.data || null;
    renderContentPackageStatus(currentState);
    syncHomePackageStatus();
    if (preflightResult.data?.requiresAdvancedContentSetup || preflightResult.data?.status === "setup_required") {
      const reason = String(
        preflightResult.data?.advancedContentSetup?.reason
        || "Run Extract all again before enabling the Advanced runtime."
      );
      showStatusModal({
        type: "warning",
        title: "Updated content.kspkg detected",
        subtitle: "Full extraction required before Advanced can be enabled",
        message: "LiveryLab kept content.kspkg active so AC EVO does not use outdated extracted files.",
        details: `${reason}\n\nRun Extract all in Game Resources. After it completes, LiveryLab will continue preparing the Advanced runtime.`,
      });
      return false;
    }
  }
  const result = await confirmGameWriteAction({
    type: "warning",
    title: "Disable or rename content.kspkg",
    message: promptOnly
      ? "Rename content.kspkg now so AC EVO uses the extracted content folder?"
      : "Rename content.kspkg so AC EVO uses the extracted content folder?",
    writes: [
      "Rename content.kspkg in place.",
      "Do not copy the package and do not create a backup.",
    ],
    backupNote: "No backup is created because content.kspkg can be very large. The file is only renamed.",
    extraDetails: `Source:\n${currentState.activePath || joinPath(root, "content.kspkg")}`,
    confirmLabel: "Disable / rename package",
    loadingLabel: "Renaming package...",
    onConfirm: async () => {
      log(`${t("log_running")} disable_content_package...`);
      return window.pywebview.api.disable_content_package(root);
    },
  });
  if (!result) return false;
  showResult(result, "disable_content_package", "disable_content_package");
  if (!result.ok) {
    await updateContentPackageStatus(root);
    return false;
  }
  state.contentPackageStatus = result.data || null;
  syncHomePackagePathFromStatus(state.contentPackageStatus);
  renderContentPackageStatus(state.contentPackageStatus);
  syncHomePackageStatus();
  await updateRootStatus(root);
  await refreshMultiplayerSafetyStatus(root);
  return true;
}

const SKIN_TEMPLATE_DEPENDENT_FIELDS = ["skinDesign", "skinActor", "skinVisual", "skinMechanical", "skinCarFinalState"];

function renderSkinVisualVariantInfo(variant = null) {
  const box = $("skinVisualVariantInfo");
  const labelEl = $("skinVisualVariantLabel");
  const metaEl = $("skinVisualVariantMeta");
  if (!box || !labelEl || !metaEl) return;
  if (!variant) {
    box.hidden = true;
    box.className = "skin-visual-variant-info is-empty";
    labelEl.textContent = "Variant could not be detected";
    metaEl.textContent = "";
    return;
  }
  const status = String(variant.status || "").trim().toLowerCase();
  const labels = Array.isArray(variant.labels) ? variant.labels.map((item) => String(item || "").trim()).filter(Boolean) : [];
  const visualRows = Array.isArray(variant.visualVariants) ? variant.visualVariants : [];
  const mechanicalCount = Number(variant.mechanicalCount || 0);
  const visualCount = Number(variant.visualCount || visualRows.length || 0);
  let label = String(variant.label || variant.message || "").trim();
  if (labels.length > 1) label = labels.join(", ");
  if (!label) label = "Variant could not be detected";
  const meta = [];
  if (status === "detected") {
    const sourceNames = visualRows
      .map((row) => String(row.rel || row.path || "").split(/[\\/]/).pop())
      .filter(Boolean);
    if (sourceNames.length === 1) meta.push(`Detected from ${sourceNames[0]}`);
    else if (sourceNames.length > 1) meta.push(`Detected from ${sourceNames.length} visual presets`);
  } else if (status === "fallback") {
    meta.push("Detected from design name");
  }
  if (mechanicalCount > 1) meta.push(`Used by ${mechanicalCount} mechanical presets`);
  else if (mechanicalCount === 1) meta.push("Used by 1 mechanical preset");
  else if (visualCount > 0) meta.push("No linked mechanical preset found");
  box.hidden = false;
  box.className = `skin-visual-variant-info ${status === "detected" || status === "fallback" ? "is-detected" : "is-unknown"}`;
  labelEl.textContent = label;
  metaEl.textContent = meta.join(" · ");
}

function applySkinPaintVariantOption(option = null) {
  if (!option) return;
  const fields = [
    ["skinVisual", option.visualPath || option.visual],
    ["skinMechanical", option.mechanicalPath || option.mechanical],
    ["skinCarFinalState", option.carfinalstatePath || option.carfinalstate],
  ];
  fields.forEach(([id, value]) => {
    const field = $(id);
    if (!field || !value) return;
    field.value = value;
    updatePathFileName(id);
  });
  renderWorkflowCompatibility();
}

function renderSkinPaintVariantOptions(data = null) {
  const row = $("skinPaintVariantRow");
  const select = $("skinPaintVariant");
  if (!row || !select) return;
  const options = Array.isArray(data?.options) ? data.options : [];
  const show = Boolean(data?.ambiguous && options.length > 1);
  state.skinPaintVariantOptions = show ? options : [];
  select.innerHTML = "";
  if (!show) {
    row.hidden = true;
    return;
  }
  options.forEach((option, index) => {
    const item = document.createElement("option");
    const count = Number(option.paintRelevantCount || 0);
    const label = String(option.label || option.visual || `Variant ${index + 1}`).trim();
    item.value = String(index);
    item.textContent = count > 0 ? `${label} (${count} paint parts)` : `${label} (base only)`;
    select.appendChild(item);
  });
  const selectedIndex = Number(data.selectedIndex);
  select.value = String(Number.isInteger(selectedIndex) && selectedIndex >= 0 && selectedIndex < options.length ? selectedIndex : 0);
  row.hidden = false;
}

function clearSkinTemplateDependentFields() {
  SKIN_TEMPLATE_DEPENDENT_FIELDS.forEach((id) => {
    const field = $(id);
    if (!field) return;
    field.value = "";
    updatePathFileName(id);
  });
  renderWorkflowCompatibility();
  renderSkinVisualVariantInfo(null);
  renderSkinPaintVariantOptions(null);
}

async function autofillSkinTemplateFields(showInspect = false) {
  const requestId = ++state.skinTemplateAutofillRequestId;
  const cardesign = $("skinCardesign")?.value.trim() || "";
  clearSkinTemplateDependentFields();
  if (!cardesign || extensionOf(cardesign) !== "cardesign") return;
  const support = await inspectSkinTemplateSupportForPath(cardesign);
  if (requestId !== state.skinTemplateAutofillRequestId || ($("skinCardesign")?.value.trim() || "") !== cardesign) return;
  if (support?.status === "unsupported") {
    const message = skinTemplateUnsupportedText(support);
    log(message, true);
    setInspectOutputText(message);
    renderWorkflowCompatibility();
    return;
  }
  let design = "";
  const inspect = await call("inspect_file", cardesign, $("rootPath").value.trim());
  if (requestId !== state.skinTemplateAutofillRequestId || ($("skinCardesign")?.value.trim() || "") !== cardesign) return;
  if (inspect.ok) {
    const cardesignInfo = inspect.data?.cardesign || {};
    const resolvedDesign = cardesignInfo.designLocal || cardesignInfo.designPath || "";
    if (resolvedDesign) {
      design = resolvedDesign;
      $("skinDesign").value = resolvedDesign;
      updatePathFileName("skinDesign");
    }
  }
  const result = await call("analyze_skin", $("rootPath").value.trim(), cardesign, design);
  if (requestId !== state.skinTemplateAutofillRequestId || ($("skinCardesign")?.value.trim() || "") !== cardesign) return;
  if (!result.ok) return;
  const found = result.data?.found || {};
  if (found.design) $("skinDesign").value = found.design;
  if (found.actor) $("skinActor").value = found.actor;
  if (found.visual) $("skinVisual").value = found.visual;
  if (found.mechanical) $("skinMechanical").value = found.mechanical;
  if (found.carfinalstate) $("skinCarFinalState").value = found.carfinalstate;
  renderSkinVisualVariantInfo(result.data?.visualVariant || { status: "unknown" });
  renderSkinPaintVariantOptions(result.data?.paintVariantOptions || null);
  SKIN_TEMPLATE_DEPENDENT_FIELDS.forEach(updatePathFileName);
  if (showInspect) {
    setInspectOutputText(result.data?.text || "");
    setTab("inspect");
  } else {
    const filled = ["design", "actor", "visual", "mechanical", "carfinalstate"].filter((key) => found[key]).length;
    log(`Skin template fields auto-filled: ${filled}`);
  }
  renderWorkflowCompatibility();
}

async function validateSkinPreviewImage(showSuccess = false) {
  const path = $("skinPreviewImage")?.value.trim() || "";
  if (!path) return true;
  const result = await window.pywebview.api.inspect_preview_image(path);
  if (!showResult(result, "Preview Image", "inspect_preview_image")) return false;
  const info = result.data || {};
  if (!info.valid) {
    showStatusModal({
      type: "error",
      title: "Livery Creator",
      subtitle: "Invalid preview image",
      error: `The preview image must be 1024x1024 pixels. Current size: ${info.width || "?"}x${info.height || "?"}.`,
      details: `Please choose a matching PNG file for the thumbnail.\n\nFile: ${path}`,
    });
    return false;
  }
  if (showSuccess) {
    log(`Preview image valid: ${info.width}x${info.height}`);
  }
  return true;
}

function setDerivedPath(target, sourcePath, extension, overwrite) {
  if (!overwrite && $(target).value.trim()) return;
  if (target === "packageOutDir" && extension === "") {
    const source = String(sourcePath || "").replace(/[\\/]+$/, "");
    const baseDir = source.split(/[\\/]/).slice(0, -1).join("\\");
    setPath(target, baseDir || source);
    return;
  }
  setPath(target, replaceExtension(sourcePath, extension));
}

async function setConvertedDerivedPath(target, kind, sourcePath, extension, overwrite = true, nameSourcePath = sourcePath) {
  if (!overwrite && $(target)?.value.trim()) return;
  const source = String(sourcePath || "").trim();
  if (!source) return;
  const fallback = replaceExtension(nameSourcePath || source, extension);
  try {
    const root = normalizeRootPath($("rootPath")?.value || state.root || "");
    const result = await window.pywebview.api.get_converted_output_root(kind, source, root);
    if (!result.ok || !result.data?.path) {
      log(result.message || "Converted output folder unavailable", true);
      setPath(target, fallback);
      return;
    }
    setPath(target, joinPath(result.data.path, fileNameWithExtension(nameSourcePath || source, extension)));
  } catch (error) {
    log(error?.message || String(error), true);
    setPath(target, fallback);
  }
}

async function setConvertedDerivedFolder(target, kind, sourcePath, subfolder, overwrite = true) {
  if (!overwrite && $(target)?.value.trim()) return;
  const source = String(sourcePath || "").trim();
  if (!source) return;
  try {
    const root = normalizeRootPath($("rootPath")?.value || state.root || "");
    const result = await window.pywebview.api.get_converted_output_root(kind, source, root);
    if (!result.ok || !result.data?.path) {
      log(result.message || "Converted output folder unavailable", true);
      return;
    }
    setPath(target, joinPath(result.data.path, subfolder));
  } catch (error) {
    log(error?.message || String(error), true);
  }
}

async function setMaterialOutputDefaults(materialPath, overwrite = true) {
  await setConvertedDerivedFolder("materialReportDir", "textures", materialPath, "MaterialReports", overwrite);
  await setConvertedDerivedFolder("materialExportDir", "textures", materialPath, "MaterialTextures", overwrite);
}

function numberplateOptionLabel(relPath) {
  const parts = String(relPath || "").split(/[\\/]/).filter(Boolean);
  const file = parts[parts.length - 1] || relPath;
  const group = parts[parts.length - 2] || "";
  const stem = file.replace(/\.texture$/i, "");
  return group ? `${group} / ${stem}` : stem;
}

function renderNumberplateSelect(selectId, values, placeholder) {
  const select = $(selectId);
  if (!select) return;
  const current = select.value || select.dataset.current || "";
  select.innerHTML = "";
  const empty = document.createElement("option");
  empty.value = "";
  empty.textContent = placeholder;
  select.append(empty);
  for (const rel of values || []) {
    const option = document.createElement("option");
    option.value = rel;
    option.textContent = numberplateOptionLabel(rel);
    option.title = rel;
    select.append(option);
  }
  if (current) {
    if (![...select.options].some((option) => option.value === current)) {
      const custom = document.createElement("option");
      custom.value = current;
      custom.textContent = `Current / ${numberplateOptionLabel(current)}`;
      custom.title = current;
      select.append(custom);
    }
    if ([...select.options].some((option) => option.value === current)) {
      select.value = current;
    }
  }
}

function renderNumberplateAssetLibrary() {
  window.LiveryLabNumberplates?.renderAssetLibrary?.(state.customNumberplates, {
    onActivate: activateCustomNumberplateAsset,
    onDeactivate: deactivateCustomNumberplateAsset,
    onReplace: replaceCustomNumberplateAssetPng,
  });
}

async function loadCustomNumberplateAssets() {
  if (!window.pywebview?.api?.list_custom_numberplates) {
    renderNumberplateAssetLibrary();
    return;
  }
  try {
    const result = await window.pywebview.api.list_custom_numberplates($("rootPath")?.value.trim() || "");
    if (result.ok) {
      state.customNumberplates = result.data || { fonts: [], backgrounds: [] };
    }
  } catch (_error) {
    state.customNumberplates = { fonts: [], backgrounds: [] };
  }
  renderNumberplateAssetLibrary();
}

function renderCustomTyreBrands() {
  const list = $("tyreBrandList");
  const detail = $("tyreBrandDetail");
  const summary = $("tyreBrandSummary");
  const status = $("tyreBrandStatus");
  const exportButton = $("exportTyreBrandPackage");
  if (!list) return;
  const items = Array.isArray(state.customTyreBrands) ? state.customTyreBrands : [];
  const selected = items.find((item) => String(item.id || "").toLowerCase() === String(state.selectedTyreBrandId || "").toLowerCase()) || null;
  list.innerHTML = "";
  if (!items.length) {
    list.innerHTML = '<div class="tyre-brand-empty">No custom racing tyre brands yet.</div>';
    state.selectedTyreBrandId = "";
    if (status) status.textContent = "Empty";
    if (detail) detail.innerHTML = '<div class="tyre-brand-empty">Create or import a racing tyre brand to make it available in Skin Edit.</div>';
    if (exportButton) exportButton.disabled = true;
    if (summary) summary.textContent = "Create a racing tyre brand to make it available in Skin Edit for racing tyre skins.";
    return;
  }
  if (!selected) {
    state.selectedTyreBrandId = String(items[0]?.id || "");
  }
  items.forEach((item) => {
    const card = document.createElement("button");
    card.type = "button";
    card.className = "tyre-brand-item";
    if (String(item.id || "") === String(state.selectedTyreBrandId || "")) card.classList.add("active");
    const files = Array.isArray(item.files) ? item.files : [];
    const localCount = files.filter((file) => file.localExists).length;
    const gameCount = files.filter((file) => file.gameExists).length;
    card.innerHTML = `
      <div class="tyre-brand-mark">${escapeHtml(String(item.label || item.id || "?").slice(0, 2).toUpperCase())}</div>
      <div class="tyre-brand-copy">
        <strong>${escapeHtml(item.label || item.id || "Tyre brand")}</strong>
        <span>${escapeHtml(item.id || "-")}</span>
        <small>${escapeHtml(item.family || "racing")} · local ${localCount}/${files.length} · game ${gameCount}/${files.length}</small>
      </div>
    `;
    card.addEventListener("click", () => {
      state.selectedTyreBrandId = String(item.id || "");
      renderCustomTyreBrands();
    });
    list.append(card);
  });
  const active = items.find((item) => String(item.id || "") === String(state.selectedTyreBrandId || "")) || items[0];
  if (exportButton) exportButton.disabled = !active;
  if (status) status.textContent = active ? "Selected" : "Ready";
  if (detail && active) {
    const files = Array.isArray(active.files) ? active.files : [];
    const localCount = files.filter((file) => file.localExists).length;
    const gameCount = files.filter((file) => file.gameExists).length;
    detail.innerHTML = `
      <div class="tyre-brand-hero">
        <div class="tyre-brand-mark is-large">${escapeHtml(String(active.label || active.id || "?").slice(0, 2).toUpperCase())}</div>
        <div class="tyre-brand-copy">
          <strong>${escapeHtml(active.label || active.id || "Tyre brand")}</strong>
          <span>${escapeHtml(active.id || "-")}</span>
        </div>
      </div>
      <div class="tyre-brand-metrics">
        <div><span>Family</span><strong>${escapeHtml(active.family || "racing")}</strong></div>
        <div><span>Local files</span><strong>${localCount}/${files.length}</strong></div>
        <div><span>Game files</span><strong>${gameCount}/${files.length}</strong></div>
        <div><span>Template</span><strong>${escapeHtml(fileNameOf(active.template || "") || active.template || "-")}</strong></div>
      </div>
      <div class="tyre-brand-paths">
        <span>Manifest</span>
        <code>${escapeHtml(active.manifestPath || "-")}</code>
      </div>
    `;
  }
  if (summary && active) {
    const files = Array.isArray(active.files) ? active.files : [];
    summary.textContent = [
      `Selected tyre brand: ${active.label || active.id || "-"}`,
      `ID: ${active.id || "-"}`,
      `Family: ${active.family || "-"}`,
      `Template: ${active.template || "-"}`,
      `Manifest: ${active.manifestPath || "-"}`,
      "",
      "Refs:",
      ...(active.refs || []).map((ref) => `- ${ref}`),
      active.fallback ? `- ${active.fallback} (fallback)` : "",
      "",
      "Files:",
      ...files.map((file) => `- ${file.rel} | local: ${file.localExists ? "yes" : "no"} | game: ${file.gameExists ? "yes" : "no"}`),
    ].filter(Boolean).join("\n");
  }
}

async function loadCustomTyreBrands() {
  if (!window.pywebview?.api?.list_custom_tyre_brands) {
    renderCustomTyreBrands();
    return;
  }
  const requestId = (state.customTyreBrandsLoadRequestId || 0) + 1;
  state.customTyreBrandsLoadRequestId = requestId;
  state.customTyreBrandsLoading = true;
  setToolLoading("tyre-brands", true);
  if ($("tyreBrandStatus")) $("tyreBrandStatus").textContent = "Loading";
  try {
    const result = await window.pywebview.api.list_custom_tyre_brands($("rootPath")?.value.trim() || "");
    if (requestId !== state.customTyreBrandsLoadRequestId) return;
    if (result.ok) {
      state.customTyreBrands = Array.isArray(result.data?.items) ? result.data.items : [];
    }
  } catch (_error) {
    if (requestId !== state.customTyreBrandsLoadRequestId) return;
    state.customTyreBrands = [];
  } finally {
    if (requestId === state.customTyreBrandsLoadRequestId) {
      state.customTyreBrandsLoading = false;
      setToolLoading("tyre-brands", false);
      renderCustomTyreBrands();
    }
  }
}

async function pickTyreBrandPng(targetId) {
  const result = await window.pywebview.api.choose_path("file", ["PNG (*.png)", "All files (*.*)"], "");
  if (showResult(result, t("log_path_selected")) && result.data?.path && $(targetId)) {
    $(targetId).value = result.data.path;
    updateTyreBrandDraftFromInputs();
  }
}

async function tyreBrandPackageRoot() {
  if (!window.pywebview?.api?.get_tyre_brand_package_export_root) return "";
  const result = await window.pywebview.api.get_tyre_brand_package_export_root($("rootPath")?.value.trim() || "");
  return result?.ok && result.data?.path ? result.data.path : "";
}

async function pickTyreBrandPackageFolder(initialDir = "") {
  const result = await window.pywebview.api.choose_path("folder", ["All files (*.*)"], "", initialDir || "");
  return result?.ok && result.data?.path ? result.data.path : "";
}

async function pickTyreBrandPackageZip(initialDir = "") {
  const result = await window.pywebview.api.choose_path("file", ["LiveryLab Package (*.zip)", "ZIP (*.zip)"], "", initialDir || "");
  return result?.ok && result.data?.path ? result.data.path : "";
}

async function exportSelectedTyreBrandPackage() {
  const selectedId = String(state.selectedTyreBrandId || "").trim();
  const selected = (Array.isArray(state.customTyreBrands) ? state.customTyreBrands : []).find((item) => String(item.id || "") === selectedId);
  if (!selected?.id) {
    showStatusModal({
      type: "error",
      title: "Tyre Brands",
      subtitle: "No tyre brand selected",
      error: "Select a custom tyre brand before exporting a package.",
    });
    return;
  }
  if (!window.pywebview?.api?.export_custom_tyre_brand_package) {
    log("Tyre brand package export is not available.", true);
    return;
  }
  const outDir = await pickTyreBrandPackageFolder(await tyreBrandPackageRoot());
  if (!outDir) return;
  const result = await call("export_custom_tyre_brand_package", {
    id: selected.id,
    outDir,
    root: $("rootPath")?.value.trim() || "",
  });
  if (!result.ok) return;
  showStatusModal({
    type: "success",
    title: "Tyre brand exported",
    subtitle: selected.label || selected.id || "Package ready",
    message: "The tyre brand package was exported successfully.",
    details: result.message || `Package: ${result.data?.packagePath || result.data?.packageRoot || "-"}`,
    openFolderPath: result.data?.packagePath || result.data?.packageRoot || "",
  });
}

async function importTyreBrandPackage() {
  if (!window.pywebview?.api?.inspect_custom_tyre_brand_package || !window.pywebview?.api?.import_custom_tyre_brand_package) {
    log("Tyre brand package import is not available.", true);
    return;
  }
  const packageDir = await pickTyreBrandPackageZip(await tyreBrandPackageRoot());
  if (!packageDir) return;
  log(`${t("log_running")} inspect_custom_tyre_brand_package...`);
  const inspected = await window.pywebview.api.inspect_custom_tyre_brand_package(packageDir);
  if (!inspected.ok) {
    showResult(inspected, "Tyre brand package inspection failed", "inspect_custom_tyre_brand_package");
    return;
  }
  const brand = inspected.data?.brand || {};
  const manifest = brand.manifest || {};
  const missingCount = Number(inspected.data?.missingCount || 0);
  if (missingCount) {
    showStatusModal({
      type: "error",
      title: "Tyre brand package",
      subtitle: "Package is incomplete",
      error: `${missingCount} package file${missingCount === 1 ? "" : "s"} missing.`,
      details: (inspected.data?.missing || []).map((item) => `- ${item.kind || "file"}: ${item.path || "-"} ${item.reason ? `(${item.reason})` : ""}`).join("\n"),
    });
    return;
  }
  const proceed = await showConfirmModal({
    type: "info",
    title: "Import tyre brand package",
    message: "Import this racing tyre brand into your local library?",
    details: [
      `Brand: ${manifest.label || brand.label || brand.id || "-"}`,
      `ZIP: ${packageDir}`,
      `Files: ${inspected.data?.fileCount || 0}`,
      "",
      "If the same tyre brand id already exists, the imported brand receives a new id.",
    ].join("\n"),
    confirmLabel: "Import Package",
    cancelLabel: "Cancel",
  });
  if (!proceed) return;
  log(`${t("log_running")} import_custom_tyre_brand_package...`);
  const result = await window.pywebview.api.import_custom_tyre_brand_package({
    packageDir,
    conflictMode: "rename",
  });
  if (!result.ok) {
    showResult(result, "Tyre brand package import failed", "import_custom_tyre_brand_package");
    return;
  }
  if (result.message) log(result.message);
  state.selectedTyreBrandId = result.data?.brandId || "";
  state.tyreBrandDraft = null;
  await loadCustomTyreBrands();
  await loadMySkins();
  showStatusModal({
    type: "success",
    title: "Tyre brand imported",
    subtitle: result.data?.brand?.label || result.data?.brandId || "Local asset ready",
    message: "The tyre brand is now available for racing tyre skins.",
    details: result.message || "",
  });
}

async function downloadTyreBrandTemplate() {
  if (!window.pywebview?.api?.download_tyre_brand_templates) {
    log("Tyre brand template download is not available.", true);
    return;
  }
  const button = $("downloadTyreBrandTemplate");
  if (button) button.disabled = true;
  const status = $("tyreBrandStatus");
  if (status) status.textContent = "Preparing templates...";
  try {
    const result = await call("download_tyre_brand_templates");
    if (!result.ok) {
      if (status) status.textContent = "Template download failed";
      return;
    }
    const folder = result.data?.folder || "";
    if (folder && window.pywebview?.api?.open_folder) {
      await window.pywebview.api.open_folder(folder);
    }
    const items = Array.isArray(result.data?.items) ? result.data.items : [];
    showStatusModal({
      type: "success",
      title: "Tyre brand template ready",
      subtitle: "Folder opened",
      message: "Tyre brand template PNGs are ready for editing.",
      details: items.map((item) => `${item.status || "ready"}: ${item.name || item.target || "-"}`).join("\n") || result.message || "",
      openFolderPath: folder,
    });
    if (status) status.textContent = "Templates ready";
  } finally {
    if (button) button.disabled = false;
  }
}

async function createRacingTyreBrand() {
  const root = $("rootPath")?.value.trim() || "";
  const brandName = $("tyreBrandName")?.value.trim() || "";
  const assetId = tyreBrandSlugFromName(brandName);
  const slickPng = $("tyreBrandSlickPng")?.value.trim() || "";
  const blurPng = $("tyreBrandBlurPng")?.value.trim() || "";
  const markPng = $("tyreBrandMarkPng")?.value.trim() || "";
  const template = $("tyreBrandTemplate")?.value || "racing_cup_1";
  const overwrite = !!$("tyreBrandOverwrite")?.checked;
  if (!root || !brandName || !slickPng || !blurPng) {
    showStatusModal({
      type: "error",
      title: "Tyre Brands",
      subtitle: "Missing inputs",
      error: "Root, brand name, slick PNG and blur PNG are required.",
    });
    return;
  }
  const result = await call("create_racing_tyre_brand", {
    root,
    brandName,
    assetId,
    slickPng,
    blurPng,
    markPng,
    template,
    overwrite,
  });
  if (!result.ok) return;
  const item = result.data?.item || {};
  showStatusModal({
    type: "success",
    title: "Tyre brand created",
    subtitle: item.label || item.id || "Local asset ready",
    message: "The racing tyre brand was created locally. It will be copied into AC EVO when a skin using it is updated in game.",
    details: result.message || "",
  });
  await loadCustomTyreBrands();
  await loadMySkins();
}

function tyreBrandTemplateFromPath(value) {
  const text = String(value || "").replace(/\//g, "\\");
  const match = text.match(/textures\\([^\\]+)\\tyre_[^\\]+\.tyretextures$/i);
  return match?.[1] || "racing_cup_1";
}

function tyreBrandSlugFromId(value) {
  return String(value || "")
    .trim()
    .toLowerCase()
    .replace(/^tyre_/, "")
    .replace(/^racing_/, "")
    .replace(/_?1$/, "")
    .replace(/[^a-z0-9._-]+/g, "_")
    .replace(/^_+|_+$/g, "");
}

function tyreBrandSlugFromName(value) {
  const slug = String(value || "")
    .normalize("NFKD")
    .replace(/[\u0300-\u036f]/g, "")
    .toLowerCase()
    .replace(/ß/g, "ss")
    .replace(/&/g, " and ")
    .replace(/[^a-z0-9]+/g, "_")
    .replace(/^_+|_+$/g, "")
    .replace(/_+/g, "_");
  return slug || "new_tyre_brand";
}

function tyreBrandFullIdFromSlug(value) {
  const slug = String(value || "").trim() || "new_tyre_brand";
  return `racing_${slug}_1`;
}

function tyreBrandAssetSlugFromDraft(draft) {
  if (!draft) return "new_tyre_brand";
  if (draft.isNew) return tyreBrandSlugFromName(draft.brandName || draft.label);
  return draft.assetId || tyreBrandSlugFromId(draft.id || draft.brandName || draft.label) || tyreBrandSlugFromName(draft.brandName || draft.label);
}

function newTyreBrandDraft() {
  return { id: "", isNew: true, label: "New tyre brand", brandName: "New tyre brand", assetId: "new_tyre_brand", template: "racing_cup_1", slickPng: "", blurPng: "", markPng: "", refs: [], files: [] };
}

function tyreBrandDraftFromItem(item) {
  if (!item) return newTyreBrandDraft();
  return {
    id: String(item.id || ""),
    isNew: false,
    label: String(item.label || item.brandName || item.id || "Tyre brand"),
    brandName: String(item.brandName || item.label || item.id || "Tyre brand"),
    assetId: tyreBrandSlugFromId(item.id || item.label || ""),
    template: tyreBrandTemplateFromPath(item.template || ""),
    slickPng: String(item.sourcePngPath || ""),
    blurPng: String(item.sourceBlurPngPath || ""),
    markPng: String(item.markPngPath || ""),
    refs: Array.isArray(item.refs) ? item.refs : [],
    files: Array.isArray(item.files) ? item.files : [],
    manifestPath: String(item.manifestPath || ""),
    family: String(item.family || "racing"),
    fallback: String(item.fallback || ""),
  };
}

function currentTyreBrandItem() {
  const selectedId = String(state.selectedTyreBrandId || "");
  return (Array.isArray(state.customTyreBrands) ? state.customTyreBrands : []).find((item) => String(item.id || "") === selectedId) || null;
}

function currentTyreBrandDraft() {
  if (state.tyreBrandDraft) return state.tyreBrandDraft;
  const item = currentTyreBrandItem() || (Array.isArray(state.customTyreBrands) ? state.customTyreBrands[0] : null);
  state.selectedTyreBrandId = item?.id || "";
  state.tyreBrandDraft = tyreBrandDraftFromItem(item);
  return state.tyreBrandDraft;
}

function syncTyreBrandEditorFromDraft() {
  const draft = currentTyreBrandDraft();
  if ($("tyreBrandName")) $("tyreBrandName").value = draft.brandName || draft.label || "";
  draft.assetId = tyreBrandAssetSlugFromDraft(draft);
  if ($("tyreBrandTemplate")) $("tyreBrandTemplate").value = draft.template || "racing_cup_1";
  if ($("tyreBrandSlickPng")) $("tyreBrandSlickPng").value = draft.slickPng || "";
  if ($("tyreBrandBlurPng")) $("tyreBrandBlurPng").value = draft.blurPng || "";
  if ($("tyreBrandMarkPng")) $("tyreBrandMarkPng").value = draft.markPng || "";
  if ($("tyreBrandEditorTitle")) $("tyreBrandEditorTitle").textContent = draft.isNew ? "New racing tyre brand" : "Edit racing tyre brand";
  updateTyreBrandIdPreview(draft);
}

function updateTyreBrandIdPreview(draft = currentTyreBrandDraft()) {
  const preview = $("tyreBrandIdPreview");
  const assetSlug = tyreBrandAssetSlugFromDraft(draft);
  if (preview) preview.textContent = `Brand ID: ${tyreBrandFullIdFromSlug(assetSlug)}`;
  return assetSlug;
}

function updateTyreBrandDraftFromInputs() {
  const draft = currentTyreBrandDraft();
  draft.brandName = $("tyreBrandName")?.value.trim() || "";
  draft.label = draft.brandName;
  draft.assetId = tyreBrandAssetSlugFromDraft(draft);
  draft.template = $("tyreBrandTemplate")?.value || "racing_cup_1";
  draft.slickPng = $("tyreBrandSlickPng")?.value.trim() || "";
  draft.blurPng = $("tyreBrandBlurPng")?.value.trim() || "";
  draft.markPng = $("tyreBrandMarkPng")?.value.trim() || "";
  state.tyreBrandDraft = draft;
  updateTyreBrandIdPreview(draft);
  renderTyreBrandPreview();
  renderTyreBrandSummary();
}

function tyreBrandMarkHtml(item, extraClass = "") {
  const label = String(item?.label || item?.brandName || item?.id || "?");
  const fallback = escapeHtml(label.slice(0, 2).toUpperCase());
  const markPath = String(item?.markPngPath || item?.markPng || "").trim();
  const className = ["tyre-brand-mark", extraClass].filter(Boolean).join(" ");
  if (!markPath) return `<div class="${className}">${fallback}</div>`;
  return `<div class="${className}" data-tyre-brand-mark-path="${escapeHtml(markPath)}">${fallback}</div>`;
}

async function hydrateTyreBrandMarks(root = document) {
  const marks = Array.from(root.querySelectorAll("[data-tyre-brand-mark-path]"));
  await Promise.all(marks.map(async (mark) => {
    const path = mark.dataset.tyreBrandMarkPath || "";
    const src = await tyreBrandPreviewDataUrl(path);
    if (!src || mark.dataset.tyreBrandMarkPath !== path) return;
    mark.innerHTML = `<img src="${escapeHtml(src)}" alt="" />`;
  }));
}

async function tyreBrandPreviewDataUrl(path) {
  const key = String(path || "").trim();
  if (!key || !window.pywebview?.api?.load_image_preview) return "";
  if (state.tyreBrandPreviewCache[key]) return state.tyreBrandPreviewCache[key];
  const result = await window.pywebview.api.load_image_preview(key, "tyre-brands");
  const src = result?.ok ? (result.data?.dataUrl || result.data?.data_url || "") : "";
  if (src) state.tyreBrandPreviewCache[key] = src;
  return src;
}

function loadTyreBrandPreviewImage(src) {
  return new Promise((resolve, reject) => {
    const image = new Image();
    image.onload = () => resolve(image);
    image.onerror = reject;
    image.src = src;
  });
}

async function drawTyreBrandCanvas(canvas, path, label, renderId) {
  if (!canvas) return;
  const context = canvas.getContext("2d");
  context.clearRect(0, 0, canvas.width, canvas.height);
  context.fillStyle = "#0c0f14";
  context.fillRect(0, 0, canvas.width, canvas.height);
  context.strokeStyle = "rgba(255,255,255,0.16)";
  context.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);
  const src = await tyreBrandPreviewDataUrl(path);
  if (renderId !== state.tyreBrandPreviewRenderId) return;
  if (src) {
    try {
      const image = await loadTyreBrandPreviewImage(src);
      if (renderId !== state.tyreBrandPreviewRenderId) return;
      const scale = Math.min(canvas.width / image.width, canvas.height / image.height);
      const width = image.width * scale;
      const height = image.height * scale;
      context.drawImage(image, (canvas.width - width) / 2, (canvas.height - height) / 2, width, height);
      return;
    } catch (_error) {
      // Fallback text below.
    }
  }
  context.fillStyle = "#8e9aa5";
  context.font = "700 26px Arial, sans-serif";
  context.textAlign = "center";
  context.fillText(`No ${label} preview`, canvas.width / 2, canvas.height / 2);
}

function renderTyreBrandPreview() {
  const draft = currentTyreBrandDraft();
  const renderId = ++state.tyreBrandPreviewRenderId;
  drawTyreBrandCanvas($("tyreBrandSlickCanvas"), draft.slickPng, "slick", renderId);
  drawTyreBrandCanvas($("tyreBrandBlurCanvas"), draft.blurPng, "blur", renderId);
}

function renderTyreBrandSummary() {
  const summary = $("tyreBrandSummary");
  const status = $("tyreBrandStatus");
  const draft = currentTyreBrandDraft();
  const files = Array.isArray(draft.files) ? draft.files : [];
  if (status) status.textContent = draft.isNew ? "New" : "Selected";
  if (!summary) return;
  summary.textContent = [
    `Template: ${draft.brandName || draft.label || "-"}`,
    `ID: ${draft.id || "(new)"}`,
    `Family: ${draft.family || "racing"}`,
    `Base template: ${draft.template || "-"}`,
    `Manifest: ${draft.manifestPath || "-"}`,
    "",
    "Refs:",
    ...(draft.refs || []).map((ref) => `- ${ref}`),
    draft.fallback ? `- ${draft.fallback} (fallback)` : "",
    "",
    "Files:",
    ...files.map((file) => `- ${file.rel} | local: ${file.localExists ? "yes" : "no"} | game: ${file.gameExists ? "yes" : "no"}`),
  ].filter(Boolean).join("\n");
}

function renderCustomTyreBrands() {
  const list = $("tyreBrandList");
  if (!list) return;
  const items = Array.isArray(state.customTyreBrands) ? state.customTyreBrands : [];
  if (!state.tyreBrandDraft) {
    const selected = items.find((item) => String(item.id || "") === String(state.selectedTyreBrandId || "")) || items[0] || null;
    state.selectedTyreBrandId = selected?.id || "";
    state.tyreBrandDraft = tyreBrandDraftFromItem(selected);
  }
  list.innerHTML = "";
  if (!items.length) {
    list.innerHTML = '<div class="tyre-brand-empty">No custom racing tyre brands yet.</div>';
  }
  items.forEach((item) => {
    const card = document.createElement("button");
    card.type = "button";
    card.className = "tyre-brand-item";
    if (!state.tyreBrandDraft?.isNew && String(item.id || "") === String(state.selectedTyreBrandId || "")) card.classList.add("active");
    const files = Array.isArray(item.files) ? item.files : [];
    const localCount = files.filter((file) => file.localExists).length;
    const gameCount = files.filter((file) => file.gameExists).length;
    card.innerHTML = `
      ${tyreBrandMarkHtml(item)}
      <div class="tyre-brand-copy">
        <strong>${escapeHtml(item.label || item.id || "Tyre brand")}</strong>
        <span>${escapeHtml(item.id || "-")}</span>
        <small>${escapeHtml(item.family || "racing")} / local ${localCount}/${files.length} / game ${gameCount}/${files.length}</small>
      </div>
    `;
    card.addEventListener("click", () => {
      state.selectedTyreBrandId = String(item.id || "");
      state.tyreBrandDraft = tyreBrandDraftFromItem(item);
      renderCustomTyreBrands();
    });
    list.append(card);
  });
  hydrateTyreBrandMarks(list);
  const hasExisting = !!state.selectedTyreBrandId && !state.tyreBrandDraft?.isNew;
  ["deleteTyreBrandTemplate", "exportTyreBrandPackage"].forEach((id) => {
    if ($(id)) $(id).disabled = !hasExisting;
  });
  syncTyreBrandEditorFromDraft();
  renderTyreBrandSummary();
  renderTyreBrandPreview();
}

function driverProfileKey(item) {
  return [item?.carModel || "", item?.id || ""].join("::").toLowerCase();
}

function selectedDriverProfile() {
  const items = Array.isArray(state.driverProfiles) ? state.driverProfiles : [];
  return items.find((item) => driverProfileKey(item) === state.selectedDriverProfileKey) || items[0] || null;
}

function driverProfileDate(value) {
  if (!value) return "-";
  const date = new Date(value);
  if (Number.isNaN(date.getTime())) return String(value);
  return date.toLocaleDateString(undefined, { year: "numeric", month: "2-digit", day: "2-digit" });
}

function skinDriverMeta(skin) {
  const source = skin?.source_skin && typeof skin.source_skin === "object" ? skin.source_skin : {};
  return source.driver_skin && typeof source.driver_skin === "object" ? source.driver_skin : null;
}

function isUsableSkinDriverMeta(meta) {
  if (!meta || typeof meta !== "object") return false;
  const driverFinalState = String(meta.local_driverfinalstate || meta.localDriverFinalState || meta.driverfinalstate || meta.driverFinalState || "").trim();
  const materials = Number(meta.materials || meta.materialCount || 0);
  return Boolean(driverFinalState && materials > 0);
}

function driverCapableMySkins() {
  return (Array.isArray(state.mySkins) ? state.mySkins : []).filter((skin) => (
    isDriverProfileCarSupported(skin?.car_model)
    && isUsableSkinDriverMeta(skinDriverMeta(skin))
  ));
}

function driverCapableMySkinsForProfile(profile = {}) {
  const car = driverCarKey(profile.carModel);
  return driverCapableMySkins().filter((skin) => !car || driverCarKey(skin?.car_model) === car);
}

function driverProfileSelection(skin = {}) {
  const value = skin?.driver_profile;
  return value && typeof value === "object" ? value : {};
}

function driverCarKey(carModel) {
  return String(carModel || "").trim().toLowerCase();
}

function isDriverProfileCarSupported(carModel) {
  return DRIVER_PROFILE_SUPPORTED_CARS.has(driverCarKey(carModel));
}

function driverProfilesForCar(carModel) {
  const car = driverCarKey(carModel);
  return (Array.isArray(state.driverProfiles) ? state.driverProfiles : []).filter((profile) => driverCarKey(profile.carModel) === car);
}

function driverProfileOptionLabel(profile = {}) {
  const files = Array.isArray(profile.files) ? profile.files.length : 0;
  const label = profile.name || profile.id || "Driver";
  return files ? `${label} (${files} files)` : label;
}

function driverProfileUsageLabel(profile = {}) {
  const count = Number(profile.usedByCount || 0);
  if (!count) return "Not used by any My Skin";
  return `Used by ${count} My Skin${count === 1 ? "" : "s"}`;
}

function driverProjectInfo(profile = {}) {
  return profile.project && typeof profile.project === "object" ? profile.project : {};
}

function driverProfilePreviewPath(profile = {}) {
  const project = driverProjectInfo(profile);
  return String(project.previewPngPath || profile.previewPngPath || "").trim();
}

function driverProfileMarkHtml(profile = {}, options = {}) {
  const previewPath = driverProfilePreviewPath(profile);
  const classes = ["driver-profile-mark"];
  if (options.large) classes.push("is-large");
  if (previewPath) classes.push("has-preview");
  if (!previewPath) return `<div class="${classes.join(" ")}">${uiIcon("driver")}</div>`;
  return `
    <div class="${classes.join(" ")}">
      <img alt="" src="${mySkinPlaceholderSrc()}" data-driver-profile-preview-path="${escapeHtml(previewPath)}" />
    </div>
  `;
}

function driverProfileDetailsEditorHtml(profile = {}) {
  const sourceKey = String(profile.sourceSkinKey || "").trim();
  const skins = driverCapableMySkinsForProfile(profile);
  const hasCurrentSource = sourceKey && skins.some((skin) => String(skin.key || "") === sourceKey);
  const options = skins.map((skin) => {
    const key = String(skin.key || "");
    const label = [skin.display_name || skin.skin_id || "Livery", skin.car_model || ""].filter(Boolean).join(" / ");
    return `<option value="${escapeHtml(key)}"${key === sourceKey ? " selected" : ""}>${escapeHtml(label)}</option>`;
  }).join("");
  const missingCurrent = sourceKey && !hasCurrentSource
    ? `<option value="${escapeHtml(sourceKey)}" selected disabled>Current source unavailable</option>`
    : "";
  const emptyOption = !sourceKey && !skins.length
    ? `<option value="" selected disabled>No compatible driver livery</option>`
    : (!sourceKey ? `<option value="" selected disabled>Select compatible livery</option>` : "");
  return `
    <div class="driver-profile-details-box">
      <div class="driver-profile-section-head">
        <strong>Profile Details</strong>
        <span>Rename or rebuild</span>
      </div>
      <label>Profile Name
        <input id="driverProfileDetailName" type="text" spellcheck="false" value="${escapeHtml(profile.name || profile.id || "")}" />
      </label>
      <label>Select Livery for Driver
        <select id="driverProfileDetailSourceSkin"${skins.length || hasCurrentSource ? "" : " disabled"}>
          ${missingCurrent}
          ${emptyOption}
          ${options}
        </select>
      </label>
      <button id="saveDriverProfileDetails" type="button" class="primary icon-text">${uiIcon("save")}Save details</button>
    </div>
  `;
}

async function loadDriverProfilePreviewImages() {
  if (!window.pywebview?.api?.load_image_preview) return;
  const nodes = Array.from(document.querySelectorAll("[data-driver-profile-preview-path]"));
  for (const image of nodes) {
    const path = image.getAttribute("data-driver-profile-preview-path") || "";
    if (!path) continue;
    try {
      const result = await window.pywebview.api.load_image_preview(path, "driver-profile-preview");
      if (result?.ok && result.data?.data_url) image.src = result.data.data_url;
    } catch (_error) {
      // Keep the generic placeholder when preview loading fails.
    }
  }
}

function driverProfileMaterialControls(profile = {}) {
  const project = driverProjectInfo(profile);
  return Array.isArray(project.materialControls) ? project.materialControls : [];
}

function driverProfileAppearance(profile = {}) {
  const project = driverProjectInfo(profile);
  return project.appearance && typeof project.appearance === "object" ? project.appearance : {};
}

function driverProfileColorControlValue(profile = {}, control = {}) {
  const appearance = driverProfileAppearance(profile);
  const controlId = String(control?.id || "");
  return normalizeLiveryPreviewHexColor(appearance[controlId] || control.color || control.defaultColor || "#ffffff");
}

function driverProjectSourcePngs(profile = {}) {
  const project = driverProjectInfo(profile);
  const root = String(project.projectRoot || profile.projectRoot || "").trim();
  const sourceFiles = Array.isArray(project.sourceFiles) ? project.sourceFiles : [];
  const fromSources = Array.isArray(project.textureSources)
    ? project.textureSources.map((source) => String(source?.sourcePng || "").trim()).filter(Boolean)
    : [];
  const previewNames = new Set(["preview.png", String(project.previewPng || "").trim().toLowerCase()].filter(Boolean));
  const names = [...new Set([...sourceFiles, ...fromSources]
    .map((name) => String(name || "").trim())
    .filter((name) => /\.png$/i.test(name) && !previewNames.has(name.toLowerCase())))];
  const order = ["helmet", "gloves", "suit", "visor"];
  return names
    .map((name) => {
      const lower = name.toLowerCase();
      const category = order.find((part) => lower === `${part}.png` || lower.startsWith(`${part}_`)) || "driver";
      return {
        name,
        category,
        label: category === "driver" ? name.replace(/\.png$/i, "") : category[0].toUpperCase() + category.slice(1),
        path: root ? joinPath(root, name) : "",
      };
    })
    .sort((a, b) => {
      const ai = order.indexOf(a.category);
      const bi = order.indexOf(b.category);
      return (ai < 0 ? 99 : ai) - (bi < 0 ? 99 : bi) || a.name.localeCompare(b.name);
    });
}

function driverProjectPngGridHtml(profile = {}) {
  const pngs = driverProjectSourcePngs(profile);
  if (!pngs.length) {
    return `
      <section class="driver-profile-preview-section">
        <div class="driver-profile-section-head">
          <strong>Source PNGs</strong>
          <span>No project PNGs</span>
        </div>
        <div class="driver-profile-empty">Create or refresh the driver project to generate PNG sources.</div>
      </section>
    `;
  }
  return `
    <section class="driver-profile-preview-section">
      <div class="driver-profile-section-head">
        <strong>Source PNGs</strong>
        <span>${pngs.length} file${pngs.length === 1 ? "" : "s"}</span>
      </div>
      <div class="driver-profile-png-grid">
        ${pngs.map((png) => `
          <article class="driver-profile-png-card">
            <div class="driver-profile-png-frame">
              <img alt="" data-driver-png-path="${escapeHtml(png.path)}" src="${mySkinPlaceholderSrc()}" />
              <span class="driver-profile-png-state">Loading</span>
            </div>
            <div class="driver-profile-png-meta">
              <strong>${escapeHtml(png.label)}</strong>
              <span>${escapeHtml(png.name)}</span>
            </div>
          </article>
        `).join("")}
      </div>
    </section>
  `;
}

async function loadDriverPngPreviews(profile = selectedDriverProfile()) {
  if (!window.pywebview?.api?.load_image_preview || activeTab() !== "drivers") return;
  const requestId = ++state.driverPngPreviewRequestId;
  const nodes = Array.from(document.querySelectorAll("[data-driver-png-path]"));
  for (const image of nodes) {
    const path = image.getAttribute("data-driver-png-path") || "";
    const frame = image.closest(".driver-profile-png-frame");
    const stateLabel = frame?.querySelector(".driver-profile-png-state");
    if (!path) {
      if (stateLabel) stateLabel.textContent = "Missing";
      continue;
    }
    try {
      const result = await window.pywebview.api.load_image_preview(path, `driver-${driverProfileKey(profile)}`);
      if (requestId !== state.driverPngPreviewRequestId) return;
      if (result?.ok && result.data?.data_url) {
        image.src = result.data.data_url;
        if (stateLabel) stateLabel.textContent = "";
        frame?.classList.remove("is-missing");
      } else {
        if (stateLabel) stateLabel.textContent = "Missing";
        frame?.classList.add("is-missing");
      }
    } catch (_error) {
      if (stateLabel) stateLabel.textContent = "Missing";
      frame?.classList.add("is-missing");
    }
  }
}

function scheduleDriverPngPreviewRefresh(profile = selectedDriverProfile()) {
  window.clearTimeout(state.driverPngPreviewTimer);
  if (activeTab() !== "drivers" || !profile) return;
  state.driverPngPreviewTimer = window.setTimeout(async () => {
    await loadDriverPngPreviews(profile);
    scheduleDriverPngPreviewRefresh(selectedDriverProfile());
  }, 2500);
}

function driverAppearanceEditorHtml(profile = {}) {
  const controls = driverProfileMaterialControls(profile);
  const colorControls = controls.filter((item) => item?.id && String(item?.category || "").toLowerCase() === "suit");
  if (!colorControls.length) return "";
  const rows = colorControls.map((control) => {
    const color = driverProfileColorControlValue(profile, control);
    return `
      <div class="driver-profile-color-control" data-driver-appearance-id="${escapeHtml(control.id)}">
        <label>${escapeHtml(control.label || control.id)}
          <input type="color" value="${escapeHtml(color)}" data-driver-appearance-color="${escapeHtml(control.id)}" />
        </label>
        <label>Hex
          <input type="text" spellcheck="false" value="${escapeHtml(color)}" data-driver-appearance-hex="${escapeHtml(control.id)}" />
        </label>
        <div class="driver-profile-material-meta">
          <span>Parameter</span>
          <code>${escapeHtml(control.parameter || "-")}</code>
        </div>
      </div>
    `;
  }).join("");
  return `
    <section class="driver-profile-appearance">
      <div class="driver-profile-section-head">
        <strong>Appearance</strong>
        <span>${colorControls.length} suit color${colorControls.length === 1 ? "" : "s"}</span>
      </div>
      <div class="driver-profile-color-list">
        ${rows}
      </div>
      <div class="driver-profile-material-meta is-wide">
        <span>Material</span>
        <code>${escapeHtml(colorControls[0]?.materialRel || "-")}</code>
      </div>
    </section>
  `;
}

function driverTeamLogoEditorHtml(profile = {}) {
  return `
    <section class="driver-profile-team-logo-box">
      <div class="driver-profile-section-head">
        <strong>Team Logo</strong>
        <span>Square PNG</span>
      </div>
      <div class="driver-profile-team-logo-preview">
        ${driverProfileMarkHtml(profile, { large: true })}
      </div>
      <button id="chooseDriverProfilePreviewPng" type="button" class="icon-text">${uiIcon("file")}Choose PNG</button>
    </section>
  `;
}

function bindDriverAppearanceEditor(profile = selectedDriverProfile()) {
  const saveButton = $("saveDriverProfileAppearance");
  if (!profile || !saveButton) return;
  document.querySelectorAll("[data-driver-appearance-id]").forEach((row) => {
    const colorInput = row.querySelector("[data-driver-appearance-color]");
    const hexInput = row.querySelector("[data-driver-appearance-hex]");
    if (!colorInput || !hexInput) return;
    const syncColor = (value, target) => {
      const color = normalizeLiveryPreviewHexColor(value, colorInput.value || "#ffffff");
      colorInput.value = color;
      hexInput.value = color;
      if (target) target.value = color;
    };
    colorInput.addEventListener("input", () => syncColor(colorInput.value, hexInput));
    hexInput.addEventListener("change", () => syncColor(hexInput.value, colorInput));
  });
  saveButton.addEventListener("click", () => saveDriverProfileAppearance(profile));
}

async function saveDriverProfileAppearance(profile = selectedDriverProfile()) {
  if (!profile?.id) return;
  const appearance = {};
  document.querySelectorAll("[data-driver-appearance-id]").forEach((row) => {
    const controlId = row.getAttribute("data-driver-appearance-id") || "";
    const hexInput = row.querySelector("[data-driver-appearance-hex]");
    const colorInput = row.querySelector("[data-driver-appearance-color]");
    if (controlId) appearance[controlId] = normalizeLiveryPreviewHexColor(hexInput?.value || colorInput?.value || "#ffffff");
  });
  await withButtonWorking("saveDriverProfileAppearance", "Saving...", async () => {
    const result = await call("update_driver_profile_appearance", {
      id: profile.id,
      carModel: profile.carModel || "",
      appearance,
    });
    if (!result.ok) return;
    const item = result.data?.item || null;
    if (item?.id) {
      const key = driverProfileKey(item);
      const existing = new Map((Array.isArray(state.driverProfiles) ? state.driverProfiles : []).map((row) => [driverProfileKey(row), row]));
      existing.set(key, item);
      state.driverProfiles = [...existing.values()].sort((a, b) => String(a.carModel || "").localeCompare(String(b.carModel || "")) || String(a.name || a.id || "").localeCompare(String(b.name || b.id || "")));
      state.selectedDriverProfileKey = key;
    } else {
      await loadDriverProfiles();
    }
    await loadMySkins();
    renderDriverProfiles();
    const affectedCount = Number(result.data?.affectedCount || 0);
    showStatusModal({
      type: "success",
      title: "Driver appearance saved",
      subtitle: profile.name || profile.id || "Driver profile",
      message: affectedCount
        ? `The driver project appearance was saved. ${affectedCount} linked My Skin${affectedCount === 1 ? "" : "s"} now need Update in Game.`
        : "The driver project appearance was saved. No linked My Skin is currently using this driver.",
      details: result.message || "",
    });
  });
}

async function chooseDriverProfilePreviewPng(profile = selectedDriverProfile()) {
  if (!profile?.id) return;
  if (!window.pywebview?.api?.choose_path) return;
  const picked = await window.pywebview.api.choose_path("file", ["PNG (*.png)", "All files (*.*)"], "");
  if (!showResult(picked, t("log_path_selected")) || !picked.data?.path) return;
  await withButtonWorking("chooseDriverProfilePreviewPng", "Saving...", async () => {
    const result = await call("save_driver_profile_preview_image", {
      id: profile.id,
      carModel: profile.carModel || "",
      previewPng: picked.data.path,
    });
    if (!result.ok) return;
    const item = result.data?.item || null;
    if (item?.id) {
      const key = driverProfileKey(item);
      const existing = new Map((Array.isArray(state.driverProfiles) ? state.driverProfiles : []).map((row) => [driverProfileKey(row), row]));
      existing.set(key, item);
      state.driverProfiles = [...existing.values()].sort((a, b) => String(a.carModel || "").localeCompare(String(b.carModel || "")) || String(a.name || a.id || "").localeCompare(String(b.name || b.id || "")));
      state.selectedDriverProfileKey = key;
    } else {
      await loadDriverProfiles();
    }
    renderDriverProfiles();
  });
}

function currentDriverPreviewProfile() {
  const previewKey = String(state.driverPreviewProfileKey || "").trim();
  return (Array.isArray(state.driverProfiles) ? state.driverProfiles : [])
    .find((row) => driverProfileKey(row) === previewKey) || selectedDriverProfile();
}

async function markDriverProfileProjectPngChanged(profile = currentDriverPreviewProfile()) {
  if (!profile?.id || !window.pywebview?.api?.mark_driver_profile_project_changed) return null;
  const result = await window.pywebview.api.mark_driver_profile_project_changed({
    id: profile.id,
    carModel: profile.carModel || "",
    reason: "Driver project PNG changed",
    workflow: "driver_project_png",
  });
  if (result?.ok) {
    await loadMySkins();
    await loadDriverProfiles();
  }
  return result || null;
}

async function openDriverProfileProjectFolder(profile = selectedDriverProfile()) {
  const project = driverProjectInfo(profile || {});
  const projectFolder = String(project.projectRoot || profile?.projectRoot || "").trim();
  if (!project.exists || !projectFolder) {
    showStatusModal({
      type: "warning",
      title: t("open_project_folder"),
      subtitle: "Project folder not found",
      message: "No LiveryProjects folder exists for this driver profile yet.",
      details: projectFolder ? `Expected folder:\n${projectFolder}` : "",
    });
    return;
  }
  const result = await window.pywebview.api.open_folder(projectFolder);
  showResult(result, "Folder opened", "open_folder");
}

function driverPreviewAvailableModels(project = {}) {
  return (Array.isArray(project.models) ? project.models : []).filter((model) => model?.available && model?.modelPath);
}

function setDriverPreviewStatus(message, isError = false) {
  const status = $("driverPreviewStatus");
  if (!status) return;
  status.textContent = message || "Ready";
  status.classList.toggle("is-error", Boolean(isError));
}

function closeDriverPreviewModal() {
  stopDriverPreviewWatcher();
  const modal = $("driverPreviewModal");
  if (modal) modal.hidden = true;
  window.LiveryLabDriverPreview?.clear?.();
}

function driverPreviewAppearanceControls(project = {}) {
  return (Array.isArray(project.materialControls) ? project.materialControls : [])
    .filter((control) => control?.id && String(control?.category || "").toLowerCase() === "suit");
}

function driverPreviewAppearanceValue(project = {}, control = {}) {
  const appearance = project.appearance && typeof project.appearance === "object" ? project.appearance : {};
  const controlId = String(control.id || "");
  return normalizeLiveryPreviewHexColor(appearance[controlId] || control.color || control.defaultColor || "#ffffff");
}

function currentDriverPreviewAppearance() {
  const appearance = {};
  document.querySelectorAll("[data-driver-preview-appearance-id]").forEach((row) => {
    const controlId = row.getAttribute("data-driver-preview-appearance-id") || "";
    const hexInput = row.querySelector("[data-driver-preview-appearance-hex]");
    const colorInput = row.querySelector("[data-driver-preview-appearance-color]");
    if (controlId) appearance[controlId] = normalizeLiveryPreviewHexColor(hexInput?.value || colorInput?.value || "#ffffff");
  });
  return appearance;
}

function applyDriverPreviewAppearance() {
  const appearance = currentDriverPreviewAppearance();
  if (state.driverPreviewProject) {
    state.driverPreviewProject.appearance = {
      ...(state.driverPreviewProject.appearance || {}),
      ...appearance,
    };
  }
  window.LiveryLabDriverPreview?.setAppearance?.(appearance);
}

function renderDriverPreviewAppearancePanel(project = state.driverPreviewProject) {
  const panel = $("driverPreviewAppearancePanel");
  const controlsRoot = $("driverPreviewAppearanceControls");
  const saveButton = $("driverPreviewSaveAppearance");
  if (!panel || !controlsRoot || !saveButton) return;
  const controls = driverPreviewAppearanceControls(project || {});
  panel.hidden = !controls.length;
  if (!controls.length) {
    controlsRoot.innerHTML = "";
    saveButton.disabled = true;
    return;
  }
  controlsRoot.innerHTML = controls.map((control) => {
    const color = driverPreviewAppearanceValue(project, control);
    return `
      <div class="driver-preview-color-control" data-driver-preview-appearance-id="${escapeHtml(control.id)}">
        <label>${escapeHtml(control.label || control.id)}
          <input type="color" value="${escapeHtml(color)}" data-driver-preview-appearance-color="${escapeHtml(control.id)}" />
        </label>
        <label>Hex
          <input type="text" spellcheck="false" value="${escapeHtml(color)}" data-driver-preview-appearance-hex="${escapeHtml(control.id)}" />
        </label>
      </div>
    `;
  }).join("");
  saveButton.disabled = false;
  document.querySelectorAll("[data-driver-preview-appearance-id]").forEach((row) => {
    const colorInput = row.querySelector("[data-driver-preview-appearance-color]");
    const hexInput = row.querySelector("[data-driver-preview-appearance-hex]");
    if (!colorInput || !hexInput) return;
    const sync = (value, target) => {
      const color = normalizeLiveryPreviewHexColor(value, colorInput.value || "#ffffff");
      colorInput.value = color;
      hexInput.value = color;
      if (target) target.value = color;
      applyDriverPreviewAppearance();
    };
    colorInput.addEventListener("input", () => sync(colorInput.value, hexInput));
    hexInput.addEventListener("change", () => sync(hexInput.value, colorInput));
  });
}

async function saveDriverPreviewAppearance() {
  const profile = selectedDriverProfile();
  if (!profile?.id || !state.driverPreviewProject) return;
  const appearance = currentDriverPreviewAppearance();
  await withButtonWorking("driverPreviewSaveAppearance", "Saving...", async () => {
    const result = await call("update_driver_profile_appearance", {
      id: profile.id,
      carModel: profile.carModel || "",
      appearance,
    });
    if (!result.ok) return;
    const item = result.data?.item || null;
    if (item?.id) {
      const key = driverProfileKey(item);
      const existing = new Map((Array.isArray(state.driverProfiles) ? state.driverProfiles : []).map((row) => [driverProfileKey(row), row]));
      existing.set(key, item);
      state.driverProfiles = [...existing.values()].sort((a, b) => String(a.carModel || "").localeCompare(String(b.carModel || "")) || String(a.name || a.id || "").localeCompare(String(b.name || b.id || "")));
      state.selectedDriverProfileKey = key;
    }
    state.driverPreviewProject.appearance = {
      ...(state.driverPreviewProject.appearance || {}),
      ...appearance,
    };
    applyDriverPreviewAppearance();
    renderDriverPreviewAppearancePanel(state.driverPreviewProject);
    setDriverPreviewStatus("Driver appearance saved");
    await loadMySkins();
    renderDriverProfiles();
  });
}

function renderDriverPreviewTabs(project, selectedSlot) {
  const tabs = $("driverPreviewModelTabs");
  if (!tabs) return;
  const models = driverPreviewAvailableModels(project);
  tabs.innerHTML = "";
  models.forEach((model) => {
    const button = document.createElement("button");
    button.type = "button";
    button.textContent = model.label || model.slot || "Model";
    button.className = model.slot === selectedSlot ? "active" : "";
    button.addEventListener("click", () => loadDriverPreviewSlot(model.slot));
    tabs.append(button);
  });
}

async function loadDriverPreviewSlot(slot) {
  const project = state.driverPreviewProject;
  if (!project || !window.LiveryLabDriverPreview?.load) return;
  const models = driverPreviewAvailableModels(project);
  const selected = models.find((model) => model.slot === slot) || models[0] || null;
  if (!selected) {
    setDriverPreviewStatus("No driver preview model is available.", true);
    return;
  }
  state.driverPreviewSelectedSlot = selected.slot || "helmet";
  renderDriverPreviewTabs(project, state.driverPreviewSelectedSlot);
  const empty = $("driverPreviewEmpty");
  if (empty) empty.textContent = "Loading driver model...";
  setDriverPreviewStatus(`Loading ${selected.label || selected.slot || "driver model"}...`);
  try {
    const result = await window.LiveryLabDriverPreview.load(project, {
      canvasId: "driverPreviewCanvas",
      slot: state.driverPreviewSelectedSlot,
    });
    if (empty) empty.textContent = "";
    const bindingText = Array.isArray(result?.textureBindings)
      ? result.textureBindings.map((item) => `${item.slot}: ${item.applied}`).join(", ")
      : "";
    setDriverPreviewStatus([
      `${result?.label || selected.label || "Driver model"} loaded`,
      result?.meshCount ? `${result.meshCount} mesh${result.meshCount === 1 ? "" : "es"}` : "",
      bindingText ? `textures ${bindingText}` : "",
    ].filter(Boolean).join(" / "));
  } catch (error) {
    if (empty) empty.textContent = "Driver preview failed";
    setDriverPreviewStatus(error?.message || String(error), true);
  }
}

async function startDriverPreviewWatcher(project = state.driverPreviewProject) {
  window.clearTimeout(state.driverPreviewWatchTimer);
  if (!project?.driverProjectRoot || !window.pywebview?.api?.start_driver_preview_watcher) return false;
  setDriverPreviewStatus("Starting PNG watcher...");
  const result = await window.pywebview.api.start_driver_preview_watcher({
    path: project.driverProjectRoot,
    allowedNames: ["helmet.png", "visor.png", "suit.png", "gloves.png"],
  });
  if (!result?.ok) {
    setDriverPreviewStatus(`Watcher failed: ${result?.message || ""}`, true);
    return false;
  }
  state.driverPreviewLastChangeId = Number(result.data?.change_id || 0);
  scheduleDriverPreviewWatcherPoll();
  setDriverPreviewStatus("Watching driver PNGs");
  return true;
}

async function stopDriverPreviewWatcher() {
  window.clearTimeout(state.driverPreviewWatchTimer);
  state.driverPreviewWatchTimer = 0;
  if (window.pywebview?.api?.stop_driver_preview_watcher) {
    await window.pywebview.api.stop_driver_preview_watcher();
  }
}

function scheduleDriverPreviewWatcherPoll() {
  window.clearTimeout(state.driverPreviewWatchTimer);
  if ($("driverPreviewModal")?.hidden) return;
  state.driverPreviewWatchTimer = window.setTimeout(() => pollDriverPreviewWatcher(), 600);
}

async function pollDriverPreviewWatcher() {
  try {
    const result = await window.pywebview.api.get_driver_preview_watcher_status();
    if (!result?.ok) {
      setDriverPreviewStatus(`Watcher status failed: ${result?.message || ""}`, true);
      scheduleDriverPreviewWatcherPoll();
      return;
    }
    const status = result.data || {};
    const changeId = Number(status.change_id || 0);
    if (changeId && changeId !== state.driverPreviewLastChangeId) {
      state.driverPreviewLastChangeId = changeId;
      const project = state.driverPreviewProject;
      if (project && window.LiveryLabDriverPreview?.reloadTextures) {
        setDriverPreviewStatus("Detected PNG update. Reloading visible driver texture...");
        const viewer = await window.LiveryLabDriverPreview.reloadTextures(project, {
          canvasId: "driverPreviewCanvas",
          slot: state.driverPreviewSelectedSlot,
        });
        const changed = (status.last_paths || [status.path]).filter(Boolean).map((path) => fileNameOf(path)).join(", ");
        const bindingText = Array.isArray(viewer?.textureBindings)
          ? viewer.textureBindings.map((item) => `${item.slot}: ${item.applied}`).join(", ")
          : "";
        let syncText = "";
        try {
          const markResult = await markDriverProfileProjectPngChanged(currentDriverPreviewProfile());
          if (markResult?.ok) {
            const affectedCount = Number(markResult.data?.affectedCount || 0);
            syncText = affectedCount
              ? `${affectedCount} linked My Skin${affectedCount === 1 ? "" : "s"} need Update in Game`
              : "no linked My Skin";
          } else if (markResult && !markResult.ok) {
            syncText = "Update marker failed";
          }
        } catch (_error) {
          syncText = "Update marker failed";
        }
        setDriverPreviewStatus([
          `Reloaded ${viewer?.label || state.driverPreviewSelectedSlot || "driver model"}`,
          changed ? `changed ${changed}` : "",
          bindingText ? `textures ${bindingText}` : "",
          syncText,
        ].filter(Boolean).join(" / "));
      }
    } else if (status.last_error) {
      setDriverPreviewStatus(`Watcher warning: ${status.last_error}`, true);
    }
  } catch (error) {
    setDriverPreviewStatus(`Watcher error: ${error?.message || error}`, true);
  }
  scheduleDriverPreviewWatcherPoll();
}

async function openDriverProfile3dPreview(profile = selectedDriverProfile()) {
  if (!profile?.id) return;
  if (!window.pywebview?.api?.prepare_driver_preview_project) {
    showStatusModal({
      type: "error",
      title: "Driver 3D Preview",
      subtitle: "Backend API missing",
      error: "This build cannot prepare driver preview projects yet.",
    });
    return;
  }
  if (!window.LiveryLabDriverPreview?.load) {
    showStatusModal({
      type: "error",
      title: "Driver 3D Preview",
      subtitle: "Viewer missing",
      error: "The driver preview viewer bundle is not loaded.",
    });
    return;
  }
  await withButtonWorking("openDriverProfile3dPreview", "Preparing...", async () => {
    const result = await window.pywebview.api.prepare_driver_preview_project({
      id: profile.id,
      carModel: profile.carModel || "",
      root: $("rootPath")?.value.trim() || "",
    });
    if (!showResult(result, "Driver 3D Preview", "")) return;
    const project = result.data?.project || null;
    const models = driverPreviewAvailableModels(project);
    if (!project || !models.length) {
      showStatusModal({
        type: "error",
        title: "Driver 3D Preview",
        subtitle: profile.name || profile.id || "Driver profile",
        error: "No driver preview model could be prepared.",
        details: (result.data?.warnings || []).join("\n"),
      });
      return;
    }
    state.driverPreviewProject = project;
    state.driverPreviewProfileKey = driverProfileKey(profile);
    state.driverPreviewSelectedSlot = models.some((model) => model.slot === state.driverPreviewSelectedSlot)
      ? state.driverPreviewSelectedSlot
      : models[0].slot;
    if ($("driverPreviewTitle")) $("driverPreviewTitle").textContent = `${profile.name || profile.id || "Driver"} 3D Preview`;
    if ($("driverPreviewSubtitle")) $("driverPreviewSubtitle").textContent = `${profile.carModel || "-"} / Dark scene`;
    if ($("driverPreviewModal")) $("driverPreviewModal").hidden = false;
    renderDriverPreviewAppearancePanel(project);
    renderDriverPreviewTabs(project, state.driverPreviewSelectedSlot);
    await loadDriverPreviewSlot(state.driverPreviewSelectedSlot);
    await startDriverPreviewWatcher(project);
    window.setTimeout(() => window.LiveryLabDriverPreview?.resize?.(), 40);
  });
}

function renderDriverSourceSkins() {
  const select = $("driverSourceSkin");
  const nameInput = $("driverProfileName");
  const createButton = $("createDriverProfileFromSkin");
  if (!select) return;
  const current = state.selectedDriverSourceSkinKey || select.value || "";
  const skins = driverCapableMySkins();
  select.innerHTML = "";
  if (!skins.length) {
    const option = document.createElement("option");
    option.value = "";
    option.textContent = "No supported driver-capable My Skin";
    select.append(option);
    state.selectedDriverSourceSkinKey = "";
    select.disabled = true;
    if (nameInput) nameInput.disabled = true;
    if (createButton) createButton.disabled = true;
    return;
  }
  skins.forEach((skin) => {
    const option = document.createElement("option");
    option.value = skin.key || "";
    option.textContent = [skin.display_name || skin.skin_id || "Skin", skin.car_model || ""].filter(Boolean).join(" / ");
    option.title = skin.key || "";
    select.append(option);
  });
  if (current && skins.some((skin) => skin.key === current)) {
    select.value = current;
  } else {
    select.value = skins[0]?.key || "";
  }
  state.selectedDriverSourceSkinKey = select.value || "";
  select.disabled = false;
  if (nameInput) {
    const source = skins.find((skin) => skin.key === state.selectedDriverSourceSkinKey) || skins[0];
    nameInput.disabled = false;
    nameInput.placeholder = source?.display_name || source?.skin_id || "Driver profile name";
  }
  if (createButton) createButton.disabled = !state.selectedDriverSourceSkinKey;
}

async function driverProfilePackageRoot() {
  if (!window.pywebview?.api?.get_driver_profile_package_export_root) return "";
  const result = await window.pywebview.api.get_driver_profile_package_export_root($("rootPath")?.value.trim() || "");
  return result?.ok && result.data?.path ? result.data.path : "";
}

async function pickDriverProfilePackageFolder(initialDir = "") {
  const result = await window.pywebview.api.choose_path("folder", ["All files (*.*)"], "", initialDir || "");
  return result?.ok && result.data?.path ? result.data.path : "";
}

async function pickDriverProfilePackageZip(initialDir = "") {
  const result = await window.pywebview.api.choose_path("file", ["LiveryLab Package (*.zip)", "ZIP (*.zip)"], "", initialDir || "");
  return result?.ok && result.data?.path ? result.data.path : "";
}

function renderDriverProfiles() {
  const list = $("driverProfileList");
  const detail = $("driverProfileDetail");
  const status = $("driverProfileStatus");
  const deleteButton = $("deleteDriverProfile");
  renderDriverSourceSkins();
  if (!list) return;
  const items = Array.isArray(state.driverProfiles) ? state.driverProfiles : [];
  if (!items.length) {
    state.selectedDriverProfileKey = "";
    list.innerHTML = `<div class="driver-profile-empty">${escapeHtml(t("no_driver_profiles"))}</div>`;
    if (detail) detail.innerHTML = `<div class="driver-profile-empty">${escapeHtml(t("no_driver_profiles_found"))}</div>`;
    if (status) status.textContent = "Empty";
    if (deleteButton) deleteButton.disabled = true;
    return;
  }
  const selected = selectedDriverProfile();
  state.selectedDriverProfileKey = driverProfileKey(selected);
  list.innerHTML = "";
  items.forEach((item) => {
    const usedByCount = Number(item.usedByCount || 0);
    const card = document.createElement("button");
    card.type = "button";
    card.className = "driver-profile-item";
    if (driverProfileKey(item) === state.selectedDriverProfileKey) card.classList.add("active");
    card.innerHTML = `
      ${driverProfileMarkHtml(item)}
      <div class="driver-profile-copy">
        <strong>${escapeHtml(item.name || item.id || "Driver")}</strong>
        <span>${escapeHtml(item.carModel || "-")} / ${escapeHtml(item.id || "-")}</span>
        <small>${usedByCount ? `${usedByCount} in use` : "Not used"}</small>
      </div>
    `;
    card.addEventListener("click", () => {
      state.selectedDriverProfileKey = driverProfileKey(item);
      renderDriverProfiles();
    });
    list.append(card);
  });
  if (deleteButton) deleteButton.disabled = !selected;
  if (status) status.textContent = selected ? "Selected" : "Ready";
  if (!detail || !selected) return;
  const files = Array.isArray(selected.files) ? selected.files : [];
  const usedBy = Array.isArray(selected.usedBySkins) ? selected.usedBySkins : [];
  const colorControls = driverProfileMaterialControls(selected).filter((item) => item?.id && item?.category === "suit");
  const canSaveAppearance = Boolean(colorControls.length);
  const sourcePngCount = driverProjectSourcePngs(selected).length;
  const usedByCount = Number(selected.usedByCount || 0);
  const projectInfo = driverProjectInfo(selected);
  const canOpenProjectFolder = Boolean(projectInfo.exists && String(projectInfo.projectRoot || selected.projectRoot || "").trim() && window.pywebview?.api?.open_folder);
  const canOpenDriverPreview = Boolean(projectInfo.exists && sourcePngCount && window.pywebview?.api?.prepare_driver_preview_project);
  detail.innerHTML = `
    <div class="driver-profile-detail-head">
      <div class="driver-profile-hero">
        ${driverProfileMarkHtml(selected, { large: true })}
        <div class="driver-profile-copy">
          <strong>${escapeHtml(selected.name || selected.id || "Driver")}</strong>
          <span>${escapeHtml([selected.carModel || "", selected.id || ""].filter(Boolean).join(" / "))}</span>
        </div>
      </div>
      <div class="driver-profile-badges">
        <span class="status complete driver-profile-status-pill">PNG sources: ${sourcePngCount}</span>
        <span class="status ${usedByCount ? "complete" : "partial"} driver-profile-status-pill">Used by ${usedByCount} skin${usedByCount === 1 ? "" : "s"}</span>
      </div>
    </div>
    <div class="driver-profile-detail-toolbar">
      <button id="openDriverProfile3dPreview" type="button" class="primary icon-text preview driver-profile-preview-action" data-tutorial-id="drivers-preview-3d"${canOpenDriverPreview ? "" : " disabled"}>${uiIcon("cube")}3D Preview</button>
      ${canSaveAppearance ? `<button id="saveDriverProfileAppearance" type="button" class="primary icon-text" data-tutorial-id="drivers-save-appearance">${uiIcon("save")}Save appearance</button>` : ""}
      <button id="openDriverProfileProjectFolder" type="button" class="icon-text" data-tutorial-id="drivers-project-folder"${canOpenProjectFolder ? "" : " disabled"}>${uiIcon("folder")}${escapeHtml(t("open_project_folder"))}</button>
      <button id="exportDriverProfilePackage" type="button" class="icon-text" data-tutorial-id="drivers-export-package">${uiIcon("export")}Export package</button>
      <button id="deleteDriverProfile" type="button" class="danger-outline icon-text" data-tutorial-id="drivers-remove">${uiIcon("trash")}Remove</button>
    </div>
    ${driverProjectPngGridHtml(selected)}
    <div class="driver-profile-workflow-grid">
      ${driverTeamLogoEditorHtml(selected)}
      ${driverProfileDetailsEditorHtml(selected)}
      ${driverAppearanceEditorHtml(selected)}
    </div>
    <details class="driver-profile-technical">
      <summary>Files and usage</summary>
      <pre class="output small">${escapeHtml([
        "Used by:",
        ...(usedBy.length ? usedBy.map((skin) => `- ${skin.displayName || skin.skinId || skin.key || "-"} (${skin.carModel || "-"})`) : ["-"]),
        "",
        "Files:",
        ...(files.length ? files.map((file) => `- ${file}`) : ["-"]),
      ].join("\n"))}</pre>
    </details>
  `;
  $("saveDriverProfileDetails")?.addEventListener("click", () => saveDriverProfileDetails(selected));
  bindDriverAppearanceEditor(selected);
  $("openDriverProfile3dPreview")?.addEventListener("click", () => openDriverProfile3dPreview(selected));
  $("openDriverProfileProjectFolder")?.addEventListener("click", () => openDriverProfileProjectFolder(selected));
  $("chooseDriverProfilePreviewPng")?.addEventListener("click", () => chooseDriverProfilePreviewPng(selected));
  $("exportDriverProfilePackage")?.addEventListener("click", () => exportSelectedDriverProfilePackage(selected));
  $("deleteDriverProfile")?.addEventListener("click", () => deleteSelectedDriverProfile());
  loadDriverProfilePreviewImages();
  loadDriverPngPreviews(selected);
  scheduleDriverPngPreviewRefresh(selected);
}

async function loadDriverProfiles() {
  if (!window.pywebview?.api?.list_driver_profiles) {
    renderDriverProfiles();
    return;
  }
  const requestId = (state.driverProfilesLoadRequestId || 0) + 1;
  state.driverProfilesLoadRequestId = requestId;
  state.driverProfilesLoading = true;
  setToolLoading("drivers", true);
  if ($("driverProfileStatus")) $("driverProfileStatus").textContent = "Loading";
  try {
    const result = await window.pywebview.api.list_driver_profiles({});
    if (requestId !== state.driverProfilesLoadRequestId) return;
    if (result.ok) {
      state.driverProfiles = Array.isArray(result.data?.items) ? result.data.items : [];
      state.driverProfilesLoadedCars = new Set(state.driverProfiles.map((item) => driverCarKey(item.carModel)).filter(Boolean));
    } else {
      state.driverProfiles = [];
      state.driverProfilesLoadedCars = new Set();
      showResult(result, "Drivers", "list_driver_profiles");
    }
  } catch (_error) {
    if (requestId !== state.driverProfilesLoadRequestId) return;
    state.driverProfiles = [];
    state.driverProfilesLoadedCars = new Set();
  } finally {
    if (requestId === state.driverProfilesLoadRequestId) {
      state.driverProfilesLoading = false;
      setToolLoading("drivers", false);
      renderDriverProfiles();
    }
  }
}

async function ensureDriverProfilesLoaded(carModel = "") {
  if (!window.pywebview?.api?.list_driver_profiles) return [];
  const carKey = driverCarKey(carModel);
  if (carKey && state.driverProfilesLoadedCars?.has(carKey)) {
    return driverProfilesForCar(carModel);
  }
  if (carKey && state.driverProfilesLoadingCars?.has(carKey)) {
    return [];
  }
  if (carKey) state.driverProfilesLoadingCars.add(carKey);
  const result = await window.pywebview.api.list_driver_profiles(carModel ? { carModel } : {});
  if (carKey) state.driverProfilesLoadingCars.delete(carKey);
  if (result?.ok) {
    const loaded = Array.isArray(result.data?.items) ? result.data.items : [];
    const existing = new Map((Array.isArray(state.driverProfiles) ? state.driverProfiles : []).map((item) => [driverProfileKey(item), item]));
    loaded.forEach((item) => existing.set(driverProfileKey(item), item));
    state.driverProfiles = [...existing.values()].sort((a, b) => String(a.carModel || "").localeCompare(String(b.carModel || "")) || String(a.name || a.id || "").localeCompare(String(b.name || b.id || "")));
    if (carKey) state.driverProfilesLoadedCars.add(carKey);
    renderDriverProfiles();
    return loaded;
  }
  showResult(result, "Drivers", "list_driver_profiles");
  return [];
}

async function loadDriverSourceSkins() {
  if (!window.pywebview?.api?.get_my_skins) {
    renderDriverSourceSkins();
    return;
  }
  try {
    const result = await window.pywebview.api.get_my_skins({
      root: $("rootPath")?.value.trim() || "",
      includeHidden: false,
      scope: "advanced",
    });
    if (result.ok) {
      state.mySkins = Array.isArray(result.data?.skins) ? result.data.skins : [];
    }
  } catch (_error) {
    if (!Array.isArray(state.mySkins)) state.mySkins = [];
  }
  renderDriverSourceSkins();
}

async function createDriverProfileFromSelectedSkin() {
  const skinKey = $("driverSourceSkin")?.value || state.selectedDriverSourceSkinKey || "";
  const source = (Array.isArray(state.mySkins) ? state.mySkins : []).find((skin) => skin.key === skinKey);
  if (!skinKey || !source) {
    log("Please select a driver-capable My Skin first", true);
    return;
  }
  const name = $("driverProfileName")?.value.trim() || source.display_name || source.skin_id || "";
  await withButtonWorking("createDriverProfileFromSkin", "Creating...", async () => {
    const result = await call("create_driver_profile_from_skin", { skinKey, name });
    if (!result.ok) return;
    const item = result.data?.item || {};
    state.selectedDriverProfileKey = driverProfileKey(item);
    if ($("driverProfileName")) $("driverProfileName").value = "";
    await loadDriverProfiles();
    showStatusModal({
      type: "success",
      title: "Driver profile created",
      subtitle: item.name || item.id || "Profile ready",
      message: `Created driver profile from "${source.display_name || source.skin_id || "My Skin"}".`,
      details: [
        result.message || "",
        `Car: ${item.carModel || source.car_model || "-"}`,
        `Files: ${Array.isArray(item.files) ? item.files.length : 0}`,
        `Manifest: ${item.manifestPath || "-"}`,
      ].filter(Boolean).join("\n"),
    });
  });
}

async function exportSelectedDriverProfilePackage(profile = selectedDriverProfile()) {
  if (!profile?.id) {
    showStatusModal({
      type: "error",
      title: "Driver Profiles",
      subtitle: "No driver profile selected",
      error: "Select a driver profile before exporting a package.",
    });
    return;
  }
  if (!window.pywebview?.api?.export_driver_profile_package) {
    log("Driver profile package export is not available.", true);
    return;
  }
  const outDir = await pickDriverProfilePackageFolder(state.driverProfileExportDir || await driverProfilePackageRoot());
  if (!outDir) return;
  state.driverProfileExportDir = outDir;
  await withButtonWorking("exportDriverProfilePackage", "Exporting...", async () => {
    const result = await call("export_driver_profile_package", {
      id: profile.id,
      carModel: profile.carModel || "",
      outDir,
      root: $("rootPath")?.value.trim() || "",
    });
    if (!result.ok) return;
    showStatusModal({
      type: "success",
      title: "Driver profile exported",
      subtitle: profile.name || profile.id || "Package ready",
      message: "The driver profile package was exported successfully.",
      details: result.message || `Package: ${result.data?.packagePath || result.data?.packageRoot || "-"}`,
      openFolderPath: result.data?.packagePath || result.data?.packageRoot || "",
    });
  });
}

async function importDriverProfilePackage() {
  if (!window.pywebview?.api?.inspect_driver_profile_package || !window.pywebview?.api?.import_driver_profile_package) {
    log("Driver profile package import is not available.", true);
    return;
  }
  const packageDir = await pickDriverProfilePackageZip(state.driverProfilePackageDir || await driverProfilePackageRoot());
  if (!packageDir) return;
  state.driverProfilePackageDir = packageDir;
  log(`${t("log_running")} inspect_driver_profile_package...`);
  const inspected = await window.pywebview.api.inspect_driver_profile_package(packageDir);
  if (!inspected.ok) {
    showResult(inspected, "Driver profile package inspection failed", "inspect_driver_profile_package");
    return;
  }
  const driver = inspected.data?.driver || {};
  const manifest = driver.manifest || {};
  const missingCount = Number(inspected.data?.missingCount || 0);
  if (missingCount) {
    showStatusModal({
      type: "error",
      title: "Driver profile package",
      subtitle: "Package is incomplete",
      error: `${missingCount} package file${missingCount === 1 ? "" : "s"} missing.`,
      details: (inspected.data?.missing || []).map((item) => `- ${item.kind || "file"}: ${item.path || "-"} ${item.reason ? `(${item.reason})` : ""}`).join("\n"),
    });
    return;
  }
  const alreadyInstalled = Boolean(inspected.data?.installed);
  const proceed = await showConfirmModal({
    type: alreadyInstalled ? "info" : "info",
    title: "Import driver profile package",
    message: alreadyInstalled
      ? "This driver profile is already installed. Import will leave the existing profile unchanged."
      : "Import this driver profile into your local driver library?",
    details: [
      `Profile: ${manifest.name || driver.label || driver.id || "-"}`,
      `Car: ${driver.carModel || "-"}`,
      `ZIP: ${packageDir}`,
      `Profile files: ${inspected.data?.profileFileCount || 0}`,
      `Project embedded: ${inspected.data?.projectEmbedded ? "yes" : "no"}`,
    ].join("\n"),
    confirmLabel: "Import Package",
    cancelLabel: "Cancel",
  });
  if (!proceed) return;
  log(`${t("log_running")} import_driver_profile_package...`);
  const result = await window.pywebview.api.import_driver_profile_package({ packageDir, conflictMode: "rename" });
  if (!result.ok) {
    showResult(result, "Driver profile package import failed", "import_driver_profile_package");
    return;
  }
  if (result.message) log(result.message);
  const item = result.data?.driver || {};
  if (item?.id) state.selectedDriverProfileKey = driverProfileKey(item);
  await loadDriverProfiles();
  await loadMySkins();
  showStatusModal({
    type: "success",
    title: result.data?.status === "already_installed" ? "Driver profile already installed" : "Driver profile imported",
    subtitle: item.name || result.data?.profileId || "Local profile ready",
    message: result.data?.status === "already_installed"
      ? "The local driver profile was already available."
      : (result.data?.renamed
        ? "The driver profile was imported as a new copy because the original profile already exists."
        : "The driver profile is now available for My Liveries."),
    details: result.message || "",
  });
}

async function deleteSelectedDriverProfile() {
  const selected = selectedDriverProfile();
  if (!selected?.id) return;
  const usedBy = Array.isArray(selected.usedBySkins) ? selected.usedBySkins : [];
  if (usedBy.length) {
    showStatusModal({
      type: "error",
      title: "Driver profile in use",
      subtitle: `${usedBy.length} My Skin${usedBy.length === 1 ? "" : "s"} still use this profile`,
      message: "Switch those skins to another driver profile or back to the skin driver before deleting this profile.",
      details: usedBy.map((skin) => `- ${skin.displayName || skin.skinId || skin.key || "-"} (${skin.carModel || "-"})`).join("\n"),
    });
    return;
  }
  const proceed = await showConfirmModal({
    type: "danger",
    title: "Delete driver profile?",
    message: `Remove "${selected.name || selected.id}" from the local driver library?`,
    details: [
      `Car: ${selected.carModel || "-"}`,
      `ID: ${selected.id || "-"}`,
      `Files: ${Array.isArray(selected.files) ? selected.files.length : 0}`,
      driverProfileUsageLabel(selected),
      "",
      "Skins that already copied these driver files keep their local assets.",
    ].join("\n"),
    confirmLabel: "Delete profile",
    cancelLabel: "Cancel",
  });
  if (!proceed) return;
  const result = await call("delete_driver_profile", { id: selected.id, carModel: selected.carModel || "" });
  if (!result.ok) return;
  state.selectedDriverProfileKey = "";
  await loadDriverProfiles();
}

function newTyreBrandTemplate() {
  state.selectedTyreBrandId = "";
  state.tyreBrandDraft = newTyreBrandDraft();
  renderCustomTyreBrands();
}

async function saveTyreBrandTemplate() {
  updateTyreBrandDraftFromInputs();
  const draft = currentTyreBrandDraft();
  const root = $("rootPath")?.value.trim() || "";
  if (!root || !draft.brandName || !draft.slickPng || !draft.blurPng) {
    showStatusModal({
      type: "error",
      title: "Tyre Brands",
      subtitle: "Missing inputs",
      error: "Root, template name, slick PNG and blur PNG are required.",
    });
    return;
  }
  await withButtonWorking("saveTyreBrandTemplate", "Saving...", async () => {
    const result = await call("save_custom_tyre_brand_template", {
      root,
      id: draft.id,
      isNew: draft.isNew,
      brandName: draft.brandName,
      assetId: draft.assetId,
      template: draft.template,
      slickPng: draft.slickPng,
      blurPng: draft.blurPng,
      markPng: draft.markPng,
    });
    if (!result.ok) return;
    const item = result.data?.item || {};
    state.selectedTyreBrandId = item.id || draft.id || "";
    state.tyreBrandDraft = null;
    state.tyreBrandPreviewCache = {};
    await loadCustomTyreBrands();
    await loadMySkins();
    showStatusModal({
      type: "success",
      title: "Tyre brand template saved",
      subtitle: item.label || item.id || "Local asset ready",
      message: "The racing tyre brand template was saved locally. Skins using it will update the game on their next Update in Game.",
      details: result.message || "",
    });
  });
}

async function deleteTyreBrandTemplate() {
  const selected = currentTyreBrandItem();
  if (!selected?.id) return;
  log(`${t("log_running")} inspect_custom_tyre_brand_usage...`);
  const usageResult = await window.pywebview.api.inspect_custom_tyre_brand_usage({ id: selected.id });
  if (!usageResult.ok) {
    showResult(usageResult, "Tyre brand usage inspection failed", "inspect_custom_tyre_brand_usage");
    return;
  }
  const usage = usageResult.data || {};
  const skins = Array.isArray(usage.usedBySkins) ? usage.usedBySkins : [];
  const skinCount = Number(usage.skinCount || skins.length || 0);
  const proceed = await showConfirmModal({
    type: skinCount ? "warning" : "danger",
    title: skinCount ? "Delete used tyre brand?" : "Delete tyre brand template?",
    message: skinCount ? "This tyre brand is still referenced by one or more local skins." : "This removes the tyre brand from your local library.",
    details: [
      `Template: ${selected.label || selected.id}`,
      "",
      skinCount
        ? `Affected skins will be reset to their default tyre setup and marked as needing Update in Game.`
        : "No skin currently references this tyre brand.",
      ...skins.slice(0, 12).map((skin) => `- ${[skin.displayName || skin.skinId || skin.key || "Skin", skin.carModel || ""].filter(Boolean).join(" / ")}`),
      skinCount > 12 ? `- ...and ${skinCount - 12} more` : "",
      "",
      "Existing custom tyre files already copied into AC EVO are not removed automatically.",
    ].filter(Boolean).join("\n"),
    confirmLabel: skinCount ? "Delete and reset skins" : "Delete template",
    cancelLabel: "Cancel",
  });
  if (!proceed) return;
  const result = await call("delete_custom_tyre_brand_template", { id: selected.id, resetUsers: true });
  if (!result.ok) return;
  state.selectedTyreBrandId = "";
  state.tyreBrandDraft = null;
  await loadCustomTyreBrands();
  await loadMySkins();
}

async function activateCustomNumberplateAsset(asset) {
  const root = $("rootPath")?.value.trim() || "";
  const assetType = String(asset?.type || "").trim();
  const assetId = String(asset?.id || "").trim();
  if (!root || !assetType || !assetId) {
    log("Please set root and select a custom numberplate asset first", true);
    return;
  }
  const result = await showConfirmModal({
    type: "warning",
    title: "Activate custom numberplate",
    message: "Write this custom numberplate asset into the AC EVO common_assets folder?",
    details: [
      `Type: ${assetType}`,
      `Name: ${asset?.name || assetId}`,
      `Base template: ${asset?.baseTemplate || "-"}`,
      `Source PNG: ${asset?.sourcePngPath || asset?.sourcePng || "-"}`,
      "",
      "Writes:",
      "- Clone and patch the base .texture descriptor.",
      "- Convert the PNG to .texturemips.",
      "- Back up existing target files before overwrite.",
    ].join("\n"),
    confirmLabel: "Activate in Game",
    loadingLabel: "Activating...",
    onConfirm: async () => {
      log(`${t("log_running")} activate_custom_numberplate_asset...`);
      return window.pywebview.api.activate_custom_numberplate_asset({
        root,
        type: assetType,
        id: assetId,
      });
    },
  });
  if (!result) return;
  if (!result.ok) {
    showResult(result, "activate_custom_numberplate_asset", "activate_custom_numberplate_asset");
    return;
  }
  await loadCustomNumberplateAssets();
  await loadNumberplateTemplateOptions();
  showResult(result, "activate_custom_numberplate_asset", "activate_custom_numberplate_asset");
}

async function replaceCustomNumberplateAssetPng(asset) {
  const root = $("rootPath")?.value.trim() || "";
  const assetType = String(asset?.type || "").trim();
  const assetId = String(asset?.id || "").trim();
  if (!root || !assetType || !assetId) {
    log("Please set root and select a custom numberplate asset first", true);
    return;
  }
  const picked = await window.pywebview.api.choose_path("file", ["PNG (*.png)", "All files (*.*)"], "");
  if (!showResult(picked, t("log_path_selected")) || !picked.data?.path) return;
  const confirmed = await showConfirmModal({
    type: "warning",
    title: "Replace PNG",
    message: "Replace the local PNG for this custom numberplate asset?",
    details: [
      `Type: ${assetType}`,
      `Name: ${asset?.name || assetId}`,
      `PNG: ${picked.data.path}`,
      "",
      "The previous local PNG is backed up in the asset folder.",
    ].join("\n"),
    confirmLabel: "Replace PNG",
  });
  if (!confirmed) return;
  const refresh = asset.active
    ? await showConfirmModal({
      type: "warning",
      title: "Refresh in Game",
      message: "This asset is active in game. Write the updated texture now?",
      details: [
        "Refreshing in game writes the updated .texture/.texturemips with backups.",
        "Choose Later if you want to use Refresh in Game manually afterwards.",
      ].join("\n"),
      confirmLabel: "Refresh in Game",
      cancelLabel: "Later",
      loadingLabel: "Refreshing...",
      onConfirm: async () => {
        log(`${t("log_running")} replace_custom_numberplate_asset_png...`);
        return window.pywebview.api.replace_custom_numberplate_asset_png({
          root,
          type: assetType,
          id: assetId,
          png: picked.data.path,
          refresh: true,
        });
      },
    })
    : false;
  const result = refresh && typeof refresh === "object"
    ? refresh
    : await call("replace_custom_numberplate_asset_png", {
      root,
      type: assetType,
      id: assetId,
      png: picked.data.path,
      refresh: false,
    });
  if (refresh && typeof refresh === "object") showResult(result, "replace_custom_numberplate_asset_png", "replace_custom_numberplate_asset_png");
  if (!result.ok) return;
  window.LiveryLabNumberplates?.clearPreviewCache?.(asset.sourcePngPath || "");
  await loadCustomNumberplateAssets();
  await loadNumberplateTemplateOptions();
}

function formatNumberplateUsageDetails(usedBy = []) {
  const rows = Array.isArray(usedBy) ? usedBy : [];
  if (!rows.length) return ["Used by: no LiveryLab-managed skin"];
  const visible = rows.slice(0, 8).map((row) => {
    const car = String(row.carModel || "").trim();
    const label = String(row.displayName || row.skinId || row.skinKey || "").trim() || "Skin";
    const stateLabel = row.active === false ? "local only" : "active in game";
    return `- ${car ? `${car} / ` : ""}${label} (${stateLabel})`;
  });
  if (rows.length > visible.length) visible.push(`- ... ${rows.length - visible.length} more`);
  return ["Used by:", ...visible];
}

async function deactivateCustomNumberplateAsset(asset) {
  const root = $("rootPath")?.value.trim() || "";
  const assetType = String(asset?.type || "").trim();
  const assetId = String(asset?.id || "").trim();
  if (!root || !assetType || !assetId) {
    log("Please set root and select a custom numberplate asset first", true);
    return;
  }
  let usage = Array.isArray(asset?.usedBy) ? asset.usedBy : [];
  if (window.pywebview?.api?.inspect_custom_numberplate_usage) {
    try {
      const usageResult = await call("inspect_custom_numberplate_usage", { root, type: assetType, id: assetId });
      if (usageResult.ok) usage = usageResult.data?.usedBy || usage;
    } catch (_error) {
      usage = Array.isArray(asset?.usedBy) ? asset.usedBy : [];
    }
  }
  const result = await showConfirmModal({
    type: "warning",
    title: "Deactivate custom numberplate",
    message: "Remove this custom numberplate asset from the AC EVO common_assets folder?",
    details: [
      `Type: ${assetType}`,
      `Name: ${asset?.name || assetId}`,
      `Game texture: ${asset?.gameTextureRel || "-"}`,
      `Fallback: ${asset?.baseTemplate || "-"}`,
      "",
      ...formatNumberplateUsageDetails(usage),
      "",
      "LiveryLab-managed skins that use this asset are switched back to the fallback first.",
      "Active skins are synced to the game before the custom files are removed.",
      "The local LiveryLibrary asset stays available.",
      "Existing game files are backed up before they are removed.",
    ].join("\n"),
    confirmLabel: "Deactivate from Game",
    loadingLabel: "Deactivating...",
    onConfirm: async () => {
      log(`${t("log_running")} deactivate_custom_numberplate_asset...`);
      return window.pywebview.api.deactivate_custom_numberplate_asset({
        root,
        type: assetType,
        id: assetId,
      });
    },
  });
  if (!result) return;
  if (!result.ok) {
    showResult(result, "deactivate_custom_numberplate_asset", "deactivate_custom_numberplate_asset");
    return;
  }
  await loadCustomNumberplateAssets();
  await loadNumberplateTemplateOptions();
  showResult(result, "deactivate_custom_numberplate_asset", "deactivate_custom_numberplate_asset");
}

function setSkinTemplateAdvancedVisible(visible) {
  const panel = $("skinTemplateAdvanced");
  const button = $("toggleSkinTemplateAdvanced");
  if (!panel || !button) return;
  panel.hidden = !visible;
  button.setAttribute("aria-expanded", visible ? "true" : "false");
  button.textContent = visible ? "Hide advanced template files" : "Show advanced template files";
}

async function collectNumberplateTexturesViaBrowser(baseRel) {
  const root = $("rootPath").value.trim();
  if (!root) return [];
  let base;
  try {
    base = await window.pywebview.api.list_dir(root, baseRel);
  } catch (_error) {
    return [];
  }
  if (!base.ok) return [];
  const textures = [];
  for (const item of base.data?.items || []) {
    if (item.isDir) {
      let nested;
      try {
        nested = await window.pywebview.api.list_dir(root, item.rel);
      } catch (_error) {
        continue;
      }
      if (!nested.ok) continue;
      for (const child of nested.data?.items || []) {
        if (!child.isDir && extensionOf(child.name || child.path || "") === "texture") {
          textures.push(String(child.rel || "").replace(/\//g, "\\"));
        }
      }
    } else if (extensionOf(item.name || item.path || "") === "texture") {
      textures.push(String(item.rel || "").replace(/\//g, "\\"));
    }
  }
  return textures.sort((a, b) => a.localeCompare(b));
}

async function loadNumberplateTemplateOptions() {
  // Legacy AC EVO base-template loader. Keep this for dormant debug/game-export
  // controls, but do not treat the counts as relevant normal startup status.
  const root = $("rootPath").value.trim();
  await loadNumberplateStudioTemplatesForPreview();
  let fonts = [];
  let backgrounds = [];
  if (root && typeof window.pywebview.api.list_numberplate_templates === "function") {
    try {
      const result = await window.pywebview.api.list_numberplate_templates(root);
      if (result.ok) {
        fonts = result.data?.fonts || [];
        backgrounds = result.data?.backgrounds || [];
      }
    } catch (_error) {
      fonts = [];
      backgrounds = [];
    }
  }
  if (root && (!fonts.length || !backgrounds.length)) {
    if (!fonts.length) fonts = await collectNumberplateTexturesViaBrowser("content\\cars\\common_assets\\numberplates\\fonts");
    if (!backgrounds.length) backgrounds = await collectNumberplateTexturesViaBrowser("content\\cars\\common_assets\\numberplates\\backgrounds");
  }
  state.numberplateTemplates = { fonts, backgrounds };
  renderNumberplateStudioTemplateSelect("skinNumberplateTemplate");
  renderNumberplateStudioTemplateSelect("mySkinNumberplateTemplate");
  renderNumberplateSelect("skinNumberplateFont", fonts, "Keep source/default font");
  renderNumberplateSelect("skinNumberplateBackground", backgrounds, "Keep source/default background");
  renderNumberplateSelect("numberplateCreatorBase", backgrounds, "Choose base background");
  renderNumberplateSelect("numberplateFontCreatorBase", fonts, "Choose base font");
  renderNumberplateSelect("mySkinNumberplateFont", fonts, "Keep current/default font");
  renderNumberplateSelect("mySkinNumberplateBackground", backgrounds, "Keep current/default background");
  renderNumberplateSelect("mySkinNumberplateHoodBackground", backgrounds, "Keep/current hood background");
  renderNumberplateSelect("mySkinNumberplateLeftBackground", backgrounds, "Keep/current vehicle-left background");
  renderNumberplateSelect("mySkinNumberplateRightBackground", backgrounds, "Keep/current vehicle-right background");
  renderNumberplateAssetLibrary();
}

function renderNumberplateCreatorSummary(data) {
  const target = $("numberplateCreatorSummary");
  if (!target) return;
  target.innerHTML = "";
  if (!data) return;
  const rows = [
    ["Name", data.name || ""],
    ["Type", data.type || ""],
    ["Base template", data.baseTemplate || ""],
    ["Source PNG", data.sourcePngPath || data.sourcePng || ""],
    ["Asset folder", data.assetDir || ""],
  ];
  rows.forEach(([label, value]) => {
    if (!value) return;
    const chip = document.createElement("div");
    chip.className = "chip";
    chip.innerHTML = `<strong>${escapeHtml(label)}</strong><span>${escapeHtml(value)}</span>`;
    target.append(chip);
  });
}

function updateMySkinNumberplateModeUi() {
  const mode = $("mySkinNumberplateMode")?.value || "same";
  const surface = mode === "surface";
  const hasHood = $("mySkinNumberplateMode")?.dataset.hasHood === "1";
  $("mySkinNumberplateBackgroundWrap")?.toggleAttribute("hidden", surface);
  document.querySelectorAll(".my-skin-numberplate-surface").forEach((node) => {
    const isHood = node.classList.contains("my-skin-numberplate-hood");
    node.toggleAttribute("hidden", !surface || (isHood && !hasHood));
  });
  document.querySelectorAll(".my-skin-numberplate-split").forEach((node) => {
    node.toggleAttribute("hidden", !surface);
  });
}

function clearMySkinAcevoNumberplateControls() {
  [
    "mySkinNumberplateFont",
    "mySkinNumberplateBackground",
    "mySkinNumberplateHoodBackground",
    "mySkinNumberplateLeftBackground",
    "mySkinNumberplateRightBackground",
  ].forEach((id) => {
    const select = $(id);
    if (select) select.value = "";
  });
}

function updateMySkinNumberplateSourceUi() {
  const source = $("mySkinNumberplateSource")?.value || "acevo";
  const isTemplate = source === "template";
  const isNone = source === "none";
  const isRoadDefault = source === "road_default";
  const isRoadCustom = source === "road_custom";
  const templatePanel = $("mySkinNumberplateTemplatePanel");
  const templateMissing = templatePanel?.dataset.templateMissing === "1";
  $("mySkinNumberplateModeWrap")?.toggleAttribute("hidden", source !== "acevo");
  $("mySkinNumberplateEvoPanel")?.toggleAttribute("hidden", source !== "acevo");
  $("mySkinRoadPlateDefaultPanel")?.toggleAttribute("hidden", !isRoadDefault);
  $("mySkinRoadPlateCustomPanel")?.toggleAttribute("hidden", !isRoadCustom);
  templatePanel?.toggleAttribute("hidden", !isTemplate);
  $("mySkinNumberplateNonePanel")?.toggleAttribute("hidden", !isNone);
  $("saveMySkinNumberplate")?.toggleAttribute("disabled", state.mySkinsDetailBusy === true || (isTemplate && templateMissing));
  if (source === "acevo") updateMySkinNumberplateModeUi();
}

async function createNumberplateBackground() {
  if (!NUMBERPLATE_STUDIO_GAME_EXPORT_ENABLED) {
    log("Numberplate Studio game export is disabled. Use PNG export instead.", true);
    return;
  }
  const root = $("rootPath")?.value.trim() || "";
  const sourceBackground = $("numberplateCreatorBase")?.value.trim() || "";
  const name = $("numberplateCreatorName")?.value.trim() || "";
  const png = $("numberplateCreatorPng")?.value.trim() || "";
  const overwrite = !!$("numberplateCreatorOverwrite")?.checked;
  if (!root || !sourceBackground || !name || !png) {
    log("Please set root, base background, new background name and PNG first", true);
    return;
  }
  const result = await call("create_custom_numberplate_asset", {
    root,
    type: "background",
    baseTemplate: sourceBackground,
    name,
    png,
    overwrite,
  });
  if (!result.ok) return;
  renderNumberplateCreatorSummary(result.data);
  await loadCustomNumberplateAssets();
  setInspectOutputText(result.message || "");
}

async function createNumberplateFont() {
  if (!NUMBERPLATE_STUDIO_GAME_EXPORT_ENABLED) {
    log("Numberplate Studio game export is disabled. Use PNG export instead.", true);
    return;
  }
  const root = $("rootPath")?.value.trim() || "";
  const baseTemplate = $("numberplateFontCreatorBase")?.value.trim() || "";
  const name = $("numberplateFontCreatorName")?.value.trim() || "";
  const png = $("numberplateFontCreatorPng")?.value.trim() || "";
  const overwrite = !!$("numberplateFontCreatorOverwrite")?.checked;
  if (!root || !baseTemplate || !name || !png) {
    log("Please set root, base font, new font name and PNG first", true);
    return;
  }
  const result = await call("create_custom_numberplate_asset", {
    root,
    type: "font",
    baseTemplate,
    name,
    png,
    overwrite,
  });
  if (!result.ok) return;
  renderNumberplateCreatorSummary(result.data);
  await loadCustomNumberplateAssets();
  setInspectOutputText(result.message || "");
}

function renderCrumbs() {
  const crumbs = $("crumbs");
  crumbs.innerHTML = "";
  const rootButton = document.createElement("button");
  rootButton.textContent = "Root";
  rootButton.addEventListener("click", () => refreshBrowser(""));
  crumbs.append(rootButton);

  let acc = "";
  state.currentRel.split(/[\\/]/).filter(Boolean).forEach((part) => {
    acc = acc ? `${acc}\\${part}` : part;
    const button = document.createElement("button");
    button.textContent = part;
    button.addEventListener("click", () => refreshBrowser(button.dataset.rel));
    button.dataset.rel = acc;
    crumbs.append(button);
  });
}

function currentBrowserPath() {
  const root = $("rootPath").value.trim();
  if (!root) return "";
  if (!state.currentRel) return root;
  return `${root}\\${state.currentRel}`;
}

function defaultFavoriteNameFromPath(path) {
  const parts = String(path || "").split(/[\\/]/).filter(Boolean);
  return parts[parts.length - 1] || "Folder";
}

function normalizeBrowserFavoritePath(path) {
  return String(path || "").trim().replace(/[\\/]+$/, "").replace(/\//g, "\\").toLowerCase();
}

function browserFavoriteForPath(path) {
  const key = normalizeBrowserFavoritePath(path);
  return (state.browserFavorites || []).find((item) => normalizeBrowserFavoritePath(item.path) === key) || null;
}

function isBrowserFavoritePath(path) {
  return Boolean(browserFavoriteForPath(path));
}

function normalizeRelPath(path) {
  return String(path || "").replace(/\\/g, "/").replace(/^\/+|\/+$/g, "").toLowerCase();
}

function isCarsRootRel(rel = state.currentRel) {
  return normalizeRelPath(rel) === "content/cars";
}

function relPathParts(rel = state.currentRel) {
  return normalizeRelPath(rel).split("/").filter(Boolean);
}

function isCarContentFolderRel(rel = state.currentRel) {
  const parts = relPathParts(rel);
  return parts.length === 3 && parts[0] === "content" && parts[1] === "cars";
}

function isCarSkinsFolderRel(rel = state.currentRel) {
  const parts = relPathParts(rel);
  return parts.length === 4 && parts[0] === "content" && parts[1] === "cars" && parts[3] === "skins";
}

function isWithinCarSkinsRel(rel = state.currentRel) {
  const parts = relPathParts(rel);
  return parts.length >= 4 && parts[0] === "content" && parts[1] === "cars" && parts[3] === "skins";
}

function isWithinCarTexturesRel(rel = state.currentRel) {
  const parts = relPathParts(rel);
  return parts.length >= 4 && parts[0] === "content" && parts[1] === "cars" && parts[3] === "textures";
}

function isSkinCreatorBrowserTarget(item) {
  if (!item) return false;
  if (isCarContentFolderRel() && item.isDir) {
    return String(item.name || "").toLowerCase() === "skins";
  }
  if (isWithinCarSkinsRel() && !item.isDir) {
    return extensionOf(item.name) === "cardesign";
  }
  return false;
}

function isTextureConverterBrowserTarget(item) {
  if (!item) return false;
  if (isCarContentFolderRel() && item.isDir) {
    return ["skins", "textures"].includes(String(item.name || "").toLowerCase());
  }
  if (isWithinCarTexturesRel() && !item.isDir) {
    return ["texture", "texturemips"].includes(extensionOf(item.name || item.path || ""));
  }
  return false;
}

function isSkinTemplateBrowserItem(item) {
  return Boolean(item && isWithinCarSkinsRel() && !item.isDir && extensionOf(item.name || item.path || "") === "cardesign");
}

function liveryLabCardesignPathKeys() {
  const keys = new Set();
  const add = (value) => {
    const raw = String(value || "").trim();
    if (!raw || extensionOf(raw) !== "cardesign") return;
    keys.add(normalizeBrowserFavoritePath(raw));
    if (!/^[a-z]:[\\/]/i.test(raw) && state.root) {
      keys.add(normalizeBrowserFavoritePath(joinPath(state.root, raw)));
    }
  };
  (state.mySkins || []).forEach((skin) => {
    const files = skin?.files && typeof skin.files === "object" ? skin.files : {};
    add(files.cardesign || skin.cardesign || skin.cardesign_path || skin.cardesignPath);
    if (Array.isArray(skin?.files_list)) {
      skin.files_list.forEach((file) => {
        const kind = String(file?.kind || file?.type || "").toLowerCase();
        if (kind === "cardesign") add(file.path || file.rel || file.value);
      });
    }
  });
  return keys;
}

function isLiveryLabCardesignBrowserItem(item) {
  if (!isSkinTemplateBrowserItem(item)) return false;
  const keys = liveryLabCardesignPathKeys();
  return keys.has(normalizeBrowserFavoritePath(item.path)) || keys.has(normalizeBrowserFavoritePath(item.rel));
}

async function ensureMySkinsForBrowserMarkers() {
  if (normalizeWorkflowScope(state.workflowScope) !== "advanced") return false;
  const root = $("rootPath")?.value.trim() || state.root || "";
  if (state.mySkinsLoaded && state.mySkinsLoadedRoot === normalizeRootPath(root) && state.mySkinsLoadedScope === "advanced") return false;
  if (state.mySkinsMarkerLoading || !window.pywebview?.api?.get_my_skins) return false;
  if (!root) return false;
  state.mySkinsMarkerLoading = true;
  try {
    const result = await window.pywebview.api.get_my_skins({ root, includeHidden: false, scope: "advanced" });
    if (!result?.ok) return false;
    state.mySkins = result.data?.skins || [];
    state.mySkinsLoaded = true;
    state.mySkinsLoadedRoot = normalizeRootPath(root);
    state.mySkinsLoadedScope = "advanced";
    return true;
  } catch (_error) {
    // Browser markers are optional; the normal My Liveries view can still load them later.
    return false;
  } finally {
    state.mySkinsMarkerLoading = false;
  }
}

function shouldLoadLiveryLabBrowserMarkers(rel = state.currentRel) {
  return activeTab() === "skin" && isWithinCarSkinsRel(rel);
}

function queueLiveryLabBrowserMarkerRefresh(rel = state.currentRel) {
  if (!shouldLoadLiveryLabBrowserMarkers(rel)) return;
  ensureMySkinsForBrowserMarkers().then((loaded) => {
    if (!loaded) return;
    if (normalizeRelPath(state.currentRel) !== normalizeRelPath(rel)) return;
    refreshBrowser(rel);
  });
}

function isLiveryRelevantBrowserRel(rel = "") {
  const normalized = normalizeRelPath(rel);
  if (!normalized) return true;
  if (normalized === "content" || normalized === "content/cars" || normalized.startsWith("content/cars/")) return true;
  if (["liverylibrary", "liveryprojects", "liverypackageexport"].some((prefix) => normalized === prefix || normalized.startsWith(`${prefix}/`))) return true;
  return false;
}

async function saveDriverProfileDetails(profile = selectedDriverProfile()) {
  if (!profile?.id) return;
  if (!window.pywebview?.api?.update_driver_profile_details) {
    showStatusModal({
      type: "error",
      title: "Driver Profiles",
      subtitle: "Backend API missing",
      error: "This build cannot update driver profile details yet.",
    });
    return;
  }
  const name = $("driverProfileDetailName")?.value.trim() || profile.name || profile.id || "";
  const sourceSkinKey = $("driverProfileDetailSourceSkin")?.value || "";
  await withButtonWorking("saveDriverProfileDetails", "Saving...", async () => {
    const result = await window.pywebview.api.update_driver_profile_details({
      id: profile.id,
      carModel: profile.carModel || "",
      name,
      sourceSkinKey,
    });
    if (!result?.ok) {
      showResult(result, "Driver profile details failed", "update_driver_profile_details");
      return;
    }
    const item = result.data?.item || {};
    if (item?.id) state.selectedDriverProfileKey = driverProfileKey(item);
    await loadDriverProfiles();
    if (Number(result.data?.affectedCount || 0) > 0) await loadMySkins();
    showStatusModal({
      type: "success",
      title: "Driver profile saved",
      subtitle: item.name || name || profile.id || "Profile updated",
      message: result.data?.sourceChanged
        ? "The driver profile was rebuilt from the selected livery."
        : "The driver profile details were saved.",
      details: [
        result.message || "",
        `Profile: ${item.name || name || profile.id || "-"}`,
        `Source livery: ${item.sourceDisplayName || item.sourceSkinId || item.sourceSkinKey || "-"}`,
        Number(result.data?.affectedCount || 0) ? `${result.data.affectedCount} linked My Skin(s) need Update in Game.` : "",
      ].filter(Boolean).join("\n"),
    });
  });
}

async function startGameFromButton(buttonId = "startGame") {
  const button = $(buttonId);
  if (button?.disabled) return;
  setButtonWorking(button, true, "Starting...");
  try {
    const result = await window.pywebview.api.start_game($("rootPath").value.trim());
    showResult(result, result?.message || "Game started", "start_game");
    if (result?.ok && result.data?.started) {
      await delay(4500);
    }
  } finally {
    setButtonWorking(button, false);
  }
}

function browserContentCarsOnlyMode(tab = activeTab()) {
  return !devModeEnabled() && ["convert", "inspect", "skin"].includes(tab);
}

function isEnduserCarBrowserRel(rel = "") {
  const normalized = normalizeRelPath(rel);
  if (normalized === "content/cars") return true;
  if (!normalized.startsWith("content/cars/")) return false;
  if (normalized.endsWith(".material")) return false;

  const parts = normalized.split("/");
  const carId = parts[2] || "";
  if (!carId.startsWith("ks_")) return false;
  if (parts.length === 3) return true;

  const allowedFolders = new Set(["skins", "textures", "presets"]);
  if (allowedFolders.has(parts[3])) return true;
  return false;
}

function enduserToolBrowserRelAllowed(rel = "", tab = activeTab()) {
  const normalized = normalizeRelPath(rel);
  if (!normalized) return false;
  if (normalized === "content/cars") return true;
  if (!normalized.startsWith("content/cars/")) return false;

  const parts = normalized.split("/");
  const carId = parts[2] || "";
  if (!carId.startsWith("ks_")) return false;
  if (parts.length === 3) return true;

  const topFolder = parts[3] || "";
  const ext = extensionOf(normalized);
  const hasFileExtension = Boolean(ext && ext !== normalized && normalized.split("/").pop()?.includes("."));
  if (tab === "skin") {
    if (topFolder !== "skins") return false;
    return !hasFileExtension || ext === "cardesign";
  }
  if (tab === "convert") {
    if (topFolder !== "textures" && topFolder !== "skins") return false;
    return !hasFileExtension || ext === "texture" || ext === "texturemips";
  }
  return isEnduserCarBrowserRel(normalized);
}

function browserRelAllowedForCurrentMode(rel = "", tab = activeTab()) {
  if (!browserContentCarsOnlyMode(tab)) return true;
  if (tab === "skin" || tab === "convert") return enduserToolBrowserRelAllowed(rel, tab);
  return isEnduserCarBrowserRel(rel);
}

function browserItemBlockedForCurrentMode(item, tab = activeTab()) {
  if (!item || !browserContentCarsOnlyMode(tab)) return false;
  const rel = item.rel || relativePathFromRoot($("rootPath")?.value.trim() || state.root || "", item.path || "") || "";
  return !browserRelAllowedForCurrentMode(rel, tab);
}

function browserPathBlockedForCurrentMode(path = "", tab = activeTab()) {
  if (!browserContentCarsOnlyMode(tab)) return false;
  const root = $("rootPath")?.value.trim() || state.root || "";
  const rel = relativePathFromRoot(root, path);
  return !rel || !browserRelAllowedForCurrentMode(rel, tab);
}

function showBrowserLockedNotice() {
  const message = "This area is locked in the enduser browser. Use a car livery folder for LiveryLab workflows.";
  log(message, true);
  setInspectOutputText(message);
}

function enduserBrowserFileAllowedForTool(path = "", tab = activeTab()) {
  if (devModeEnabled()) return true;
  const ext = extensionOf(path);
  if (tab === "skin") return ext === "cardesign";
  if (tab === "convert") return ["texture", "texturemips"].includes(ext);
  return true;
}

function showBrowserToolMismatchNotice(tab = activeTab()) {
  const message = tab === "skin"
    ? "Livery Creator uses .cardesign files. Open Texture Converter to use texture files."
    : "Texture Converter uses .texture or .texturemips files from car skins or textures folders.";
  log(message, true);
  setInspectOutputText(message);
}

function gameRootRelForPath(path = "") {
  const root = $("rootPath")?.value.trim() || state.root || "";
  if (!root || !String(path || "").trim()) return null;
  return relativePathFromRoot(root, path);
}

function pathInsideAppStorage(path = "") {
  const value = String(path || "").trim();
  if (!value) return false;
  const normalize = (text) => String(text || "").replace(/\\/g, "/").replace(/\/+/g, "/").replace(/\/$/, "").toLowerCase();
  const pathKey = normalize(value);
  return (state.appStorageRoots || [])
    .map((item) => normalize(item))
    .filter(Boolean)
    .some((storageKey) => pathKey === storageKey || pathKey.startsWith(`${storageKey}/`));
}

function textureConverterGameRelAllowed(rel = "") {
  const parts = relPathParts(rel);
  if (parts.length < 4) return false;
  if (parts[0] !== "content" || parts[1] !== "cars") return false;
  if (!String(parts[2] || "").startsWith("ks_")) return false;
  return parts[3] === "skins" || parts[3] === "textures";
}

function textureConverterPathAllowed(path = "") {
  if (devModeEnabled()) return true;
  if (pathInsideAppStorage(path)) return true;
  const rel = gameRootRelForPath(path);
  if (rel === null) return true;
  return textureConverterGameRelAllowed(rel);
}

function ensureTextureConverterPathsAllowed(entries = []) {
  for (const entry of entries) {
    const path = String(entry?.path || "").trim();
    if (!path || textureConverterPathAllowed(path)) continue;
    const label = entry?.label || "Selected path";
    const rel = gameRootRelForPath(path) || path;
    const message = `${label} is locked in the enduser Texture Converter: ${rel}`;
    log(message, true);
    setInspectOutputText(message);
    return false;
  }
  return true;
}

function carCompatibilityById() {
  const map = new Map();
  for (const car of state.carCompatibility?.cars || []) {
    if (car?.carId) map.set(String(car.carId).toLowerCase(), car);
  }
  return map;
}

function carIdFromPath(path) {
  const parts = String(path || "").replace(/\\/g, "/").split("/").filter(Boolean);
  for (let index = 0; index < parts.length - 2; index += 1) {
    if (parts[index].toLowerCase() === "content" && parts[index + 1].toLowerCase() === "cars") {
      return parts[index + 2] || "";
    }
  }
  return "";
}

function carCompatibilityForId(carId) {
  const key = String(carId || "").trim().toLowerCase();
  if (!key) return null;
  return carCompatibilityById().get(key) || null;
}

function carCompatibilityForPath(path) {
  const carId = carIdFromPath(path);
  return {
    carId,
    car: carCompatibilityForId(carId),
  };
}

function skinTemplateSupportKey(value) {
  return String(value || "").replace(/\//g, "\\").replace(/^[\\]+/, "").toLowerCase();
}

function skinTemplateSupportByPath() {
  const map = new Map();
  const add = (template) => {
    if (!template || typeof template !== "object") return;
    [template.cardesign, template.cardesignPath].forEach((value) => {
      const key = skinTemplateSupportKey(value);
      if (key) map.set(key, template);
    });
  };
  (state.carCompatibility?.skinTemplates || []).forEach(add);
  (state.carCompatibility?.cars || []).forEach((car) => (car?.skinTemplates || []).forEach(add));
  return map;
}

function skinTemplateSupportForPath(path) {
  const map = skinTemplateSupportByPath();
  const direct = skinTemplateSupportKey(path);
  if (map.has(direct)) return map.get(direct);
  const root = $("rootPath")?.value.trim() || state.root || "";
  const rel = relativePathFromRoot(root, path);
  const relKey = skinTemplateSupportKey(rel || "");
  return relKey ? (map.get(relKey) || null) : null;
}

function skinTemplateSupportReason(support) {
  const reasons = Array.isArray(support?.reasons) ? support.reasons.filter(Boolean) : [];
  if (reasons.length) return reasons.join(" ");
  const missing = Array.isArray(support?.missing) ? support.missing.filter(Boolean) : [];
  if (missing.length) return `Missing: ${missing.join(", ")}`;
  return support?.message || SKIN_TEMPLATE_UNSUPPORTED_MESSAGE;
}

function skinTemplateUnsupportedText(support) {
  return `${support?.message || SKIN_TEMPLATE_UNSUPPORTED_MESSAGE}${skinTemplateSupportReason(support) ? ` ${skinTemplateSupportReason(support)}` : ""}`;
}

function isUnsupportedSkinTemplatePath(path) {
  const support = skinTemplateSupportForPath(path);
  return support && support.status === "unsupported";
}

async function inspectSkinTemplateSupportForPath(cardesign, design = "") {
  const cached = skinTemplateSupportForPath(cardesign);
  if (cached && cached.status !== "unknown") return cached;
  if (extensionOf(cardesign || "") !== "cardesign") return cached || null;
  if (typeof window.pywebview?.api?.inspect_skin_template_support !== "function") return cached || null;
  const result = await window.pywebview.api.inspect_skin_template_support({
    root: $("rootPath")?.value.trim() || state.root || "",
    cardesign,
    design,
  });
  if (!result?.ok) return cached || null;
  return result.data || cached || null;
}

function compatibilitySymbolHtml(status) {
  status = carCompatibilityDisplayStatus(status);
  return {
    ready: "&check;",
    liverylab_livery: "L",
    unsupported: "&times;",
    unknown: "?",
  }[status || "unknown"] || "?";
}

function carCompatibilityDisplayStatus(status) {
  return status === "paint_only" ? "ready" : (status || "unknown");
}

function carCompatibilityReasonText(car, carId = "") {
  if (!carId) return "";
  if (state.carCompatibilityLoading) {
    const current = state.carCompatibilityProgress?.current || "";
    return current ? `${t("about_car_compatibility_checking")}: ${current}` : t("about_car_compatibility_loading");
  }
  if (!state.carCompatibility) return "Run Analyze Cars to fill this status.";
  if (!car) return "No compatibility result found for this car.";
  if (car.missing?.length) return car.missing.join(" | ");
  const status = carCompatibilityDisplayStatus(car.status);
  if (status === "ready") return "Ready for the standard LiveryLab livery workflow.";
  if (status === "unsupported") return "Missing the required skin or exterior UV signals.";
  return "Compatibility status is unknown.";
}

function carCompatibilityBadgeHtml(car, carId = "", { compact = false } = {}) {
  const status = carCompatibilityDisplayStatus(car?.status);
  const label = car ? carCompatibilityStatusText(status) : "Not analyzed";
  const reason = carCompatibilityReasonText(car, carId);
  return `<span class="compatibility-inline-badge status-${escapeHtml(status)}${compact ? " compact" : ""}" title="${escapeHtml(reason || label)}">
    <span class="compatibility-inline-symbol" aria-hidden="true">${compatibilitySymbolHtml(status)}</span>
    <span>${escapeHtml(label)}</span>
  </span>`;
}

function workflowCompatibilityHtml(path, title = "Livery compatibility") {
  const { carId, car } = carCompatibilityForPath(path);
  if (!carId) return "";
  const reason = carCompatibilityReasonText(car, carId);
  return `
    <div class="workflow-compatibility-main">
      <span class="workflow-compatibility-label">${escapeHtml(title)}</span>
      ${carCompatibilityBadgeHtml(car, carId)}
      <strong>${escapeHtml(carId)}</strong>
    </div>
    <div class="workflow-compatibility-note">${escapeHtml(reason)}</div>
  `;
}

function renderWorkflowCompatibilityTarget(targetId, path, title) {
  const target = $(targetId);
  if (!target) return;
  const html = workflowCompatibilityHtml(path, title);
  target.hidden = !html;
  target.innerHTML = html;
}

function renderSelectedBrowserCompatibility() {
  renderWorkflowCompatibilityTarget("browserCompatibilityStatus", state.selected?.path || "", "Selected car");
}

function renderWorkflowCompatibility() {
  const skinPath = $("skinCardesign")?.value.trim() || $("skinDesign")?.value.trim() || "";
  renderWorkflowCompatibilityTarget("skinCompatibilityStatus", skinPath, "Template car");
  const previewProject = payloadPreview3dProject();
  renderWorkflowCompatibilityTarget(
    "preview3dCompatibilityStatus",
    previewProject.carContentPath || previewProject.exteriorMeshPath || previewProject.modelPath || previewProject.texturePath || "",
    "Preview car"
  );
  renderWorkflowCompatibilityTarget("uvCompatibilityStatus", $("uvMeshPath")?.value.trim() || "", "UV mesh car");
  renderSelectedBrowserCompatibility();
}

function mySkinCarId(skin) {
  const direct = carIdFromPath(skin?.car_content_path || "");
  if (direct) return direct;
  for (const file of skin?.files_list || []) {
    const found = carIdFromPath(file?.path || "");
    if (found) return found;
  }
  return String(skin?.car_model || "").trim();
}

function mySkinCompatibilityWarningBadgeHtml(skin) {
  const carId = mySkinCarId(skin);
  if (!carId) return "";
  const compatibility = carCompatibilityForId(carId);
  if (!compatibility || compatibility.status === "ready" || compatibility.status === "paint_only") return "";
  return carCompatibilityBadgeHtml(compatibility, carId, { compact: true });
}

function browserInfoCell(item, compatibilityMap, isAdvanced = false, lockedForMode = false) {
  if (lockedForMode) {
    return `<td class="compat-cell"><span class="browser-scope-status is-locked" title="Locked in the enduser browser." aria-label="Locked in the enduser browser.">Locked</span></td>`;
  }
  if (isAdvanced) {
    return `<td class="compat-cell"><span class="browser-scope-status is-locked" title="Advanced area outside the standard livery workflow." aria-label="Advanced area outside the standard livery workflow.">Locked</span></td>`;
  }
  if (isSkinTemplateBrowserItem(item)) {
    if (isLiveryLabCardesignBrowserItem(item)) {
      const label = "LiveryLab Livery";
      return `<td class="compat-cell"><span class="browser-compat-status status-liverylab_livery" title="${escapeHtml(label)}" aria-label="${escapeHtml(label)}">L</span></td>`;
    }
    const support = skinTemplateSupportForPath(item.path);
    const status = carCompatibilityDisplayStatus(support?.status);
    const display = { ready: "OK", unsupported: "X", unknown: "?" }[status] || "?";
    const label = status === "ready"
      ? "Skin template ready"
      : (status === "unsupported" ? skinTemplateSupportReason(support) : "Skin template not analyzed. Run Analyze Cars.");
    return `<td class="compat-cell"><span class="browser-compat-status status-${escapeHtml(status)}" title="${escapeHtml(label)}" aria-label="${escapeHtml(label)}">${escapeHtml(display)}</span></td>`;
  }
  if (!isCarsRootRel() || !item.isDir) {
    return `<td class="compat-cell"><span class="browser-scope-status is-unlocked" title="Livery workflow area." aria-label="Livery workflow area.">Unlocked</span></td>`;
  }
  const car = compatibilityMap.get(String(item.name || "").toLowerCase());
  const status = carCompatibilityDisplayStatus(car?.status);
  const display = {
    ready: "OK",
    unsupported: "X",
    unknown: "?",
  }[status] || "?";
  const label = car ? carCompatibilityStatusText(status) : "Not analyzed";
  return `<td class="compat-cell"><span class="browser-compat-status status-${escapeHtml(status)}" title="${escapeHtml(label)}" aria-label="${escapeHtml(label)}">${escapeHtml(display)}</span></td>`;
}

function renderBrowserFavorites() {
  const select = $("browserFavoriteSelect");
  if (!select) return;
  const currentValue = select.value;
  select.innerHTML = '<option value="">-</option>';
  for (const favorite of state.browserFavorites) {
    const option = document.createElement("option");
    option.value = favorite.name;
    option.textContent = `${favorite.name} - ${favorite.path}`;
    select.append(option);
  }
  if (state.browserFavorites.some((item) => item.name === currentValue)) {
    select.value = currentValue;
  }
}

async function openSelectedBrowserFavorite() {
  const name = $("browserFavoriteSelect")?.value.trim() || "";
  if (!name) {
    log("Please select a favorite first", true);
    return;
  }
  const favorite = state.browserFavorites.find((item) => item.name === name);
  if (!favorite) {
    log("Favorite not found", true);
    return;
  }
  const activeRoot = normalizeRootPath($("rootPath").value || state.root || "");
  const favoritePath = String(favorite.path || "").trim();
  const rel = relativePathFromRoot(activeRoot, favoritePath);
  if (activeRoot && rel !== null) {
    $("rootPath").value = activeRoot;
    state.root = activeRoot;
    if (!browserRelAllowedForCurrentMode(rel)) {
      showBrowserLockedNotice();
      await refreshBrowser("content/cars");
      return;
    }
    await refreshBrowser(rel);
    return;
  }
  if (browserContentCarsOnlyMode()) {
    showBrowserLockedNotice();
    await refreshBrowser("content/cars");
    return;
  }
  const fallbackRoot = normalizeRootPath(favoritePath);
  const fallbackRel = relativePathFromRoot(fallbackRoot, favoritePath);
  $("rootPath").value = fallbackRoot;
  state.root = fallbackRoot;
  await refreshBrowser(fallbackRel || "");
}

async function focusSkinCreatorBrowserStart() {
  const root = $("rootPath")?.value.trim();
  if (!root) return;
  const current = normalizeRelPath(state.currentRel);
  await refreshBrowser(browserRelAllowedForCurrentMode(current, "skin") ? current : "content/cars");
}

async function focusContentCarsBrowserStartForEnduser(tab = activeTab()) {
  if (!browserContentCarsOnlyMode(tab)) return;
  const root = $("rootPath")?.value.trim();
  if (!root) return;
  const current = normalizeRelPath(state.currentRel);
  await refreshBrowser(browserRelAllowedForCurrentMode(current, tab) ? current : "content/cars");
}

async function toggleBrowserFavoriteForPath(path) {
  const favoritePath = String(path || "").trim();
  if (!favoritePath) {
    log("Please select a valid folder path first", true);
    return;
  }
  const current = browserFavoriteForPath(favoritePath);
  if (current) {
    const result = await call("delete_browser_favorite", current.path);
    if (!result.ok) return;
    state.browserFavorites = result.data.favorites || [];
    renderBrowserFavorites();
    await refreshBrowser(state.currentRel);
    return;
  }
  const result = await call("add_browser_favorite", defaultFavoriteNameFromPath(favoritePath), favoritePath);
  if (!result.ok) return;
  state.browserFavorites = result.data.favorites || [];
  renderBrowserFavorites();
  const updated = browserFavoriteForPath(favoritePath);
  $("browserFavoriteSelect").value = updated?.name || "";
  await refreshBrowser(state.currentRel);
}

async function refreshBrowser(rel = state.currentRel) {
  state.root = normalizeRootPath($("rootPath").value);
  $("rootPath").value = state.root;
  if (!state.root) {
    log(t("log_root_empty"), true);
    return;
  }
  if (!browserRelAllowedForCurrentMode(rel || "")) {
    rel = "content/cars";
  }
  const result = await window.pywebview.api.list_dir(state.root, rel || "");
  if (!showResult(result, t("log_folder_loaded"))) return;
  state.currentRel = result.data.rel || "";
  state.selected = null;
  renderSelectedBrowserCompatibility();
  renderCrumbs();

  const tbody = $("fileRows");
  tbody.innerHTML = "";
  const compatibilityMap = carCompatibilityById();
  for (const item of result.data.items) {
    const row = document.createElement("tr");
    const isAdvanced = !isLiveryRelevantBrowserRel(item.rel);
    const lockedForMode = browserItemBlockedForCurrentMode(item);
    if (isAdvanced || lockedForMode) {
      row.classList.add("browser-row-muted");
      row.title = lockedForMode
        ? "Locked in the enduser browser. Use content/cars for LiveryLab workflows."
        : "Advanced area outside the standard livery workflow.";
    }
    if (lockedForMode) row.classList.add("browser-row-hard-locked");
    const liveryLabCardesign = isLiveryLabCardesignBrowserItem(item);
    const templateSupport = isSkinTemplateBrowserItem(item) ? skinTemplateSupportForPath(item.path) : null;
    if (!liveryLabCardesign && templateSupport?.status === "unsupported") {
      row.classList.add("browser-row-template-unsupported");
      row.title = skinTemplateUnsupportedText(templateSupport);
    }
    const favoriteActive = item.isDir && isBrowserFavoritePath(item.path);
    const tab = activeTab();
    const skinCreatorTarget = tab === "skin" && isSkinCreatorBrowserTarget(item);
    const textureConverterTarget = tab === "convert" && isTextureConverterBrowserTarget(item);
    const nameClass = !lockedForMode && textureConverterTarget
      ? "browser-workflow-target"
      : (!lockedForMode && skinCreatorTarget
      ? (liveryLabCardesign ? "browser-workflow-liverylab" : (templateSupport?.status === "unsupported" ? "browser-workflow-blocked" : "browser-workflow-target"))
      : "");
    row.innerHTML = [
      `<td class="favorite-cell">${item.isDir ? `<button type="button" class="browser-favorite-toggle ${favoriteActive ? "is-active" : ""}" aria-label="${favoriteActive ? "Remove favorite" : "Add favorite"}">${favoriteActive ? "★" : "☆"}</button>` : ""}</td>`,
      browserInfoCell(item, compatibilityMap, isAdvanced, lockedForMode),
      `<td><span class="${nameClass}">${item.isDir ? "[ ] " : ""}${escapeHtml(item.name)}</span></td>`,
      `<td>${escapeHtml(item.kind)}</td>`,
      `<td>${escapeHtml(item.size)}</td>`,
    ].join("");
    row.draggable = !lockedForMode;
    row.addEventListener("dragstart", (event) => {
      if (lockedForMode) {
        event.preventDefault();
        showBrowserLockedNotice();
        return;
      }
      row.classList.add("is-dragging");
      event.dataTransfer.effectAllowed = "copy";
      event.dataTransfer.setData("text/plain", item.path);
      event.dataTransfer.setData("application/x-liverylab-browser-path", item.path);
    });
    row.addEventListener("dragend", () => row.classList.remove("is-dragging"));
    const star = row.querySelector(".browser-favorite-toggle");
    if (star) {
      star.addEventListener("click", (event) => {
        event.preventDefault();
        event.stopPropagation();
        if (lockedForMode) {
          showBrowserLockedNotice();
          return;
        }
        toggleBrowserFavoriteForPath(item.path);
      });
    }
    row.addEventListener("click", () => {
      if (lockedForMode) {
        showBrowserLockedNotice();
        return;
      }
      selectRow(row, item);
    });
    row.addEventListener("dblclick", () => {
      if (lockedForMode) {
        showBrowserLockedNotice();
        return;
      }
      if (item.isDir) {
        refreshBrowser(item.rel);
      } else if (!devModeEnabled()) {
        selectRow(row, item);
      } else if (activeTab() === "skin" && isSkinCreatorBrowserTarget(item)) {
        selectRow(row, item);
      } else {
        inspectPath(item.path);
      }
    });
    tbody.append(row);
  }
  queueLiveryLabBrowserMarkerRefresh(state.currentRel);
}

async function ensureInitialBrowserLoaded(maxAttempts = 6, delayMs = 300) {
  const root = $("rootPath").value.trim();
  if (!root) return;
  state.currentRel = "";
  for (let attempt = 1; attempt <= maxAttempts; attempt += 1) {
    await refreshBrowser("");
    if ($("fileRows").children.length) return;
    await new Promise((resolve) => window.setTimeout(resolve, delayMs));
  }
  log("Initial browser load stayed empty; manual refresh should still work", true);
}

async function ensureBrowserLoadedIfEmpty() {
  if (!$("rootPath").value.trim() || $("fileRows").children.length) return;
  await refreshBrowser("");
}

function scheduleInitialBrowserReloads() {
  const delays = [100, 650, 1600, 3200, 5200];
  delays.forEach((delay) => {
    window.setTimeout(async () => {
      await ensureBrowserLoadedIfEmpty();
    }, delay);
  });
}

function selectRow(row, item) {
  if (browserItemBlockedForCurrentMode(item)) {
    showBrowserLockedNotice();
    return;
  }
  document.querySelectorAll("#fileRows tr").forEach((tr) => tr.classList.remove("selected"));
  row.classList.add("selected");
  state.selected = item;
  renderSelectedBrowserCompatibility();
  inspectPath(item.path, true);
  if (!item.isDir) applySelectionForCurrentTool(false);
}

async function inspectPath(path, previewOnly = false) {
  if (browserPathBlockedForCurrentMode(path)) {
    showBrowserLockedNotice();
    return;
  }
  const result = await window.pywebview.api.inspect_file(path, $("rootPath").value.trim());
  if (!result.ok) {
    writeFileInfoToLog(result.message || "");
    if (!previewOnly) setInspectOutputText(displayDetails(firstMessageLine(result.message, "File inspection failed"), technicalDetailsFromResult(result)));
    renderMeshSummary(null);
    renderActorEditor(null, "");
    renderMaterialEditor(null, "");
    renderTextureEditor(null, "");
    renderVisualPresetEditor(null, "");
    if (!previewOnly) renderMechanicalEditor(null, "");
    if (!previewOnly) renderCarFinalStateEditor(null, "");
    return;
  }
  const text = result.data.summary || JSON.stringify(result.data, null, 2);
  writeFileInfoToLog(text);
  setInspectOutputText(text);
  renderMeshSummary(result.data);
  renderActorEditor(result.data, path);
  renderMaterialEditor(result.data, path);
  renderTextureEditor(result.data, path);
  renderVisualPresetEditor(result.data, path);
  renderMechanicalEditor(result.data, path);
  renderCarFinalStateEditor(result.data, path);
  if (!previewOnly) setTab("inspect");
}

function setInspectOutputText(text) {
  state.inspectText = String(text ?? "");
  state.inspectMatchIndex = 0;
  renderInspectOutput();
}

function inspectMatches() {
  const query = state.inspectSearch;
  if (!query) return [];
  const text = state.inspectText.toLowerCase();
  const needle = query.toLowerCase();
  const matches = [];
  let pos = 0;
  while (needle && pos <= text.length) {
    const found = text.indexOf(needle, pos);
    if (found === -1) break;
    matches.push({ start: found, end: found + needle.length });
    pos = found + Math.max(needle.length, 1);
  }
  return matches;
}

function renderInspectOutput() {
  const output = $("inspectOutput");
  const count = $("inspectSearchCount");
  if (!output) return;
  const matches = inspectMatches();
  if (!matches.length) {
    output.textContent = state.inspectText;
    if (count) count.textContent = state.inspectSearch ? "0/0" : "";
    return;
  }
  state.inspectMatchIndex = Math.min(Math.max(state.inspectMatchIndex, 0), matches.length - 1);
  const parts = [];
  let cursor = 0;
  matches.forEach((match, index) => {
    parts.push(escapeHtml(state.inspectText.slice(cursor, match.start)));
    const cls = index === state.inspectMatchIndex ? "preview-hit active" : "preview-hit";
    parts.push(`<mark class="${cls}">${escapeHtml(state.inspectText.slice(match.start, match.end))}</mark>`);
    cursor = match.end;
  });
  parts.push(escapeHtml(state.inspectText.slice(cursor)));
  output.innerHTML = parts.join("");
  if (count) count.textContent = `${state.inspectMatchIndex + 1}/${matches.length}`;
  output.querySelector(".preview-hit.active")?.scrollIntoView({ block: "center", inline: "nearest" });
}

function stepInspectSearch(direction) {
  const matches = inspectMatches();
  if (!matches.length) return;
  state.inspectMatchIndex = (state.inspectMatchIndex + direction + matches.length) % matches.length;
  renderInspectOutput();
}

function renderActorEditor(data, fallbackPath = "") {
  const editor = $("actorEditor");
  if (!editor) return;
  const path = data?.path || fallbackPath;
  if (extensionOf(path || "") !== "actor") {
    editor.hidden = true;
    editor.innerHTML = "";
    return;
  }
  editor.hidden = false;
  editor.innerHTML = "";

  const title = document.createElement("div");
  title.className = "inspect-editor-title";
  const total = (data?.cardesigns?.length || 0) + (data?.visualPresets?.length || 0) + (data?.mechanicalPresets?.length || 0);
  const numberplateCount = data?.numberplates?.length || 0;
  const placementCount = data?.numberplatePlacements?.length || 0;
  title.innerHTML = `<strong>Actor Editor</strong><span>${total} Listen, ${numberplateCount} Numberplates, ${placementCount} Placements</span>`;
  editor.append(title);

  const sections = [
    { kind: "cardesign", label: "Cardesigns", values: data?.cardesigns || [], placeholder: "content\\...\\skin_xxx.cardesign" },
    { kind: "visual", label: "Visual Presets", values: data?.visualPresets || [], placeholder: "content\\...\\preset_xxx.visualcarpreset" },
    { kind: "mechanical", label: "Mechanical Presets", values: data?.mechanicalPresets || [], placeholder: "content\\...\\preset_xxx.mechanicalcarpreset" },
  ];

  sections.forEach((section) => {
    const block = document.createElement("div");
    block.className = "inspect-editor-block";

    const heading = document.createElement("div");
    heading.className = "inspect-editor-subtitle";
    heading.textContent = `${section.label} (${section.values.length})`;
    block.append(heading);

    const addRow = document.createElement("div");
    addRow.className = "mechanical-add-row";
    const input = document.createElement("input");
    input.type = "text";
    input.placeholder = section.placeholder;
    input.spellcheck = false;
    input.dataset.actorKind = section.kind;
    input.addEventListener("keydown", (event) => {
      if (event.key === "Enter") {
        event.preventDefault();
        addActorResource(path, section.kind, input.value.trim());
      }
    });
    const addButton = document.createElement("button");
    addButton.type = "button";
    addButton.textContent = "Hinzufuegen";
    addButton.addEventListener("click", () => addActorResource(path, section.kind, input.value.trim()));
    addRow.append(input, addButton);
    block.append(addRow);

    const list = document.createElement("div");
    list.className = "inspect-reference-list";
    section.values.forEach((value) => {
      const row = document.createElement("div");
      row.className = "mechanical-visual-row";
      const label = document.createElement("span");
      label.textContent = value;
      const button = document.createElement("button");
      button.type = "button";
      button.textContent = "Remove";
      button.addEventListener("click", () => removeActorResource(path, section.kind, value));
      row.append(label, button);
      list.append(row);
    });
    block.append(list);
    editor.append(block);
  });

  const numberplates = data?.numberplates || [];
  if (numberplates.length) {
    const block = document.createElement("div");
    block.className = "inspect-editor-block";
    const heading = document.createElement("div");
    heading.className = "inspect-editor-subtitle";
    heading.textContent = `Numberplates (${numberplates.length})`;
    block.append(heading);

    numberplates.forEach((entry) => {
      const item = document.createElement("div");
      item.className = "inspect-numberplate-card";
      item.dataset.numberplateIndex = String(entry.index);
      const itemTitle = document.createElement("div");
      itemTitle.className = "inspect-editor-title";
      itemTitle.innerHTML = `<strong>Numberplate ${entry.index}</strong><span>${escapeHtml(entry.value_offset_hex || "")}</span>`;
      item.append(itemTitle);

      const grid = document.createElement("div");
      grid.className = "inspect-editor-grid";
      [
        { label: "Font", key: "font_path", value: entry.font_path || "" },
        { label: "Background", key: "background_path", value: entry.background_path || "" },
        { label: "Opacity", key: "opacity", value: entry.opacity ?? "" },
        { label: "Number Scale 1 (field 8)", key: "value_8", value: entry.value_8 ?? "" },
        { label: "Number Scale 2 (field 9)", key: "value_9", value: entry.value_9 ?? "" },
        { label: "Number Scale 3 (field 10)", key: "value_10", value: entry.value_10 ?? "" },
        { label: "Number Coord 1 (field 11)", key: "value_11", value: entry.value_11 ?? "" },
        { label: "Number Coord 2 (field 15)", key: "value_15", value: entry.value_15 ?? "" },
        { label: "Number Coord 3 (field 16)", key: "value_16", value: entry.value_16 ?? "" },
      ].forEach((field) => {
        const label = document.createElement("label");
        label.className = "inspect-editor-field";
        label.innerHTML = `<span>${escapeHtml(field.label)}</span><input type="text" data-numberplate-field="${escapeHtml(field.key)}" value="${escapeHtml(field.value)}" spellcheck="false" />`;
        grid.append(label);
      });
      item.append(grid);

      const overlay = document.createElement("div");
      overlay.className = "inspect-numberplate-subgrid";
      ["Overlay 1", "Overlay 2", "Overlay 3", "Overlay 4"].forEach((labelText, idx) => {
        const label = document.createElement("label");
        label.className = "inspect-editor-field";
        label.innerHTML = `<span>${escapeHtml(labelText)}</span><input type="text" data-numberplate-overlay="${idx}" value="${escapeHtml((entry.overlay || [])[idx] ?? "")}" spellcheck="false" />`;
        overlay.append(label);
      });
      item.append(overlay);
      block.append(item);
    });
    editor.append(block);
  }

  const placements = data?.numberplatePlacements || [];
  if (placements.length) {
    const block = document.createElement("div");
    block.className = "inspect-editor-block";
    const heading = document.createElement("div");
    heading.className = "inspect-editor-subtitle";
    heading.textContent = `Numberplate Placements (${placements.length})`;
    block.append(heading);

    placements.forEach((entry) => {
      const item = document.createElement("div");
      item.className = "inspect-numberplate-card";
      item.dataset.placementIndex = String(entry.index);
      const itemTitle = document.createElement("div");
      itemTitle.className = "inspect-editor-title";
      itemTitle.innerHTML = `<strong>${escapeHtml(entry.label || `Placement ${entry.index}`)}</strong><span>${escapeHtml(entry.value_offset_hex || "")}</span>`;
      item.append(itemTitle);

      const grid = document.createElement("div");
      grid.className = "inspect-editor-grid";
      const fieldOneLabel = ["HOOD", "DOOR_L", "DOOR_R"].includes(String(entry.label || "").toUpperCase()) ? "Opacity" : "Field 1";
      [
        { label: "Label", key: "label", value: entry.label || "" },
        { label: "Outer Field", key: "outer_field_number", value: entry.outer_field_number ?? "", readonly: true },
        { label: fieldOneLabel, key: "field_1", value: entry.field_1 ?? "" },
        { label: "Field 2", key: "field_2", value: entry.field_2 ?? "" },
      ].forEach((field) => {
        const label = document.createElement("label");
        label.className = "inspect-editor-field";
        label.innerHTML = `<span>${escapeHtml(field.label)}</span><input type="text" data-placement-field="${escapeHtml(field.key)}" value="${escapeHtml(field.value)}" ${field.readonly ? "readonly" : ""} spellcheck="false" />`;
        grid.append(label);
      });
      item.append(grid);

      const transforms = document.createElement("div");
      transforms.className = "inspect-numberplate-subgrid";
      [
        ["Pos X", "position", 0],
        ["Pos Y", "position", 1],
        ["Pos Z", "position", 2],
        ["Rot X", "rotation", 0],
        ["Rot Y", "rotation", 1],
        ["Rot Z", "rotation", 2],
        ["Scale X", "scale", 0],
        ["Scale Y", "scale", 1],
        ["Scale Z", "scale", 2],
      ].forEach(([labelText, key, idx]) => {
        const label = document.createElement("label");
        label.className = "inspect-editor-field";
        label.innerHTML = `<span>${escapeHtml(labelText)}</span><input type="text" data-placement-vector="${escapeHtml(key)}" data-placement-vector-index="${idx}" value="${escapeHtml((entry[key] || [])[idx] ?? "")}" spellcheck="false" />`;
        transforms.append(label);
      });
      item.append(transforms);
      block.append(item);
    });
    editor.append(block);
  }

  if (numberplates.length || placements.length) {
    const actions = document.createElement("div");
    actions.className = "inspect-editor-actions";
    const save = document.createElement("button");
    save.type = "button";
    save.className = "primary";
    save.textContent = "Save actor numberplates";
    save.addEventListener("click", () => saveActorNumberplates(path));
    actions.append(save);
    editor.append(actions);
  }
}

async function saveActorNumberplates(path) {
  const editor = $("actorEditor");
  if (!editor) return;
  const numberplates = Array.from(editor.querySelectorAll("[data-numberplate-index]")).map((card) => ({
    index: Number(card.dataset.numberplateIndex || 0),
    font_path: card.querySelector('[data-numberplate-field="font_path"]')?.value.trim() || "",
    background_path: card.querySelector('[data-numberplate-field="background_path"]')?.value.trim() || "",
    opacity: card.querySelector('[data-numberplate-field="opacity"]')?.value.trim() || "",
    value_8: card.querySelector('[data-numberplate-field="value_8"]')?.value.trim() || "",
    value_9: card.querySelector('[data-numberplate-field="value_9"]')?.value.trim() || "",
    value_10: card.querySelector('[data-numberplate-field="value_10"]')?.value.trim() || "",
    value_11: card.querySelector('[data-numberplate-field="value_11"]')?.value.trim() || "",
    value_15: card.querySelector('[data-numberplate-field="value_15"]')?.value.trim() || "",
    value_16: card.querySelector('[data-numberplate-field="value_16"]')?.value.trim() || "",
    overlay: Array.from(card.querySelectorAll("[data-numberplate-overlay]")).map((input) => input.value.trim()),
  }));
  const placements = Array.from(editor.querySelectorAll("[data-placement-index]")).map((card) => {
    const vector = (name) =>
      Array.from(card.querySelectorAll(`[data-placement-vector="${name}"]`))
        .sort((a, b) => Number(a.dataset.placementVectorIndex || 0) - Number(b.dataset.placementVectorIndex || 0))
        .map((input) => input.value.trim());
    return {
      index: Number(card.dataset.placementIndex || 0),
      label: card.querySelector('[data-placement-field="label"]')?.value.trim() || "",
      field_1: card.querySelector('[data-placement-field="field_1"]')?.value.trim() || "",
      field_2: card.querySelector('[data-placement-field="field_2"]')?.value.trim() || "",
      position: vector("position"),
      rotation: vector("rotation"),
      scale: vector("scale"),
    };
  });
  const result = await call("update_actor_numberplates", { path, numberplates, placements });
  if (!result.ok) return;
  await inspectPath(path, false);
}

function renderMaterialEditor(data, fallbackPath = "") {
  const editor = $("materialEditor");
  if (!editor) return;
  const path = data?.path || fallbackPath;
  if (extensionOf(path || "") !== "material") {
    editor.hidden = true;
    editor.innerHTML = "";
    return;
  }
  editor.hidden = false;
  editor.innerHTML = "";

  const title = document.createElement("div");
  title.className = "inspect-editor-title";
  title.innerHTML = `<strong>Material Editor</strong><span>${(data?.textureReferences?.length || 0)} texture refs</span>`;
  editor.append(title);

  const grid = document.createElement("div");
  grid.className = "inspect-editor-grid";
  [
    { label: "Shader", key: "shader", value: data?.shader || "" },
    { label: "Material Path", key: "materialPathRef", value: data?.materialPathRef || "" },
  ].forEach((field) => {
    const label = document.createElement("label");
    label.className = "inspect-editor-field";
    label.innerHTML = `<span>${escapeHtml(field.label)}</span><input type="text" data-material-field="${escapeHtml(field.key)}" value="${escapeHtml(field.value)}" spellcheck="false" />`;
    grid.append(label);
  });
  editor.append(grid);

  const listTitle = document.createElement("div");
  listTitle.className = "inspect-editor-subtitle";
  listTitle.textContent = "Texture References";
  editor.append(listTitle);

  const list = document.createElement("div");
  list.className = "inspect-reference-list";
  (data?.textureReferences || []).forEach((ref, index) => {
    const row = document.createElement("div");
    row.className = "inspect-reference-row";
    const meta = document.createElement("div");
    meta.className = "inspect-reference-meta";
    meta.innerHTML = `<strong>${escapeHtml(ref.name || `slot_${index}`)}</strong><span>${escapeHtml(ref.suggested_format || "")}</span>`;
    const input = document.createElement("input");
    input.type = "text";
    input.value = ref.path || "";
    input.spellcheck = false;
    input.dataset.materialSlotName = ref.name || "";
    row.append(meta, input);
    list.append(row);
  });
  editor.append(list);

  const stringParams = data?.stringParameters || [];
  if (stringParams.length) {
    const paramsTitle = document.createElement("div");
    paramsTitle.className = "inspect-editor-subtitle";
    paramsTitle.textContent = "String Parameters";
    editor.append(paramsTitle);
    const paramsList = document.createElement("div");
    paramsList.className = "inspect-reference-list";
    stringParams.forEach((param, index) => {
      const row = document.createElement("div");
      row.className = "inspect-reference-row";
      const meta = document.createElement("div");
      meta.className = "inspect-reference-meta";
      meta.innerHTML = `<strong>${escapeHtml(param.name || `param_${index}`)}</strong><span>string</span>`;
      const input = document.createElement("input");
      input.type = "text";
      input.value = param.value || "";
      input.spellcheck = false;
      input.dataset.materialParamName = param.name || "";
      row.append(meta, input);
      paramsList.append(row);
    });
    editor.append(paramsList);
  }

  const actions = document.createElement("div");
  actions.className = "inspect-editor-actions";
  const save = document.createElement("button");
  save.type = "button";
  save.className = "primary";
  save.textContent = "Save material";
  save.addEventListener("click", () => saveMaterialEditor(path));
  actions.append(save);
  editor.append(actions);
}

function renderTextureEditor(data, fallbackPath = "") {
  const editor = $("textureEditor");
  if (!editor) return;
  const path = data?.path || fallbackPath;
  if (extensionOf(path || "") !== "texture") {
    editor.hidden = true;
    editor.innerHTML = "";
    return;
  }

  editor.hidden = false;
  editor.innerHTML = "";

  const title = document.createElement("div");
  title.className = "inspect-editor-title";
  title.innerHTML = `<strong>Texture Inspector</strong><span>${escapeHtml(fileNameOf(path))}</span>`;
  editor.append(title);

  const grid = document.createElement("div");
  grid.className = "inspect-editor-grid";
  const rows = [
    ["Path", path || ""],
    ["Resolution", data?.width && data?.height ? `${data.width}x${data.height}` : "-"],
    ["Mip levels", data?.mips ?? "-"],
    ["Tile", data?.tile_w && data?.tile_h ? `${data.tile_w}x${data.tile_h}` : "-"],
    ["ACE format code", data?.ace_format_code || "-"],
    ["Companion .texturemips", data?.texturemips_path || "-"],
    ["Guessed DDS format", data?.original_format || "-"],
    ["DXGI", data?.original_dxgi ?? "-"],
    ["Block size", data?.block_bytes ? `${data.block_bytes} bytes` : "-"],
  ];

  rows.forEach(([labelText, valueText]) => {
    const label = document.createElement("label");
    label.className = "inspect-editor-field";
    label.innerHTML = `<span>${escapeHtml(labelText)}</span><input type="text" value="${escapeHtml(String(valueText ?? ""))}" readonly spellcheck="false" />`;
    grid.append(label);
  });
  editor.append(grid);

  const summaryTitle = document.createElement("div");
  summaryTitle.className = "inspect-editor-subtitle";
  summaryTitle.textContent = "Texture Summary";
  editor.append(summaryTitle);

  const summary = document.createElement("pre");
  summary.className = "output small inspect-summary-box";
  summary.textContent = data?.summary || "";
  editor.append(summary);
}

async function saveMaterialEditor(path) {
  const editor = $("materialEditor");
  if (!editor) return;
  const getField = (name) => editor.querySelector(`[data-material-field="${name}"]`)?.value.trim() || "";
  const slotReplacements = Array.from(editor.querySelectorAll("[data-material-slot-name]"))
    .map((input) => ({ name: input.dataset.materialSlotName || "", value: input.value.trim() }))
    .filter((row) => row.name && row.value);
  const parameterReplacements = Array.from(editor.querySelectorAll("[data-material-param-name]"))
    .map((input) => ({ name: input.dataset.materialParamName || "", value: input.value.trim() }))
    .filter((row) => row.name && row.value);
  const payload = {
    path,
    shader: getField("shader"),
    materialPathRef: getField("materialPathRef"),
    slotReplacements,
    parameterReplacements,
  };
  const result = await call("update_material", payload);
  if (!result.ok) return;
  await inspectPath(path, false);
}

async function addActorResource(actorPath, kind, resource) {
  const value = String(resource || "").trim();
  if (!value) {
    log("Please enter a path first", true);
    return;
  }
  const result = await call("update_actor_resource", { path: actorPath, kind, action: "add", resource: value });
  if (!result.ok) return;
  await inspectPath(actorPath, false);
}

async function removeActorResource(actorPath, kind, resource) {
  const result = await showConfirmModal({
    type: "danger",
    title: "Remove actor resource",
    message: `Remove this ${kind} from the actor?`,
    details: resource,
    confirmLabel: "Remove",
    loadingLabel: "Removing...",
    onConfirm: () => call("update_actor_resource", { path: actorPath, kind, action: "remove", resource }),
  });
  if (!result) return;
  if (!result.ok) return;
  await inspectPath(actorPath, false);
}

function renderVisualPresetEditor(data, fallbackPath = "") {
  const editor = $("visualPresetEditor");
  if (!editor) return;
  const path = data?.path || fallbackPath;
  if (extensionOf(path || "") !== "visualcarpreset") {
    editor.hidden = true;
    editor.innerHTML = "";
    return;
  }
  const references = (data?.references || []).filter((ref) => {
    const value = ref?.path || "";
    return value && value !== data?.cardesign && value !== data?.rimDesign;
  });
  editor.hidden = false;
  editor.innerHTML = "";

  const title = document.createElement("div");
  title.className = "inspect-editor-title";
  title.innerHTML = `<strong>Visual Preset Editor</strong><span>${references.length} Referenzen</span>`;
  editor.append(title);

  const grid = document.createElement("div");
  grid.className = "inspect-editor-grid";
  [
    { label: "Manufacturer", key: "manufacturer", value: data?.manufacturer || "" },
    { label: "Name", key: "name", value: data?.name || "" },
    { label: "Presentation", key: "presentation", value: data?.presentation || "" },
    { label: "Cardesign", key: "cardesign", value: data?.cardesign || "" },
    { label: "Rim Design", key: "rimDesign", value: data?.rimDesign || "" },
    { label: "Start Number", key: "startNumber", value: data?.startNumber || data?.start_number || "" },
  ].forEach((field) => {
    const label = document.createElement("label");
    label.className = "inspect-editor-field";
    label.innerHTML = `<span>${escapeHtml(field.label)}</span><input type="text" data-visual-field="${escapeHtml(field.key)}" value="${escapeHtml(field.value)}" spellcheck="false" />`;
    grid.append(label);
  });
  editor.append(grid);

  const listTitle = document.createElement("div");
  listTitle.className = "inspect-editor-subtitle";
  listTitle.textContent = "Weitere Referenzen";
  editor.append(listTitle);

  const list = document.createElement("div");
  list.className = "inspect-reference-list";
  references.forEach((ref, index) => {
    const row = document.createElement("div");
    row.className = "inspect-reference-row";
    const meta = document.createElement("div");
    meta.className = "inspect-reference-meta";
    meta.innerHTML = `<strong>${escapeHtml(ref.category || "reference")}</strong><span>${escapeHtml(ref.offset_hex || "")}</span>`;
    const input = document.createElement("input");
    input.type = "text";
    input.value = ref.path || "";
    input.spellcheck = false;
    input.dataset.referenceIndex = String(index);
    input.dataset.referenceOriginal = ref.path || "";
    row.append(meta, input);
    list.append(row);
  });
  editor.append(list);

  const actions = document.createElement("div");
  actions.className = "inspect-editor-actions";
  const save = document.createElement("button");
  save.type = "button";
  save.className = "primary";
  save.textContent = "Save visual preset";
  save.addEventListener("click", () => saveVisualPresetEditor(path));
  actions.append(save);
  editor.append(actions);
}

async function saveVisualPresetEditor(path) {
  const editor = $("visualPresetEditor");
  if (!editor) return;
  const getField = (name) => editor.querySelector(`[data-visual-field="${name}"]`)?.value.trim() || "";
  const replacements = Array.from(editor.querySelectorAll("[data-reference-original]"))
    .map((input) => ({ old: input.dataset.referenceOriginal || "", new: input.value.trim() }))
    .filter((row) => row.old && row.new && row.old !== row.new);
  const payload = {
    path,
    manufacturer: getField("manufacturer"),
    name: getField("name"),
    presentation: getField("presentation"),
    cardesign: getField("cardesign"),
    rimDesign: getField("rimDesign"),
    startNumber: getField("startNumber").replace(/\D/g, "").slice(0, 3),
    replacements,
  };
  const result = await call("update_visualpreset", payload);
  if (!result.ok) return;
  await inspectPath(path, false);
}

function renderCarFinalStateEditor(data, fallbackPath = "") {
  const editor = $("carFinalStateEditor");
  if (!editor) return;
  const path = data?.path || fallbackPath;
  if (extensionOf(path || "") !== "carfinalstate") {
    editor.hidden = true;
    editor.innerHTML = "";
    return;
  }
  const topValues = new Set(
    [
      data?.carDataPath,
      data?.actorPathRef,
      data?.mechanicalPresetPath,
      data?.visualPresetPath,
      data?.compatibleRimPath,
      data?.rimDesignPath,
      data?.extSkinMaterialPath,
    ].filter(Boolean),
  );
  const references = (data?.references || []).filter((ref) => {
    const value = ref?.path || "";
    return value && !topValues.has(value);
  });

  editor.hidden = false;
  editor.innerHTML = "";

  const title = document.createElement("div");
  title.className = "inspect-editor-title";
  title.innerHTML = `<strong>Car Final State Editor</strong><span>${references.length} Referenzen</span>`;
  editor.append(title);

  const grid = document.createElement("div");
  grid.className = "inspect-editor-grid";
  [
    { label: "Car Data", key: "carDataPath", value: data?.carDataPath || "" },
    { label: "Actor", key: "actorPathRef", value: data?.actorPathRef || "" },
    { label: "Mechanical Preset", key: "mechanicalPresetPath", value: data?.mechanicalPresetPath || "" },
    { label: "Visual Preset", key: "visualPresetPath", value: data?.visualPresetPath || "" },
    { label: "Compatible Rim", key: "compatibleRimPath", value: data?.compatibleRimPath || "" },
    { label: "Rim Design", key: "rimDesignPath", value: data?.rimDesignPath || "" },
  ].forEach((field) => {
    const label = document.createElement("label");
    label.className = "inspect-editor-field";
    label.innerHTML = `<span>${escapeHtml(field.label)}</span><input type="text" data-finalstate-field="${escapeHtml(field.key)}" value="${escapeHtml(field.value)}" spellcheck="false" />`;
    grid.append(label);
  });
  editor.append(grid);

  const listTitle = document.createElement("div");
  listTitle.className = "inspect-editor-subtitle";
  listTitle.textContent = "Weitere Referenzen";
  editor.append(listTitle);

  const list = document.createElement("div");
  list.className = "inspect-reference-list";
  references.forEach((ref, index) => {
    const row = document.createElement("div");
    row.className = "inspect-reference-row";
    const meta = document.createElement("div");
    meta.className = "inspect-reference-meta";
    meta.innerHTML = `<strong>${escapeHtml(ref.category || "reference")}</strong><span>${escapeHtml(ref.offset_hex || "")}</span>`;
    const input = document.createElement("input");
    input.type = "text";
    input.value = ref.path || "";
    input.spellcheck = false;
    input.dataset.referenceIndex = String(index);
    input.dataset.referenceOriginal = ref.path || "";
    row.append(meta, input);
    list.append(row);
  });
  editor.append(list);

  const actions = document.createElement("div");
  actions.className = "inspect-editor-actions";
  const save = document.createElement("button");
  save.type = "button";
  save.className = "primary";
  save.textContent = "Save car final state";
  save.addEventListener("click", () => saveCarFinalStateEditor(path));
  actions.append(save);
  editor.append(actions);
}

async function saveCarFinalStateEditor(path) {
  const editor = $("carFinalStateEditor");
  if (!editor) return;
  const getField = (name) => editor.querySelector(`[data-finalstate-field="${name}"]`)?.value.trim() || "";
  const replacements = Array.from(editor.querySelectorAll("[data-reference-original]"))
    .map((input) => ({ old: input.dataset.referenceOriginal || "", new: input.value.trim() }))
    .filter((row) => row.old && row.new && row.old !== row.new);
  const payload = {
    path,
    carDataPath: getField("carDataPath"),
    actorPathRef: getField("actorPathRef"),
    mechanicalPresetPath: getField("mechanicalPresetPath"),
    visualPresetPath: getField("visualPresetPath"),
    compatibleRimPath: getField("compatibleRimPath"),
    rimDesignPath: getField("rimDesignPath"),
    replacements,
  };
  const result = await call("update_carfinalstate", payload);
  if (!result.ok) return;
  await inspectPath(path, false);
}

function renderMechanicalEditor(data, fallbackPath = "") {
  const editor = $("mechanicalEditor");
  if (!editor) return;
  const path = data?.path || fallbackPath;
  const visuals = data?.visualPresets || [];
  if (extensionOf(path || "") !== "mechanicalcarpreset") {
    editor.hidden = true;
    editor.innerHTML = "";
    return;
  }
  editor.hidden = false;
  editor.innerHTML = "";
  const title = document.createElement("div");
  title.className = "mechanical-editor-title";
  title.innerHTML = `<strong>Mechanical Visual Presets</strong><span>${visuals.length} Eintraege</span>`;
  editor.append(title);

  const addRow = document.createElement("div");
  addRow.className = "mechanical-add-row";
  const input = document.createElement("input");
  input.type = "text";
  input.placeholder = "content\\...\\preset_xxx.visualcarpreset";
  input.spellcheck = false;
  input.id = "mechanicalVisualAddInput";
  input.addEventListener("keydown", (event) => {
    if (event.key === "Enter") {
      event.preventDefault();
      addMechanicalVisual(path);
    }
  });
  const addButton = document.createElement("button");
  addButton.type = "button";
  addButton.textContent = "Hinzufuegen";
  addButton.addEventListener("click", () => addMechanicalVisual(path));
  addRow.append(input, addButton);
  editor.append(addRow);

  const list = document.createElement("div");
  list.className = "mechanical-visual-list";
  visuals.forEach((visual) => {
    const row = document.createElement("div");
    row.className = "mechanical-visual-row";
    const label = document.createElement("span");
    label.textContent = visual;
    const button = document.createElement("button");
    button.type = "button";
    button.textContent = "Remove";
    button.addEventListener("click", () => removeMechanicalVisual(path, visual));
    row.append(label, button);
    list.append(row);
  });
  editor.append(list);
}

async function addMechanicalVisual(mechanicalPath) {
  const input = $("mechanicalVisualAddInput");
  const visualPath = input?.value.trim() || "";
  if (!visualPath) {
    log("Please enter a visual preset path first", true);
    return;
  }
  const result = await call("add_mechanical_visual", mechanicalPath, visualPath);
  if (!result.ok) return;
  await inspectPath(mechanicalPath, false);
}

async function removeMechanicalVisual(mechanicalPath, visualPath) {
  const result = await showConfirmModal({
    type: "danger",
    title: "Remove visual preset",
    message: "Remove this visual preset?",
    details: visualPath,
    confirmLabel: "Remove",
    loadingLabel: "Removing...",
    onConfirm: () => call("remove_mechanical_visual", mechanicalPath, visualPath),
  });
  if (!result) return;
  if (!result.ok) return;
  await inspectPath(mechanicalPath, false);
}

async function useSelected() {
  if (!state.selected || state.selected.isDir) return;
  if (browserItemBlockedForCurrentMode(state.selected)) {
    showBrowserLockedNotice();
    return;
  }
  if (!enduserBrowserFileAllowedForTool(state.selected.path)) {
    showBrowserToolMismatchNotice();
    return;
  }
  const target = defaultTargetForPath(state.selected.path);
  if (target) {
    if (target === "skinCardesign" && isUnsupportedSkinTemplatePath(state.selected.path)) {
      const support = skinTemplateSupportForPath(state.selected.path);
      log(skinTemplateUnsupportedText(support), true);
      setInspectOutputText(skinTemplateUnsupportedText(support));
      return;
    }
    setPath(target, state.selected.path);
    await applyPathDefaults(target, state.selected.path);
  }
}

function defaultTargetForPath(path) {
  const ext = extensionOf(path);
  const map = {
    texture: "texturePath",
    texturemips: "mipsPath",
    dds: "ddsIn",
    mesh: "meshPath",
    rimmesh: "rimSourcePath",
    compatiblerim: "rimCompatPath",
    png: "uvTexturePath",
    jpg: "uvTexturePath",
    jpeg: "uvTexturePath",
    bmp: "uvTexturePath",
    webp: "uvTexturePath",
    gif: "uvTexturePath",
    tga: "uvTexturePath",
    tif: "uvTexturePath",
    tiff: "uvTexturePath",
    material: "materialPath",
    cardesign: "skinCardesign",
    design: "skinDesign",
    actor: "skinActor",
    visualcarpreset: "skinVisual",
    mechanicalcarpreset: "skinMechanical",
    carfinalstate: "skinCarFinalState",
  };
  return map[ext] || "";
}

function extensionOf(path) {
  return path.toLowerCase().split(".").pop();
}

function setPath(target, path) {
  if (!$(target)) return;
  $(target).value = path;
  updatePathFileName(target);
  log(`${t("log_set")} ${target}: ${path}`);
  renderWorkflowCompatibility();
}

function updatePathFileName(target) {
  const input = $(target);
  if (!input) return;
  const path = input.value.trim();
  const name = path ? fileNameOf(path) : "";
  document.querySelectorAll("[data-path-filename]").forEach((element) => {
    if (element.dataset.pathFilename !== target) return;
    element.textContent = name || "-";
    element.title = name || "";
  });
}

function setupPathFileNames(root = document) {
  const targets = new Set(
    [...root.querySelectorAll("[data-path-filename]")]
      .map((element) => element.dataset.pathFilename)
      .filter(Boolean),
  );
  targets.forEach((target) => {
    const input = $(target);
    if (!input) return;
    updatePathFileName(target);
    input.addEventListener("input", () => updatePathFileName(target));
    input.addEventListener("change", () => updatePathFileName(target));
  });
}

function setupConverterPathDefaults() {
  ["texturePath", "mipsPath", "meshPath", "backTexture", "ddsIn", "pngIn"].forEach((target) => {
    const input = $(target);
    if (!input) return;
    input.addEventListener("change", async () => {
      if (input.dataset.skipPathDefaults === "1") return;
      const path = input.value.trim();
      if (!path) return;
      await applyPathDefaults(target, path);
    });
  });
}

function rimCardesignPathValues() {
  return [...document.querySelectorAll(".rim-cardesign-path")]
    .map((input) => input.value.trim())
    .filter(Boolean);
}

function addRimCardesignPathRow(path = "") {
  const list = $("rimCardesignRows");
  if (!list) return;
  const row = document.createElement("div");
  row.className = "dynamic-path-row";
  const input = document.createElement("input");
  input.type = "text";
  input.spellcheck = false;
  input.className = "rim-cardesign-path";
  input.value = path;
  setupPathInputDrop(input, { filter: "Cardesign (*.cardesign)" });
  const pick = document.createElement("button");
  pick.type = "button";
  pick.textContent = "File";
  pick.addEventListener("click", async () => {
    const result = await window.pywebview.api.choose_path("file", ["Cardesign (*.cardesign)", "All files (*.*)"], "");
    if (!showResult(result, t("log_path_selected")) || !result.data.path) return;
    input.value = result.data.path;
  });
  const remove = document.createElement("button");
  remove.type = "button";
  remove.textContent = "Remove";
  remove.addEventListener("click", () => {
    if (list.children.length <= 1) {
      input.value = "";
      return;
    }
    row.remove();
  });
  row.append(input, pick, remove);
  list.append(row);
}

function replaceExtension(path, ext) {
  const idx = path.lastIndexOf(".");
  return `${idx > -1 ? path.slice(0, idx) : path}${ext}`;
}

function fileNameWithExtension(path, ext) {
  return replaceExtension(path.split(/[\\/]/).pop() || path, ext);
}

function fileNameWithoutExtension(path) {
  const name = path.split(/[\\/]/).pop() || path;
  const idx = name.lastIndexOf(".");
  return idx > -1 ? name.slice(0, idx) : name;
}

function fileNameOf(path) {
  return path.split(/[\\/]/).pop() || path;
}

async function applySelectionForCurrentTool(forceTabChange = true) {
  if (!state.selected || state.selected.isDir) return;
  if (browserItemBlockedForCurrentMode(state.selected)) {
    showBrowserLockedNotice();
    return;
  }
  const path = state.selected.path;
  const ext = extensionOf(path);
  const tab = activeTab();
  if (!enduserBrowserFileAllowedForTool(path, tab)) {
    showBrowserToolMismatchNotice(tab);
    return;
  }

  if (tab === "convert") {
    await applySelectionForExport();
    await applySelectionForImport(false);
    if (forceTabChange) setTab("convert");
    return;
  }
  if (tab === "package" && ext === "kspkg") {
    applySelectionForPackage();
    if (forceTabChange) setTab("package");
    return;
  }
  if (tab === "mesh" && ext === "mesh") {
    await applySelectionForMesh();
    if (forceTabChange) setTab("mesh");
    return;
  }
  if (tab === "uv") {
    await applySelectionForUv();
    if (forceTabChange) setTab("uv");
    return;
  }
  if (tab === "material" && ext === "material") {
    setPath("materialPath", path);
    await setMaterialOutputDefaults(path, true);
    return;
  }
  if (tab === "skin") {
    useSelected();
    return;
  }
  useSelected();
}

async function applySelectionForExport(overwriteDerived = false) {
  if (!state.selected || state.selected.isDir) return;
  if (browserItemBlockedForCurrentMode(state.selected)) {
    showBrowserLockedNotice();
    return;
  }
  const path = state.selected.path;
  const ext = extensionOf(path);
  if (ext === "texture") {
    setPath("texturePath", path);
    setPath("mipsPath", replaceExtension(path, ".texturemips"));
    await setConvertedDerivedPath("ddsOut", "textures", path, ".dds", true);
    await setConvertedDerivedPath("pngOut", "textures", path, ".png", true);
  } else if (ext === "texturemips") {
    setPath("mipsPath", path);
    setPath("texturePath", replaceExtension(path, ".texture"));
    await setConvertedDerivedPath("ddsOut", "textures", path, ".dds", true);
    await setConvertedDerivedPath("pngOut", "textures", path, ".png", true);
  } else {
    await useSelected();
  }
}

async function applySelectionForImport(fillTexture = true) {
  if (!state.selected || state.selected.isDir) return;
  if (browserItemBlockedForCurrentMode(state.selected)) {
    showBrowserLockedNotice();
    return;
  }
  const path = state.selected.path;
  const ext = extensionOf(path);
  if (ext === "dds") {
    setPath("ddsIn", path);
    const carSource = $("backTexture")?.value.trim() || path;
    await setConvertedDerivedPath("mipsOut", "textures", carSource, ".texturemips", !$("mipsOut").value.trim(), path);
  } else if (ext === "png") {
    setPath("pngIn", path);
    const carSource = $("backTexture")?.value.trim() || path;
    await setConvertedDerivedPath("mipsOut", "textures", carSource, ".texturemips", !$("mipsOut").value.trim(), path);
  } else if (fillTexture && ext === "texture") {
    setPath("backTexture", path);
    const ddsSource = $("pngIn")?.value.trim() || $("ddsIn")?.value.trim() || path;
    await setConvertedDerivedPath("mipsOut", "textures", path, ".texturemips", !$("mipsOut").value.trim(), ddsSource);
  }
}

function applySelectionForPackage(overwriteDerived = false) {
  if (!state.selected || state.selected.isDir) return;
  if (browserItemBlockedForCurrentMode(state.selected)) {
    showBrowserLockedNotice();
    return;
  }
  const path = state.selected.path;
  if (extensionOf(path) !== "kspkg") return;
  setPath("packagePath", path);
  if (overwriteDerived || !$("packageOutDir").value.trim()) {
    const root = normalizeRootPath($("rootPath").value || state.root || "");
    if (root) setPath("packageOutDir", root);
    else setDerivedPath("packageOutDir", path, "", true);
  }
}

async function applySelectionForMesh(overwriteDerived = false) {
  if (!state.selected || state.selected.isDir) return;
  if (browserItemBlockedForCurrentMode(state.selected)) {
    showBrowserLockedNotice();
    return;
  }
  const path = state.selected.path;
  if (extensionOf(path) !== "mesh") return;
  setPath("meshPath", path);
  if (overwriteDerived || !$("meshObjOut").value.trim()) {
    await setConvertedDerivedPath("meshObjOut", "meshes", path, ".obj", true);
  }
}

function applySelectionForRim(overwriteDerived = false, target = "rimSourcePath") {
  if (!state.selected || state.selected.isDir) return;
  if (browserItemBlockedForCurrentMode(state.selected)) {
    showBrowserLockedNotice();
    return;
  }
  const path = state.selected.path;
  if (extensionOf(path) !== "rimmesh") return;
  setPath(target, path);
  if (target === "rimTargetPath" && (overwriteDerived || !$("rimOutPath").value.trim())) {
    setPath("rimOutPath", replaceExtension(path, "_swap_test.rimmesh"));
  }
}

async function applySelectionForUv(overwriteDerived = false) {
  if (!state.selected || state.selected.isDir) return;
  if (browserItemBlockedForCurrentMode(state.selected)) {
    showBrowserLockedNotice();
    return;
  }
  const path = state.selected.path;
  const ext = extensionOf(path);
  if (ext === "mesh") {
    setPath("uvMeshPath", path);
    if (overwriteDerived || !$("uvExportPath").value.trim()) {
      await setConvertedDerivedPath("uvExportPath", "uv", path, ".png", true);
    }
    return;
  }
  if (["png", "jpg", "jpeg", "bmp", "webp", "gif", "tga", "tif", "tiff"].includes(ext)) {
    setPath("uvTexturePath", path);
  }
}

function renderMeshSummary(data) {
  const wrap = $("meshSummary");
  if (!wrap) return;
  wrap.innerHTML = "";
  if (!data || extensionOf(data.path || "") !== "mesh") return;
  const rows = [
    ["Mesh", data.meshName || "-"],
    ["Material", data.materialPathRef || "-"],
    ["Vertices", String(data.vertexCount ?? 0)],
    ["Normals", String(data.normalCount ?? 0)],
    ["UVs", String(data.uvCount ?? 0)],
    ["Triangles", String(data.triangleCount ?? 0)],
    ["Submeshes", String((data.submeshes || []).length)],
  ];
  rows.forEach(([label, value]) => {
    const chip = document.createElement("div");
    chip.className = "chip";
    chip.innerHTML = `<strong>${escapeHtml(label)}</strong><span>${escapeHtml(value)}</span>`;
    wrap.append(chip);
  });
}

function renderRimSummary(targetId, data) {
  const wrap = $(targetId);
  if (!wrap) return;
  wrap.innerHTML = "";
  const rim = data?.result || data;
  if (!rim) return;
  const rows = [
    ["Name", rim.name || "-"],
    ["Brand", rim.brand || "-"],
    ["Diameter", rim.diameter ?? "-"],
    ["Width", rim.width ?? "-"],
    ["Offset", rim.offset ?? "-"],
    ["LF", rim.mesh_lf || "-"],
    ["RF", rim.mesh_rf || "-"],
    ["Blur LF", rim.mesh_blur_lf || "-"],
    ["Blur RF", rim.mesh_blur_rf || "-"],
    ["Tyre", rim.tyre_mesh || "-"],
    ["Rim design", rim.rimdesign || "-"],
  ];
  rows.forEach(([label, value]) => {
    const chip = document.createElement("div");
    chip.className = "chip";
    chip.innerHTML = `<strong>${escapeHtml(label)}</strong><span>${escapeHtml(String(value))}</span>`;
    wrap.append(chip);
  });
  (rim.missing_refs || []).forEach((value) => {
    const chip = document.createElement("div");
    chip.className = "chip danger";
    chip.innerHTML = `<strong>Missing</strong><span>${escapeHtml(String(value))}</span>`;
    wrap.append(chip);
  });
}

function renderRimCompatibility(source, target) {
  const wrap = $("rimCompatibility");
  if (!wrap) return;
  wrap.innerHTML = "";
  if (!source || !target) return;
  const warnings = [];
  if (source.diameter !== target.diameter) warnings.push(`Different diameter: source ${source.diameter ?? "-"} / target ${target.diameter ?? "-"}`);
  if (source.width !== target.width) warnings.push(`Different width: source ${source.width ?? "-"} / target ${target.width ?? "-"}`);
  if (source.offset !== target.offset) warnings.push(`Different offset: source ${source.offset ?? "-"} / target ${target.offset ?? "-"}`);
  const chip = document.createElement("div");
  chip.className = warnings.some((row) => row.startsWith("Different diameter") || row.startsWith("Different width")) ? "chip danger" : "chip";
  chip.innerHTML = `<strong>Compatibility</strong><span>${warnings.length ? escapeHtml(warnings.join(" | ")) : "Dimensions match"}</span>`;
  wrap.append(chip);
}

async function inspectRimMesh(kind) {
  const inputId = kind === "target" ? "rimTargetPath" : "rimSourcePath";
  const summaryId = kind === "target" ? "rimTargetSummary" : "rimSourceSummary";
  const path = $(inputId).value.trim();
  if (!path) {
    log(`Please select a ${kind} .rimmesh first`, true);
    return null;
  }
  const result = await call("inspect_rimmesh", path, $("rootPath").value.trim());
  if (!result.ok) return null;
  renderRimSummary(summaryId, result.data);
  setInspectOutputText(result.data?.summary || "");
  const otherPath = $(kind === "target" ? "rimSourcePath" : "rimTargetPath").value.trim();
  if (otherPath) {
    const other = await window.pywebview.api.inspect_rimmesh(otherPath, $("rootPath").value.trim());
    if (other?.ok) {
      const source = kind === "source" ? result.data.result : other.data.result;
      const target = kind === "target" ? result.data.result : other.data.result;
      renderRimCompatibility(source, target);
    }
  }
  return result.data?.result || null;
}

function renderPackageSummary(data) {
  const wrap = $("packageSummary");
  if (!wrap) return;
  wrap.innerHTML = "";
  if (!data || extensionOf(data.file || "") !== "kspkg") return;
  const rows = [
    ["Package", fileNameOf(data.file || "") || "-"],
    ["Entries", String(data.entry_count ?? 0)],
    ["Files", String(data.file_count ?? 0)],
    ["Directories", String(data.directory_count ?? 0)],
    ["Encrypted", String(data.encrypted_count ?? 0)],
  ];
  rows.forEach(([label, value]) => {
    const chip = document.createElement("div");
    chip.className = "chip";
    chip.innerHTML = `<strong>${escapeHtml(label)}</strong><span>${escapeHtml(value)}</span>`;
    wrap.append(chip);
  });
}

function resetPackageProgress() {
  state.packageProgress = null;
  updatePackageProgress();
}

function setPackageExtractModalVisible(visible) {
  const modal = $("packageExtractModal");
  if (!modal) return;
  modal.hidden = !visible;
  state.packageExtractModalOpen = Boolean(visible);
  renderOfficialCarResourcesStatus(state.officialCarResourcesStatus);
}

function resetPackageExtractModal({ official = false } = {}) {
  state.packageExtractCancelRequested = false;
  const title = $("packageExtractModalTitle");
  if (title) title.textContent = official ? "Preparing Official Preview Resources" : "Extracting package";
  const busyLabel = $("packageExtractModalBusyLabel");
  if (busyLabel) busyLabel.textContent = official ? "Preparing..." : "Extracting...";
  const cancel = $("packageExtractModalCancel");
  if (cancel) {
    cancel.disabled = false;
    cancel.textContent = "Cancel";
  }
  updatePackageExtractModal(null);
}

function showPackageExtractModal(options = {}) {
  resetPackageExtractModal(options);
  setPackageExtractModalVisible(true);
}

function hidePackageExtractModal() {
  setPackageExtractModalVisible(false);
  state.packageExtractCancelRequested = false;
}

function formatPackageExtractBytes(bytes = 0) {
  const value = Math.max(0, Number(bytes) || 0);
  if (!value) return "0 B";
  const units = ["B", "KiB", "MiB", "GiB", "TiB"];
  const unit = Math.min(units.length - 1, Math.floor(Math.log(value) / Math.log(1024)));
  const scaled = value / (1024 ** unit);
  return `${scaled.toFixed(unit === 0 ? 0 : scaled >= 100 ? 0 : scaled >= 10 ? 1 : 2)} ${units[unit]}`;
}

function formatPackageExtractDuration(seconds = 0) {
  const total = Math.max(0, Math.round(Number(seconds) || 0));
  if (!total) return "-";
  const hours = Math.floor(total / 3600);
  const minutes = Math.floor((total % 3600) / 60);
  const secs = total % 60;
  if (hours) return `${hours}h ${minutes}m`;
  if (minutes) return `${minutes}m ${secs}s`;
  return `${secs}s`;
}

function formatPackageExtractPercent(value = 0) {
  const percent = Math.max(0, Math.min(100, Number(value) || 0));
  return Number.isInteger(percent) ? `${percent}%` : `${percent.toFixed(1)}%`;
}

function formatPackagePreviewStage(value = "") {
  const labels = {
    start: "Starting",
    scan: "Scanning resources",
    variants: "Vehicle variants",
    mesh: "3D model",
    uv: "UV template",
    ao: "Ambient occlusion",
    thumbnail: "Thumbnail",
    manifest: "Saving metadata",
    done: "Complete",
  };
  const key = String(value || "").trim();
  return labels[key] || "Preview assets";
}

function updatePackageExtractModal(info) {
  const modal = $("packageExtractModal");
  if (!modal) return;
  const fill = $("packageExtractModalFill");
  const percentNode = $("packageExtractModalPercent");
  const countNode = $("packageExtractModalCount");
  const pathNode = $("packageExtractModalPath");
  const filesNode = $("packageExtractModalFiles");
  const dirsNode = $("packageExtractModalDirs");
  const dataNode = $("packageExtractModalData");
  const subtitleNode = $("packageExtractModalSubtitle");
  const cancel = $("packageExtractModalCancel");
  const phase = String(info?.phase || "idle");
  const percent = Math.max(0, Math.min(100, Number(info?.percent || 0)));
  const current = Number(info?.current || 0);
  const total = Number(info?.total || 0);
  const carId = String(info?.carId || "").trim();
  const previewMessage = String(info?.previewMessage || "").trim();
  const path = phase === "prepare_preview_assets"
    ? [carId ? `Preparing ${carId}` : "Preparing preview assets", previewMessage].filter(Boolean).join(" · ")
    : String(info?.path || info?.message || "");
  if (fill) fill.style.width = `${percent}%`;
  if (percentNode) percentNode.textContent = formatPackageExtractPercent(percent);
  if (countNode) countNode.textContent = total ? `${current} / ${total}` : "Preparing...";
  if (pathNode) pathNode.textContent = path || "Waiting for package data...";
  if (filesNode) filesNode.textContent = `Files written: ${Number(info?.filesWritten || 0)}`;
  if (dirsNode) dirsNode.textContent = `Directories created: ${Number(info?.directoriesCreated || 0)}`;
  if (dataNode) {
    const processed = Number(info?.bytesProcessed || 0);
    const totalBytes = Number(info?.totalBytes || 0);
    const speed = Number(info?.bytesPerSecond || 0);
    const eta = Number(info?.etaSeconds || 0);
    if (phase === "prepare_preview_assets") {
      const stageCurrent = Number(info?.previewStageCurrent || 0);
      const stageTotal = Number(info?.previewStageTotal || 0);
      const stageCount = stageTotal ? ` ${stageCurrent}/${stageTotal}` : "";
      dataNode.textContent = `Preview: ${formatPackagePreviewStage(info?.previewStage)}${stageCount} · ETA ${formatPackageExtractDuration(eta)}`;
    } else {
      dataNode.textContent = totalBytes
        ? `Data: ${formatPackageExtractBytes(processed)} / ${formatPackageExtractBytes(totalBytes)} · ${speed ? `${formatPackageExtractBytes(speed)}/s` : "measuring speed"} · ETA ${formatPackageExtractDuration(eta)}`
        : "Data: preparing...";
    }
  }
  if (subtitleNode) {
    if (phase === "cancel_requested") subtitleNode.textContent = "Cancelling...";
    else if (phase === "cancelled") subtitleNode.textContent = "Cancelled";
    else if (phase === "done") subtitleNode.textContent = "Finishing...";
    else if (phase === "error") subtitleNode.textContent = "Failed";
    else if (phase === "prepare_preview_assets") subtitleNode.textContent = total
      ? `Preparing preview assets ${current} of ${total} · ${formatPackagePreviewStage(info?.previewStage)}`
      : "Preparing preview assets";
    else subtitleNode.textContent = total ? `${current} of ${total}` : "Preparing...";
  }
  if (cancel && phase === "cancel_requested") {
    cancel.disabled = true;
    cancel.textContent = "Cancelling...";
  }
}

async function cancelPackageExtraction() {
  if (state.packageExtractCancelRequested) return;
  state.packageExtractCancelRequested = true;
  const cancel = $("packageExtractModalCancel");
  if (cancel) {
    cancel.disabled = true;
    cancel.textContent = "Cancelling...";
  }
  updatePackageExtractModal({ ...(state.packageProgress || {}), phase: "cancel_requested", message: "Cancelling package extraction..." });
  try {
    await window.pywebview.api.cancel_package_extract();
  } catch (error) {
    log(error?.message || String(error), true);
  }
}

function updatePackageProgressElements(prefix, info) {
  const base = prefix ? `${prefix}Package` : "package";
  const wrap = $(`${base}Progress`);
  const fill = $(`${base}ProgressFill`);
  const label = $(`${base}ProgressLabel`);
  const text = $(`${base}ProgressText`);
  if (!wrap || !fill || !label || !text) return;
  if (!info || info.phase === "done" || info.phase === "error") {
    if (info?.phase === "error") {
      wrap.hidden = false;
      fill.style.width = "0%";
      label.textContent = "Error";
      text.textContent = info.message || "";
      return;
    }
    wrap.hidden = true;
    fill.style.width = "0%";
    label.textContent = "0%";
    text.textContent = "";
    return;
  }
  wrap.hidden = false;
  const percent = Number(info.percent || 0);
  fill.style.width = `${Math.max(0, Math.min(100, percent))}%`;
  label.textContent = formatPackageExtractPercent(percent);
  const current = Number(info.current || 0);
  const total = Number(info.total || 0);
  const phase = String(info.phase || "");
  const carId = String(info.carId || "").trim();
  const previewMessage = String(info.previewMessage || "").trim();
  const path = phase === "prepare_preview_assets"
    ? [carId ? `Preparing ${carId}` : "Preparing preview assets", previewMessage].filter(Boolean).join(" · ")
    : String(info.path || "");
  const bytesProcessed = Number(info.bytesProcessed || 0);
  const totalBytes = Number(info.totalBytes || 0);
  const speed = Number(info.bytesPerSecond || 0);
  const eta = Number(info.etaSeconds || 0);
  const stageCurrent = Number(info.previewStageCurrent || 0);
  const stageTotal = Number(info.previewStageTotal || 0);
  const byteStatus = phase === "prepare_preview_assets"
    ? `${formatPackagePreviewStage(info.previewStage)}${stageTotal ? ` ${stageCurrent}/${stageTotal}` : ""}${eta ? ` · ETA ${formatPackageExtractDuration(eta)}` : ""}`
    : totalBytes
      ? `${formatPackageExtractBytes(bytesProcessed)}/${formatPackageExtractBytes(totalBytes)}${speed ? ` · ${formatPackageExtractBytes(speed)}/s` : ""}${eta ? ` · ETA ${formatPackageExtractDuration(eta)}` : ""}`
      : "";
  text.textContent = [total ? `${current}/${total}` : "", byteStatus, path].filter(Boolean).join(" - ");
}

function updatePackageProgress(payload = null) {
  if (payload) {
    state.packageProgress = { ...(state.packageProgress || {}), ...payload };
  }
  const info = state.packageProgress;
  updatePackageProgressElements("", info);
  updatePackageProgressElements("home", info);
  if (state.packageExtractModalOpen) updatePackageExtractModal(info);
}

function renderUvSummary(data) {
  const wrap = $("uvSummary");
  if (!wrap) return;
  wrap.innerHTML = "";
  const diagnosticsWrap = $("uvGlassDiagnostics");
  if (diagnosticsWrap) diagnosticsWrap.innerHTML = "";
  if (!data) return;
  const visibleGroups = visibleUvGroups();
  const skinGroups = (state.uvGroups || []).filter((group) => group.skinRelevant);
  const glassGroups = (state.uvGroups || []).filter((group) => group.materialCategory === "glass");
  const glassMaterials = (data.material_diagnostics || []).filter((item) => {
    const text = `${item?.name || ""} ${item?.material_path || ""} ${item?.shader || ""}`.toLowerCase();
    if (text.includes("light") || text.includes("lamp")) return false;
    return text.includes("window") || text.includes("windscreen") || text.includes("windshield") || text.includes("glass");
  });
  const rows = [
    ["Mesh", data.mesh_name || "-"],
    ["Material", data.material_path || "-"],
    ["UVs", String(data.uv_count ?? 0)],
    ["Triangles", String(data.triangle_count ?? 0)],
    ["UV groups", `${visibleGroups.length || 0} / ${state.uvGroups.length || 0}`],
    ["EXT_SKIN groups", String(skinGroups.length || 0)],
    ["Glass groups", String(glassGroups.length || 0)],
    ["Glass materials", String(glassMaterials.length || 0)],
    ["Slots", String((data.texture_slots || []).length)],
    ["Submeshes", String((data.submeshes || []).length)],
  ];
  rows.forEach(([label, value]) => {
    const chip = document.createElement("div");
    chip.className = "chip";
    chip.innerHTML = `<strong>${escapeHtml(label)}</strong><span>${escapeHtml(value)}</span>`;
    wrap.append(chip);
  });
  renderUvGlassDiagnostics(glassMaterials);
}

function classifyUvMaterialRange(range) {
  const text = [
    range?.name,
    range?.material,
    range?.materialPath,
    range?.resolvedMaterialPath,
    range?.shader,
  ].map((value) => String(value || "").toLowerCase()).join(" ");
  if (text.includes("skin")) return "skin";
  if (text.includes("light") || text.includes("lamp") || text.includes("emissive")) return "lights";
  if (text.includes("window") || text.includes("windscreen") || text.includes("windshield") || text.includes("glass")) return "glass";
  if (text.includes("decal") || text.includes("sticker") || text.includes("banner")) return "decals";
  if (text.includes("wheel") || text.includes("rim") || text.includes("tyre") || text.includes("tire")) return "wheels";
  return "other";
}

function formatUvMaterialValue(value) {
  if (Array.isArray(value)) return value.map((entry) => Number.isFinite(Number(entry)) ? Number(entry).toFixed(3) : String(entry)).join(", ");
  if (value === null || typeof value === "undefined") return "-";
  return String(value);
}

function renderUvGlassDiagnostics(items) {
  const wrap = $("uvGlassDiagnostics");
  if (!wrap) return;
  wrap.innerHTML = "";
  if (!items?.length) {
    const empty = document.createElement("div");
    empty.className = "uv-glass-empty";
    empty.textContent = "No window/glass material diagnostics found for this mesh.";
    wrap.append(empty);
    return;
  }
  items.forEach((item) => {
    const block = document.createElement("div");
    block.className = "uv-glass-card";
    const title = document.createElement("strong");
    title.textContent = item.name || "Glass material";
    const meta = document.createElement("div");
    meta.className = "muted";
    meta.textContent = `${item.shader || "Unknown shader"} - ${item.material_path || "-"}`;
    block.append(title, meta);
    const slots = (item.texture_slots || []).slice(0, 8);
    if (slots.length) {
      const slotList = document.createElement("div");
      slotList.className = "uv-glass-list";
      slots.forEach((slot) => {
        const row = document.createElement("div");
        row.innerHTML = `<span>${escapeHtml(slot.name || "slot")}</span><span>${escapeHtml(formatUvMaterialValue(slot.value))}</span>`;
        slotList.append(row);
      });
      block.append(slotList);
    }
    const params = (item.parameters || []).slice(0, 8);
    if (params.length) {
      const paramList = document.createElement("div");
      paramList.className = "uv-glass-list";
      params.forEach((param) => {
        const row = document.createElement("div");
        row.innerHTML = `<span>${escapeHtml(param.name || "parameter")}</span><span>${escapeHtml(formatUvMaterialValue(param.value))}</span>`;
        paramList.append(row);
      });
      block.append(paramList);
    }
    wrap.append(block);
  });
}

function uvMaterialFilterLabel(category) {
  return {
    all: "All",
    skin: "EXT_SKIN",
    decals: "Decals",
    glass: "Glass",
    lights: "Lights",
    wheels: "Wheels",
    other: "Other",
  }[category] || "Other";
}

function buildUvTriangleMaterialLookup(mesh) {
  const triangleCount = Math.floor((mesh?.indices || []).length / 3);
  const lookup = new Array(triangleCount).fill(null);
  (mesh?.material_ranges || []).forEach((range) => {
    const category = classifyUvMaterialRange(range);
    const blockName = String(range?.name || "").toLowerCase();
    const start = Math.max(0, Number(range.startTriangle) || 0);
    const count = Math.max(0, Number(range.triangleCount) || 0);
    const end = Math.min(triangleCount, start + count);
    for (let tri = start; tri < end; tri += 1) {
      lookup[tri] = {
        rangeIndex: range.index ?? null,
        category,
        name: range.name || "",
        material: range.material || "",
        skinRelevant: category === "skin" || blockName.includes("skin"),
      };
    }
  });
  return lookup;
}

function uvTriangleMaterialKey(info) {
  if (!info) return "other";
  return `${info.rangeIndex ?? "none"}:${info.category || "other"}:${info.name || ""}:${info.material || ""}`;
}

function summarizeUvGroupMaterial(triangles, materialLookup) {
  const counts = {};
  const names = {};
  let skinRelevant = false;
  triangles.forEach((tri) => {
    const info = materialLookup[tri];
    const category = info?.category || "other";
    counts[category] = (counts[category] || 0) + 1;
    const name = info?.name || info?.material || uvMaterialFilterLabel(category);
    if (name) names[name] = (names[name] || 0) + 1;
    if (info?.skinRelevant) skinRelevant = true;
  });
  const category = Object.keys(counts).sort((a, b) => counts[b] - counts[a])[0] || "other";
  const materialName = Object.keys(names).sort((a, b) => names[b] - names[a])[0] || uvMaterialFilterLabel(category);
  return { category, materialName, skinRelevant };
}

function buildUvGroups(mesh) {
  const indices = mesh?.indices || [];
  if (!indices.length) return [];
  const triangleCount = Math.floor(indices.length / 3);
  const materialLookup = buildUvTriangleMaterialLookup(mesh);
  const vertexToTriangles = new Map();
  for (let tri = 0; tri < triangleCount; tri += 1) {
    for (let corner = 0; corner < 3; corner += 1) {
      const vertexIndex = indices[tri * 3 + corner];
      if (!vertexToTriangles.has(vertexIndex)) vertexToTriangles.set(vertexIndex, []);
      vertexToTriangles.get(vertexIndex).push(tri);
    }
  }
  const visited = new Array(triangleCount).fill(false);
  const groups = [];
  for (let start = 0; start < triangleCount; start += 1) {
    if (visited[start]) continue;
    const startKey = uvTriangleMaterialKey(materialLookup[start]);
    const queue = [start];
    visited[start] = true;
    const triangles = [];
    const vertices = new Set();
    while (queue.length) {
      const tri = queue.pop();
      triangles.push(tri);
      for (let corner = 0; corner < 3; corner += 1) {
        const vertexIndex = indices[tri * 3 + corner];
        vertices.add(vertexIndex);
        for (const neighbor of vertexToTriangles.get(vertexIndex) || []) {
          if (uvTriangleMaterialKey(materialLookup[neighbor]) !== startKey) continue;
          if (!visited[neighbor]) {
            visited[neighbor] = true;
            queue.push(neighbor);
          }
        }
      }
    }
    groups.push({ triangles, vertexCount: vertices.size });
  }
  groups.sort((a, b) => b.triangles.length - a.triangles.length);
  const categoryCounts = {};
  return groups.map((group, index) => {
    const materialInfo = summarizeUvGroupMaterial(group.triangles, materialLookup);
    categoryCounts[materialInfo.category] = (categoryCounts[materialInfo.category] || 0) + 1;
    const materialLabel = uvMaterialFilterLabel(materialInfo.category);
    return {
      id: `group-${index + 1}`,
      label: `${materialLabel} ${categoryCounts[materialInfo.category]}`,
      materialName: materialInfo.materialName,
      materialCategory: materialInfo.category,
      skinRelevant: materialInfo.skinRelevant,
      triangles: group.triangles,
      triangleCount: group.triangles.length,
      vertexCount: group.vertexCount,
    };
  });
}

function uvGroupFilterThreshold() {
  const raw = Number($("uvGroupMinTriangles")?.value ?? state.uvGroupMinTriangles ?? 100);
  const value = Number.isFinite(raw) ? Math.floor(raw) : 100;
  return Math.max(1, value);
}

function visibleUvGroups() {
  let groups = state.uvGroups || [];
  if (state.uvMaterialFilter && state.uvMaterialFilter !== "all") {
    groups = groups.filter((group) => group.materialCategory === state.uvMaterialFilter);
  }
  if (!state.uvMajorOnly) return groups;
  const threshold = uvGroupFilterThreshold();
  const majorGroups = groups.filter((group) => (group.triangleCount || 0) >= threshold);
  if (majorGroups.length || !state.uvMaterialFilter || state.uvMaterialFilter === "all") return majorGroups;
  return groups;
}

function selectedUvTriangles() {
  const groups = visibleUvGroups();
  const selected = state.uvSelectedGroups;
  if (!groups.length) return [];
  if (!selected.size) return [];
  return groups
    .filter((group) => selected.has(group.id))
    .flatMap((group) => group.triangles);
}

function uvPointToCanvas(uv, width, height) {
  const flipU = !!$("uvFlipU")?.checked;
  const flipV = !!$("uvFlipV")?.checked;
  const u = flipU ? 1 - uv[0] : uv[0];
  const v = flipV ? uv[1] : 1 - uv[1];
  return [u * width, v * height];
}

function ensureUvPickingCanvas(width, height) {
  if (!state.uvPickingCanvas) {
    state.uvPickingCanvas = document.createElement("canvas");
  }
  if (state.uvPickingCanvas.width !== width) state.uvPickingCanvas.width = width;
  if (state.uvPickingCanvas.height !== height) state.uvPickingCanvas.height = height;
  return state.uvPickingCanvas;
}

function colorForUvGroupIndex(index) {
  const value = index + 1;
  return [value & 255, (value >> 8) & 255, (value >> 16) & 255];
}

function groupIdFromPickColor(r, g, b) {
  const value = r | (g << 8) | (b << 16);
  if (!value) return null;
  return state.uvGroups[value - 1]?.id || null;
}

function stopUvBlink() {
  if (state.uvBlinkRaf) {
    window.cancelAnimationFrame(state.uvBlinkRaf);
    state.uvBlinkRaf = 0;
  }
  state.uvBlink = null;
}

function triggerUvBlink(groupId) {
  if (!groupId) return;
  stopUvBlink();
  state.uvBlink = {
    groupId,
    startedAt: performance.now(),
    cycleMs: 140,
    pulses: 4,
  };
  const tick = () => {
    if (!state.uvBlink) return;
    const elapsed = performance.now() - state.uvBlink.startedAt;
    const total = state.uvBlink.pulses * 2 * state.uvBlink.cycleMs;
    if (elapsed >= total) {
      stopUvBlink();
      renderUvCanvas();
      return;
    }
    renderUvCanvas();
    state.uvBlinkRaf = window.requestAnimationFrame(tick);
  };
  renderUvCanvas();
  state.uvBlinkRaf = window.requestAnimationFrame(tick);
}

function currentUvViewState() {
  return {
    groups: Array.from(state.uvSelectedGroups),
    showTexture: !!$("uvShowTexture")?.checked,
    outlineOnly: !!$("uvOutlineOnly")?.checked,
    flipU: !!$("uvFlipU")?.checked,
    flipV: !!$("uvFlipV")?.checked,
    majorOnly: !!$("uvMajorOnly")?.checked,
    minTriangles: uvGroupFilterThreshold(),
    exportOverlay: !!$("uvExportOverlay")?.checked,
  };
}

function applyUvViewState(viewState, options = {}) {
  const groups = new Set((viewState?.groups || []).filter((groupId) => state.uvGroups.some((group) => group.id === groupId)));
  state.uvMajorOnly = typeof viewState?.majorOnly === "boolean" ? !!viewState.majorOnly : state.uvMajorOnly;
  state.uvGroupMinTriangles = Number.isFinite(Number(viewState?.minTriangles)) ? Math.max(1, Math.floor(Number(viewState.minTriangles))) : state.uvGroupMinTriangles;
  if ($("uvMajorOnly")) $("uvMajorOnly").checked = !!state.uvMajorOnly;
  if ($("uvGroupMinTriangles")) $("uvGroupMinTriangles").value = String(state.uvGroupMinTriangles);
  state.uvSelectedGroups = groups.size ? groups : new Set(visibleUvGroups().map((group) => group.id));
  if ($("uvShowTexture") && typeof viewState?.showTexture === "boolean") $("uvShowTexture").checked = !!viewState.showTexture;
  if ($("uvOutlineOnly") && typeof viewState?.outlineOnly === "boolean") $("uvOutlineOnly").checked = !!viewState.outlineOnly;
  if ($("uvFlipU") && typeof viewState?.flipU === "boolean") $("uvFlipU").checked = !!viewState.flipU;
  if ($("uvFlipV") && typeof viewState?.flipV === "boolean") $("uvFlipV").checked = !!viewState.flipV;
  if ($("uvExportOverlay") && typeof viewState?.exportOverlay === "boolean") $("uvExportOverlay").checked = !!viewState.exportOverlay;
  renderUvGroupList();
  renderUvSummary(state.uvMeshData);
  renderUvCanvas();
  if (!options.skipPersist) scheduleSaveUvState();
}

function renderUvPresetSelect(stored) {
  const select = $("uvPresetSelect");
  if (!select) return;
  select.innerHTML = "";
  const placeholder = document.createElement("option");
  placeholder.value = "";
  placeholder.textContent = "-";
  select.append(placeholder);
  const presets = stored?.presets || {};
  Object.keys(presets).sort((a, b) => a.localeCompare(b)).forEach((name) => {
    const option = document.createElement("option");
    option.value = name;
    option.textContent = name;
    select.append(option);
  });
}

function applyStoredUvState(stored) {
  state.uvStored = stored || null;
  renderUvPresetSelect(state.uvStored);
  if (state.uvStored?.last_used) {
    applyUvViewState(state.uvStored.last_used, { skipPersist: true });
  } else {
    renderUvGroupList();
    renderUvCanvas();
  }
}

function setUvHoveredGroup(groupId) {
  if (state.uvHoveredGroupId === groupId) return;
  state.uvHoveredGroupId = groupId || null;
  renderUvGroupList();
  renderUvCanvas();
}

function setUvFocusedGroup(groupId) {
  state.uvFocusedGroupId = groupId || null;
  renderUvGroupList();
  renderUvCanvas();
  if (!groupId) return;
  const row = document.querySelector(`.uv-group-item[data-group-id="${groupId}"]`);
  row?.scrollIntoView({ block: "nearest" });
}

function scheduleSaveUvState() {
  window.clearTimeout(state.uvPersistTimer);
  if (!state.uvMeshData?.file) return;
  state.uvPersistTimer = window.setTimeout(async () => {
    const result = await call("save_uv_mesh_state", state.uvMeshData.file, $("rootPath").value.trim(), currentUvViewState());
    if (!result.ok) return;
    state.uvStored = result.data.stored || state.uvStored;
    renderUvPresetSelect(state.uvStored);
  }, 250);
}

function renderUvGroupList() {
  const wrap = $("uvGroupList");
  if (!wrap) return;
  wrap.innerHTML = "";
  const groups = visibleUvGroups();
  if (!groups.length) {
    const empty = document.createElement("div");
    empty.className = "uv-group-empty";
    empty.textContent = state.uvGroups.length ? "No groups match the current filter." : "Analyze a mesh to see groups.";
    wrap.append(empty);
    return;
  }
  groups.forEach((group) => {
    const label = document.createElement("div");
    label.className = "uv-group-item";
    label.dataset.groupId = group.id;
    label.classList.toggle("is-hovered", state.uvHoveredGroupId === group.id);
    label.classList.toggle("is-focused", state.uvFocusedGroupId === group.id);
    label.addEventListener("mouseenter", () => setUvHoveredGroup(group.id));
    label.addEventListener("mouseleave", () => setUvHoveredGroup(null));
    label.addEventListener("click", (event) => {
      if (event.target instanceof HTMLInputElement) return;
      setUvFocusedGroup(group.id);
    });
    const input = document.createElement("input");
    input.type = "checkbox";
    input.checked = state.uvSelectedGroups.has(group.id);
    input.dataset.groupId = group.id;
    input.addEventListener("change", () => {
      if (input.checked) {
        state.uvSelectedGroups.add(group.id);
        triggerUvBlink(group.id);
      } else {
        state.uvSelectedGroups.delete(group.id);
        if (state.uvBlink?.groupId === group.id) stopUvBlink();
      }
      renderUvCanvas();
      scheduleSaveUvState();
    });
    const text = document.createElement("span");
    const material = group.materialName && group.materialName !== group.label ? ` - ${group.materialName}` : "";
    text.textContent = `${group.label}${material} - ${group.triangleCount} Triangles / ${group.vertexCount} Vertices`;
    label.append(input, text);
    wrap.append(label);
  });
}

function selectAllUvGroups() {
  state.uvSelectedGroups = new Set(visibleUvGroups().map((group) => group.id));
  renderUvGroupList();
  renderUvCanvas();
  scheduleSaveUvState();
}

function clearUvGroups() {
  stopUvBlink();
  state.uvSelectedGroups = new Set();
  state.uvHoveredGroupId = null;
  state.uvFocusedGroupId = null;
  renderUvGroupList();
  renderUvCanvas();
  scheduleSaveUvState();
}

function selectLargestUvGroup() {
  const largest = visibleUvGroups()[0];
  state.uvSelectedGroups = largest ? new Set([largest.id]) : new Set();
  renderUvGroupList();
  renderUvCanvas();
  scheduleSaveUvState();
}

function selectUvSkinGroups() {
  setUvMaterialFilter("skin");
}

function selectUvGlassGroups() {
  setUvMaterialFilter("glass");
}

function setUvMaterialFilter(filter) {
  state.uvMaterialFilter = filter || "all";
  const groups = visibleUvGroups();
  state.uvSelectedGroups = new Set(groups.map((group) => group.id));
  updateUvMaterialFilterButtons();
  renderUvSummary(state.uvMeshData);
  renderUvGroupList();
  renderUvCanvas();
  scheduleSaveUvState();
}

function updateUvMaterialFilterButtons() {
  document.querySelectorAll("[data-uv-material-filter]").forEach((button) => {
    button.classList.toggle("active", button.dataset.uvMaterialFilter === state.uvMaterialFilter);
  });
}

function populateUvSlotSelect(data) {
  const select = $("uvSlotSelect");
  if (!select) return;
  select.innerHTML = "";
  const placeholder = document.createElement("option");
  placeholder.value = "";
  placeholder.textContent = "-";
  select.append(placeholder);
  (data?.texture_slots || []).forEach((slot, index) => {
    const option = document.createElement("option");
    option.value = String(index);
    const suffix = slot.preview_supported ? "" : " (Preview extern)";
    option.textContent = `${slot.name || "slot"} -> ${slot.path || slot.resolved_path || "-"}${suffix}`;
    select.append(option);
  });
}

function drawUvTriangles(ctx, width, height, triangles, options = {}) {
  ctx.beginPath();
  const mesh = state.uvMeshData;
  if (!mesh?.uvs?.length || !mesh?.indices?.length || !triangles?.length) return;
  const uvs = mesh.uvs;
  const indices = mesh.indices;
  const outlineOnly = options.outlineOnly ?? !!$("uvOutlineOnly")?.checked;
  if (outlineOnly) {
    const edgeCounts = new Map();
    triangles.forEach((tri) => {
      const base = tri * 3;
      const triIndices = [indices[base], indices[base + 1], indices[base + 2]];
      [[0, 1], [1, 2], [2, 0]].forEach(([ai, bi]) => {
        const a = triIndices[ai];
        const b = triIndices[bi];
        const key = a < b ? `${a}:${b}` : `${b}:${a}`;
        edgeCounts.set(key, (edgeCounts.get(key) || 0) + 1);
      });
    });
    edgeCounts.forEach((count, key) => {
      if (count !== 1) return;
      const [aIndex, bIndex] = key.split(":").map((value) => Number(value));
      const a = uvs[aIndex];
      const b = uvs[bIndex];
      if (!a || !b) return;
      const [ax, ay] = uvPointToCanvas(a, width, height);
      const [bx, by] = uvPointToCanvas(b, width, height);
      ctx.moveTo(ax, ay);
      ctx.lineTo(bx, by);
    });
  } else {
    triangles.forEach((tri) => {
      const base = tri * 3;
      const a = uvs[indices[base]];
      const b = uvs[indices[base + 1]];
      const c = uvs[indices[base + 2]];
      if (!a || !b || !c) return;
      const [ax, ay] = uvPointToCanvas(a, width, height);
      const [bx, by] = uvPointToCanvas(b, width, height);
      const [cx, cy] = uvPointToCanvas(c, width, height);
      ctx.moveTo(ax, ay);
      ctx.lineTo(bx, by);
      ctx.lineTo(cx, cy);
      ctx.lineTo(ax, ay);
    });
  }
}

function renderUvPickingCanvas(width, height) {
  const mesh = state.uvMeshData;
  const pickCanvas = ensureUvPickingCanvas(width, height);
  const pickCtx = pickCanvas.getContext("2d", { willReadFrequently: true });
  pickCtx.clearRect(0, 0, width, height);
  if (!mesh?.uvs?.length || !mesh?.indices?.length) return;
  state.uvGroups.forEach((group, index) => {
    if (!state.uvSelectedGroups.has(group.id)) return;
    const [r, g, b] = colorForUvGroupIndex(index);
    pickCtx.fillStyle = `rgb(${r}, ${g}, ${b})`;
    pickCtx.beginPath();
    group.triangles.forEach((tri) => {
      const base = tri * 3;
      const a = mesh.uvs[mesh.indices[base]];
      const bPt = mesh.uvs[mesh.indices[base + 1]];
      const c = mesh.uvs[mesh.indices[base + 2]];
      if (!a || !bPt || !c) return;
      const [ax, ay] = uvPointToCanvas(a, width, height);
      const [bx, by] = uvPointToCanvas(bPt, width, height);
      const [cx, cy] = uvPointToCanvas(c, width, height);
      pickCtx.moveTo(ax, ay);
      pickCtx.lineTo(bx, by);
      pickCtx.lineTo(cx, cy);
      pickCtx.closePath();
    });
    pickCtx.fill();
  });
}

function renderUvSceneToCanvas(canvas, width, height, options = {}) {
  if (!canvas) return;
  const ctx = canvas.getContext("2d");
  const image = state.uvImageData;
  const mesh = state.uvMeshData;
  if (canvas.width !== width) canvas.width = width;
  if (canvas.height !== height) canvas.height = height;
  ctx.clearRect(0, 0, width, height);

  if ((options.showTexture ?? !!$("uvShowTexture")?.checked) && image?.element) {
    ctx.drawImage(image.element, 0, 0, width, height);
  } else if (options.transparentBackground) {
    ctx.clearRect(0, 0, width, height);
  } else {
    ctx.fillStyle = "#10161d";
    ctx.fillRect(0, 0, width, height);
  }

  if (!mesh?.uvs?.length || !mesh?.indices?.length) return;
  const triangles = selectedUvTriangles();
  ctx.save();
  ctx.strokeStyle = "rgba(255, 64, 64, 0.85)";
  ctx.lineWidth = Math.max(1, Math.min(width, height) / 1024);
  drawUvTriangles(ctx, width, height, triangles, { outlineOnly: options.outlineOnly });
  ctx.stroke();
  if (state.uvFocusedGroupId) {
    const group = state.uvGroups.find((entry) => entry.id === state.uvFocusedGroupId);
    if (group && state.uvSelectedGroups.has(group.id)) {
      ctx.strokeStyle = "rgba(255, 184, 77, 0.95)";
      ctx.lineWidth = Math.max(2, Math.min(width, height) / 512);
      drawUvTriangles(ctx, width, height, group.triangles, { outlineOnly: true });
      ctx.stroke();
    }
  }
  if (state.uvHoveredGroupId) {
    const group = state.uvGroups.find((entry) => entry.id === state.uvHoveredGroupId);
    if (group && state.uvSelectedGroups.has(group.id)) {
      ctx.strokeStyle = "rgba(88, 166, 255, 0.98)";
      ctx.lineWidth = Math.max(2, Math.min(width, height) / 480);
      drawUvTriangles(ctx, width, height, group.triangles, { outlineOnly: true });
      ctx.stroke();
    }
  }
  if (state.uvBlink?.groupId) {
    const group = state.uvGroups.find((entry) => entry.id === state.uvBlink.groupId);
    if (group && state.uvSelectedGroups.has(group.id)) {
      const elapsed = performance.now() - state.uvBlink.startedAt;
      const phase = Math.floor(elapsed / state.uvBlink.cycleMs);
      const on = phase % 2 === 0;
      if (on) {
        const fade = 1 - Math.min(1, (elapsed % state.uvBlink.cycleMs) / state.uvBlink.cycleMs);
        ctx.strokeStyle = `rgba(74, 222, 128, ${0.25 + (0.75 * fade)})`;
        ctx.lineWidth = Math.max(3, Math.min(width, height) / 360);
        drawUvTriangles(ctx, width, height, group.triangles, { outlineOnly: true });
        ctx.stroke();
        ctx.strokeStyle = `rgba(34, 197, 94, ${0.12 + (0.35 * fade)})`;
        ctx.lineWidth = Math.max(5, Math.min(width, height) / 220);
        drawUvTriangles(ctx, width, height, group.triangles, { outlineOnly: true });
        ctx.stroke();
      }
    }
  }
  ctx.restore();
  if (options.includePicking !== false) {
    renderUvPickingCanvas(width, height);
  }
}

function exportUvRenderSize() {
  if (state.uvImageData?.width && state.uvImageData?.height) {
    return { width: state.uvImageData.width, height: state.uvImageData.height };
  }
  if ($("uvExportOverlay")?.checked) {
    return { width: 4096, height: 4096 };
  }
  const canvas = $("uvCanvas");
  return {
    width: canvas?.width || 1024,
    height: canvas?.height || 1024,
  };
}

function renderUvCanvas() {
  const canvas = $("uvCanvas");
  if (!canvas) return;
  const image = state.uvImageData;
  const width = image?.width || 1024;
  const height = image?.height || 1024;
  renderUvSceneToCanvas(canvas, width, height, { includePicking: true });
}

async function analyzeUvMesh() {
  const mesh = $("uvMeshPath").value.trim();
  if (!mesh) {
    log("Please select a .mesh file first", true);
    return;
  }
  const result = await call("inspect_uv_mesh", mesh, $("rootPath").value.trim());
  if (!result.ok) return;
  stopUvBlink();
  state.uvMeshData = result.data.result || null;
  state.uvGroups = buildUvGroups(state.uvMeshData);
  state.uvMaterialFilter = state.uvGroups.some((group) => group.skinRelevant || group.materialCategory === "skin") ? "skin" : "all";
  updateUvMaterialFilterButtons();
  state.uvMajorOnly = $("uvMajorOnly") ? $("uvMajorOnly").checked : state.uvMajorOnly;
  state.uvGroupMinTriangles = uvGroupFilterThreshold();
  state.uvSelectedGroups = new Set(visibleUvGroups().map((group) => group.id));
  state.uvStored = result.data.stored || null;
  state.uvHoveredGroupId = null;
  state.uvFocusedGroupId = null;
  renderUvSummary(state.uvMeshData);
  populateUvSlotSelect(state.uvMeshData);
  applyStoredUvState(state.uvStored);
  setInspectOutputText(result.data.summary || "");
  setFilePreviewText(result.data.summary || "");
  if (!$("uvExportPath").value.trim()) {
    await setConvertedDerivedPath("uvExportPath", "uv", mesh, ".png", true);
  }
}

async function loadUvTexture() {
  const path = $("uvTexturePath").value.trim();
  state.uvTexturePath = path;
  if (!path) {
    state.uvImageData = null;
    renderUvCanvas();
    return;
  }
  const result = await call("load_image_preview", path);
  if (!result.ok) return;
  const image = new Image();
  image.onload = () => {
    state.uvImageData = {
      width: result.data.width,
      height: result.data.height,
      dataUrl: result.data.data_url,
      element: image,
    };
    renderUvCanvas();
  };
  image.src = result.data.data_url;
}

async function useUvSlot() {
  const select = $("uvSlotSelect");
  const index = Number(select?.value || "");
  if (!Number.isFinite(index)) return;
  const slot = state.uvMeshData?.texture_slots?.[index];
  if (!slot) return;
  const chosenPath = slot.resolved_path || slot.path || "";
  if (chosenPath) {
    setPath("uvTexturePath", chosenPath);
    state.uvTexturePath = chosenPath;
  }
  if (!slot.preview_supported) {
    log("This material slot does not reference a directly previewable image format. For v1, please choose PNG/TGA/JPG as the background.", true);
    return;
  }
  await loadUvTexture();
}

async function exportUvTemplate() {
  const out = $("uvExportPath").value.trim();
  if (!out) {
    log("Please set a PNG output path first", true);
    return;
  }
  const offscreen = document.createElement("canvas");
  const size = exportUvRenderSize();
  const overlayExport = !!$("uvExportOverlay")?.checked;
  renderUvSceneToCanvas(offscreen, size.width, size.height, {
    includePicking: false,
    showTexture: overlayExport ? false : undefined,
    transparentBackground: overlayExport,
    outlineOnly: overlayExport ? true : undefined,
  });
  const result = await confirmFreeOutputIfInGameRoot({
    target: out,
    title: "Write UV output inside AC EVO installation folder",
    message: "The selected UV PNG target is inside the AC EVO installation folder. Continue?",
    writes: ["Write the UV template PNG."],
    confirmLabel: "Save",
    loadingLabel: "Saving...",
    onConfirm: () => call("save_uv_template", out, offscreen.toDataURL("image/png"), $("rootPath").value.trim()),
  });
  if (!result) return;
  if (!result.ok) return;
}

async function loadSelectedUvPreset() {
  const name = $("uvPresetSelect")?.value.trim() || "";
  if (!name) {
    log("Please select a UV preset first", true);
    return;
  }
  const preset = state.uvStored?.presets?.[name];
  if (!preset) {
    log(`UV preset not found: ${name}`, true);
    return;
  }
  applyUvViewState(preset);
}

async function saveNamedUvPreset() {
  const name = $("uvPresetName")?.value.trim() || "";
  if (!name) {
    log("Please enter a preset name first", true);
    return;
  }
  if (!state.uvMeshData?.file) {
    log("Please analyze a .mesh first", true);
    return;
  }
  const result = await call("save_uv_mesh_preset", state.uvMeshData.file, $("rootPath").value.trim(), name, currentUvViewState());
  if (!result.ok) return;
  state.uvStored = result.data.stored || state.uvStored;
  renderUvPresetSelect(state.uvStored);
  $("uvPresetSelect").value = name;
}

async function deleteSelectedUvPreset() {
  const name = $("uvPresetSelect")?.value.trim() || "";
  if (!name) {
    log("Please select a UV preset first", true);
    return;
  }
  const result = await showConfirmModal({
    type: "danger",
    title: "Delete UV preset",
    message: "Delete this UV preset?",
    details: name,
    confirmLabel: "Delete",
    loadingLabel: "Deleting...",
    onConfirm: () => call("delete_uv_mesh_preset", state.uvMeshData.file, $("rootPath").value.trim(), name),
  });
  if (!result) return;
  if (!result.ok) return;
  state.uvStored = result.data.stored || state.uvStored;
  renderUvPresetSelect(state.uvStored);
}

function uvGroupIdAtPointer(event) {
  const canvas = $("uvCanvas");
  const pickCanvas = state.uvPickingCanvas;
  if (!canvas || !pickCanvas) return null;
  const rect = canvas.getBoundingClientRect();
  if (!rect.width || !rect.height) return null;
  const x = Math.max(0, Math.min(pickCanvas.width - 1, Math.floor(((event.clientX - rect.left) / rect.width) * pickCanvas.width)));
  const y = Math.max(0, Math.min(pickCanvas.height - 1, Math.floor(((event.clientY - rect.top) / rect.height) * pickCanvas.height)));
  const pixel = pickCanvas.getContext("2d", { willReadFrequently: true }).getImageData(x, y, 1, 1).data;
  return groupIdFromPickColor(pixel[0], pixel[1], pixel[2]);
}

function renderMaterial(result) {
  const slots = result.data.result.texture_slots || [];
  const wrap = $("materialSlots");
  wrap.innerHTML = "";
  for (const slot of slots) {
    if (!slot.is_referenced && !slot.value) continue;
    const chip = document.createElement("div");
    chip.className = "chip";
    const size = slot.width ? `${slot.width}x${slot.height} / ${slot.mips} mips` : "";
    chip.innerHTML = `<strong>${escapeHtml(slot.name || "slot")}</strong><span>${escapeHtml(slot.suggested_format || "")} ${escapeHtml(size)}</span><span>${escapeHtml(slot.value || "")}</span>`;
    wrap.append(chip);
  }
  setInspectOutputText(result.data.summary);
}

function mySkinPlaceholderSrc() {
  return "./assets/placeholder.png";
}

function mySkinThumbnailVersion(skin = {}) {
  const mtime = Number(skin.thumbnail_mtime_ns || 0);
  const size = Number(skin.thumbnail_size || 0);
  return [mtime, size].filter(Boolean).join(":");
}

function mySkinThumbnailCacheKey(path, version = "") {
  const diskPath = String(path || "").trim();
  const suffix = String(version || "").trim();
  return suffix ? `${diskPath}::${suffix}` : diskPath;
}

async function loadMySkinThumbnail(path, version = "") {
  const diskPath = String(path || "").trim();
  const key = mySkinThumbnailCacheKey(diskPath, version);
  if (!diskPath) return null;
  if (state.mySkinThumbCache[key]) return state.mySkinThumbCache[key];
  if (state.mySkinThumbPending[key]) return state.mySkinThumbPending[key];
  state.mySkinThumbPending[key] = (async () => {
    try {
      let result = await window.pywebview.api.load_image_preview(diskPath);
      if ((!result?.ok || !result.data?.data_url) && diskPath.toLowerCase().endsWith(".texture")) {
        await new Promise((resolve) => window.setTimeout(resolve, 180));
        result = await window.pywebview.api.load_image_preview(diskPath);
      }
      if (!result?.ok || !result.data?.data_url) return null;
      state.mySkinThumbCache[key] = result.data.data_url;
      return result.data.data_url;
    } finally {
      delete state.mySkinThumbPending[key];
    }
  })();
  return state.mySkinThumbPending[key];
}

function clearMySkinThumbnailCache(path = "") {
  const key = String(path || "").trim();
  if (!key) {
    state.mySkinThumbCache = {};
    state.mySkinThumbPending = {};
    return;
  }
  Object.keys(state.mySkinThumbCache || {}).forEach((cacheKey) => {
    if (cacheKey === key || cacheKey.startsWith(`${key}::`)) delete state.mySkinThumbCache[cacheKey];
  });
  Object.keys(state.mySkinThumbPending || {}).forEach((cacheKey) => {
    if (cacheKey === key || cacheKey.startsWith(`${key}::`)) delete state.mySkinThumbPending[cacheKey];
  });
}

function queueMySkinThumbnail(img, path) {
  const target = img;
  const diskPath = String(path || "").trim();
  const version = String(target.dataset.skinThumbVersion || "").trim();
  const cacheKey = mySkinThumbnailCacheKey(diskPath, version);
  if (diskPath && target.dataset.skinThumbLoadedPath === cacheKey && target.src && !target.src.endsWith(mySkinPlaceholderSrc())) {
    return;
  }
  const requestId = `${Date.now()}-${Math.random()}`;
  target.dataset.skinThumbRequestId = requestId;
  target.dataset.skinThumbLoadedPath = "";
  target.src = mySkinPlaceholderSrc();
  target.classList.toggle("is-loading", Boolean(diskPath));
  target.parentElement?.classList.toggle("is-loading", Boolean(diskPath));
  if (!diskPath) return;
  loadMySkinThumbnail(diskPath, version).then((src) => {
    if (target.dataset.skinThumbRequestId !== requestId) return;
    if (src) {
      target.src = src;
      target.dataset.skinThumbLoadedPath = cacheKey;
    }
    return src;
  }).catch(() => {}).finally(() => {
    if (target.dataset.skinThumbRequestId !== requestId) return;
    target.classList.remove("is-loading");
    target.parentElement?.classList.remove("is-loading");
  });
}

function hydrateMySkinThumbnails(scope = document) {
  scope.querySelectorAll?.("img[data-skin-thumb-path]")?.forEach((img) => {
    queueMySkinThumbnail(img, img.dataset.skinThumbPath || "");
  });
}

function hydrateOfficialCreatorThumbnails(scope = document) {
  scope.querySelectorAll?.("img[data-official-car-thumb-path]")?.forEach((img) => {
    const target = img;
    const diskPath = String(target.dataset.officialCarThumbPath || "").trim();
    const cacheKey = mySkinThumbnailCacheKey(diskPath);
    if (diskPath && target.dataset.officialCarThumbLoadedPath === cacheKey && target.src && !target.src.endsWith(mySkinPlaceholderSrc())) {
      return;
    }
    const requestId = `${Date.now()}-${Math.random()}`;
    target.dataset.officialCarThumbRequestId = requestId;
    target.dataset.officialCarThumbLoadedPath = "";
    target.src = mySkinPlaceholderSrc();
    if (!diskPath) return;
    loadMySkinThumbnail(diskPath).then((src) => {
      if (target.dataset.officialCarThumbRequestId !== requestId) return;
      if (src) {
        target.src = src;
        target.dataset.officialCarThumbLoadedPath = cacheKey;
      }
    }).catch(() => {});
  });
}

function hydrateAdvancedCreatorThumbnails(scope = document) {
  scope.querySelectorAll?.("img[data-advanced-car-thumb-path]")?.forEach((img) => {
    const target = img;
    const diskPath = String(target.dataset.advancedCarThumbPath || "").trim();
    const cacheKey = mySkinThumbnailCacheKey(diskPath);
    if (diskPath && target.dataset.advancedCarThumbLoadedPath === cacheKey && target.src && !target.src.endsWith(mySkinPlaceholderSrc())) {
      return;
    }
    const requestId = `${Date.now()}-${Math.random()}`;
    target.dataset.advancedCarThumbRequestId = requestId;
    target.dataset.advancedCarThumbLoadedPath = "";
    target.src = mySkinPlaceholderSrc();
    if (!diskPath) return;
    loadMySkinThumbnail(diskPath).then((src) => {
      if (target.dataset.advancedCarThumbRequestId !== requestId) return;
      if (src) {
        target.src = src;
        target.dataset.advancedCarThumbLoadedPath = cacheKey;
      }
    }).catch(() => {});
  });
}

function hydrateMySkinsViewThumbnails(list, detail) {
  hydrateMySkinThumbnails(list);
  hydrateMySkinThumbnails(detail);
  const hydrateNextFrame = () => {
    hydrateMySkinThumbnails(list);
    hydrateMySkinThumbnails(detail);
  };
  if (typeof window.requestAnimationFrame === "function") {
    window.requestAnimationFrame(hydrateNextFrame);
  } else {
    window.setTimeout(hydrateNextFrame, 0);
  }
}

async function startMySkinThumbnailWatcher(skin = {}) {
  const skinKey = String(skin.key || "").trim();
  if (activeTab() !== "library") return;
  if (!skinKey || !window.pywebview?.api?.start_my_skin_thumbnail_watcher) return;
  if (state.mySkinThumbnailWatchSkinKey === skinKey && state.mySkinThumbnailWatchTimer) return;
  window.clearTimeout(state.mySkinThumbnailWatchTimer);
  state.mySkinThumbnailWatchTimer = 0;
  const result = await window.pywebview.api.start_my_skin_thumbnail_watcher({ skinKey });
  if (!result?.ok) return;
  state.mySkinThumbnailWatchSkinKey = skinKey;
  state.mySkinThumbnailWatchChangeId = Number(result.data?.change_id || 0);
  scheduleMySkinThumbnailWatcherPoll();
}

async function stopMySkinThumbnailWatcher() {
  window.clearTimeout(state.mySkinThumbnailWatchTimer);
  state.mySkinThumbnailWatchTimer = 0;
  state.mySkinThumbnailWatchSkinKey = "";
  state.mySkinThumbnailWatchChangeId = 0;
  if (window.pywebview?.api?.stop_my_skin_thumbnail_watcher) {
    await window.pywebview.api.stop_my_skin_thumbnail_watcher();
  }
}

function scheduleMySkinThumbnailWatcherPoll() {
  window.clearTimeout(state.mySkinThumbnailWatchTimer);
  state.mySkinThumbnailWatchTimer = 0;
  if (!state.mySkinThumbnailWatchSkinKey) return;
  if (activeTab() !== "library") return;
  const detail = $("mySkinsDetail");
  if (!detail) return;
  state.mySkinThumbnailWatchTimer = window.setTimeout(() => pollMySkinThumbnailWatcher(), 700);
}

async function pollMySkinThumbnailWatcher() {
  state.mySkinThumbnailWatchTimer = 0;
  try {
    const result = await window.pywebview.api.get_my_skin_thumbnail_watcher_status();
    if (!result?.ok) {
      scheduleMySkinThumbnailWatcherPoll();
      return;
    }
    const status = result.data || {};
    const changeId = Number(status.change_id || 0);
    if (changeId && changeId !== state.mySkinThumbnailWatchChangeId) {
      state.mySkinThumbnailWatchChangeId = changeId;
      const changedPaths = Array.isArray(status.last_paths) && status.last_paths.length ? status.last_paths : [status.path];
      const changedPath = String(changedPaths.filter(Boolean)[0] || "");
      if (changedPath) clearMySkinThumbnailCache(changedPath);
      const selected = (state.mySkins || []).find((row) => row.key === state.mySkinThumbnailWatchSkinKey);
      if (selected?.active !== false && window.pywebview?.api?.mark_my_skin_game_sync_required) {
        await window.pywebview.api.mark_my_skin_game_sync_required({
          skinKey: selected.key,
          reason: "Preview thumbnail changed",
          workflow: "preview_thumbnail",
        });
      }
      await loadMySkins();
      return;
    }
  } catch (error) {
    log(error?.message || String(error), true);
  }
  scheduleMySkinThumbnailWatcherPoll();
}

function mySkinRoadPlateWatchPath(skin = {}) {
  const plate = roadPlateInfo(skin);
  const policy = mySkinPolicy(skin);
  if (String(plate.source || "").toLowerCase() !== "custom") return "";
  if (policy.readOnly) return "";
  if (plate.projectPngEditable !== true) return "";
  if (plate.projectPngExists !== true) return "";
  return String(plate.projectPngPath || "").trim();
}

function fileSignatureKey(signature = {}) {
  if (!signature?.exists) return "missing";
  return `${Number(signature.size || 0)}:${Number(signature.mtimeNs || signature.mtime_ns || 0)}`;
}

async function startMySkinRoadPlateWatcher(skin = {}) {
  const skinKey = String(skin.key || "").trim();
  const path = mySkinRoadPlateWatchPath(skin);
  if (activeTab() !== "library") return;
  if (!skinKey || !path || !window.pywebview?.api?.get_file_signature) {
    stopMySkinRoadPlateWatcher();
    return;
  }
  if (
    state.mySkinRoadPlateWatchSkinKey === skinKey
    && state.mySkinRoadPlateWatchPath === path
    && state.mySkinRoadPlateWatchTimer
  ) {
    return;
  }
  window.clearTimeout(state.mySkinRoadPlateWatchTimer);
  state.mySkinRoadPlateWatchTimer = 0;
  const result = await window.pywebview.api.get_file_signature(path);
  if (!result?.ok) return;
  state.mySkinRoadPlateWatchSkinKey = skinKey;
  state.mySkinRoadPlateWatchPath = path;
  state.mySkinRoadPlateWatchSignature = fileSignatureKey(result.data || {});
  scheduleMySkinRoadPlateWatcherPoll();
}

function stopMySkinRoadPlateWatcher() {
  window.clearTimeout(state.mySkinRoadPlateWatchTimer);
  state.mySkinRoadPlateWatchTimer = 0;
  state.mySkinRoadPlateWatchSkinKey = "";
  state.mySkinRoadPlateWatchPath = "";
  state.mySkinRoadPlateWatchSignature = "";
}

function scheduleMySkinRoadPlateWatcherPoll() {
  window.clearTimeout(state.mySkinRoadPlateWatchTimer);
  state.mySkinRoadPlateWatchTimer = 0;
  if (!state.mySkinRoadPlateWatchSkinKey || !state.mySkinRoadPlateWatchPath) return;
  if (activeTab() !== "library") return;
  const detail = $("mySkinsDetail");
  if (!detail) return;
  state.mySkinRoadPlateWatchTimer = window.setTimeout(() => pollMySkinRoadPlateWatcher(), 3000);
}

async function pollMySkinRoadPlateWatcher() {
  state.mySkinRoadPlateWatchTimer = 0;
  try {
    const path = state.mySkinRoadPlateWatchPath;
    const skinKey = state.mySkinRoadPlateWatchSkinKey;
    if (!path || !skinKey) return;
    const result = await window.pywebview.api.get_file_signature(path);
    if (!result?.ok) {
      scheduleMySkinRoadPlateWatcherPoll();
      return;
    }
    const signature = fileSignatureKey(result.data || {});
    if (signature !== state.mySkinRoadPlateWatchSignature) {
      state.mySkinRoadPlateWatchSignature = signature;
      await loadMySkins();
      return;
    }
  } catch (error) {
    log(error?.message || String(error), true);
  }
  scheduleMySkinRoadPlateWatcherPoll();
}

function mySkinThumbnailPath(skin = {}) {
  return String(skin.project_thumbnail_disk_path || skin.effective_thumbnail_disk_path || skin.thumbnail_png_disk_path || "").trim();
}

function mySkinAceSnapshot(skin = {}) {
  const snapshot = skin.ace_snapshot;
  return snapshot && typeof snapshot === "object" ? snapshot : {};
}

function mySkinPreviewSourceLabel(skin = {}) {
  const source = String(skin.preview_source || "auto").toLowerCase();
  const path = mySkinThumbnailPath(skin);
  if (source === "ace" && path) return "Local thumbnail from AC EVO";
  if (source === "custom" && path) return "Local thumbnail";
  if (path) return "Local thumbnail";
  return "No preview";
}

function mySkinAceSnapshotHtml(skin = {}) {
  const ace = mySkinAceSnapshot(skin);
  const available = ace.available === true;
  const canUseThumbnail = mySkinCan(skin, "canEditPreviewImage");
  const officialExternal = isOfficialExternalSkin(skin);
  const useDisabled = !available || !canUseThumbnail;
  const statusClass = available ? "complete" : "missing";
  const statusText = available ? "AC EVO thumbnail available" : "No AC EVO thumbnail found";
  const detail = available
    ? [ace.saved_car_updated_at || "", ace.guid || ""].filter(Boolean).join(" / ")
    : String(ace.message || "Open and save this car in AC EVO to create a game snapshot.");
  const guidance = officialExternal
    ? "After activating this Official livery, open its existing garage car in AC EVO. Change at least one Car Customization option and press Apply/Save once so AC EVO creates the thumbnail. Return here and click Refresh; LiveryLab will keep checking briefly while EVO finishes writing the files."
    : "To use an AC EVO thumbnail, activate this livery in game, save it to your AC EVO garage, change at least one customization option and press Apply, then return to your livery in LiveryLab and click Refresh.";
  return `
    <div class="my-skin-ace-preview-state ${available ? "is-available" : "is-missing"}" data-tutorial-id="my-skins-ace-thumbnail">
      <div class="my-skin-ace-preview-copy">
        <span id="mySkinAceThumbnailStatus" class="status ${statusClass}">${escapeHtml(statusText)}</span>
        <strong>${escapeHtml(mySkinPreviewSourceLabel(skin))}</strong>
        <small>${escapeHtml(detail)}</small>
        <p>${escapeHtml(guidance)}</p>
      </div>
      <div class="my-skin-ace-preview-actions">
        <button id="refreshMySkinAceThumbnail" type="button" class="icon-text">${uiIcon("refresh")}Refresh</button>
        <button id="useMySkinAceThumbnail" type="button" class="icon-text"${useDisabled ? " disabled" : ""} aria-disabled="${useDisabled ? "true" : "false"}" title="${escapeHtml(available ? (canUseThumbnail ? "Use the detected AC EVO thumbnail" : "Preview editing is unavailable for this livery") : "Refresh after AC EVO has generated the garage thumbnail")}">${uiIcon("import")}<span>Use AC EVO Thumbnail</span></button>
      </div>
    </div>
  `;
}

function mySkinAceThumbnailPaths(skin = {}) {
  const ace = mySkinAceSnapshot(skin);
  const info = officialExternalInfo(skin);
  const generated = info.generatedSavedCar && typeof info.generatedSavedCar === "object" ? info.generatedSavedCar : {};
  const guid = String(ace.guid || skin.car_guid || info.linkedSavedCarGuid || generated.carGuid || "").trim().toLowerCase();
  const thumbnailRoot = String(ace.thumbnail_root || "").trim().replace(/[\\/]+$/, "");
  if (!guid || !thumbnailRoot || typeof window.pywebview?.api?.get_file_signature !== "function") {
    return { ready: false, reason: "Thumbnail GUID or Saved Games thumbnail folder is unavailable." };
  }
  const separator = thumbnailRoot.includes("\\") ? "\\" : "/";
  return {
    ready: true,
    texturePath: `${thumbnailRoot}${separator}${guid}.texture`,
    mipsPath: `${thumbnailRoot}${separator}${guid}.texturemips`,
  };
}

async function readMySkinAceThumbnailSignature(skin = {}) {
  const paths = mySkinAceThumbnailPaths(skin);
  if (!paths.ready) return paths;
  const [textureResult, mipsResult] = await Promise.all([
    window.pywebview.api.get_file_signature(paths.texturePath),
    window.pywebview.api.get_file_signature(paths.mipsPath),
  ]);
  if (!textureResult?.ok || !mipsResult?.ok) {
    return { ready: false, reason: textureResult?.message || mipsResult?.message || "Thumbnail file check failed." };
  }
  const texture = textureResult.data || {};
  const mips = mipsResult.data || {};
  const complete = texture.exists === true && mips.exists === true
    && texture.stable !== false && mips.stable !== false
    && Number(texture.size || 0) > 0 && Number(mips.size || 0) > 0
    && String(texture.signature || "").trim() && String(mips.signature || "").trim();
  return {
    ...paths,
    ready: Boolean(complete),
    signature: complete
      ? `${String(texture.signature).trim()}|${String(mips.signature).trim()}`
      : "",
    reason: complete ? "" : "Thumbnail files are incomplete or still being written.",
  };
}

function setMySkinAceThumbnailActionState(button, status, actionState, canUseThumbnail) {
  if (!button) return;
  button.classList.toggle("primary", actionState === "update");
  button.disabled = actionState === "checking" || actionState === "current" || !canUseThumbnail;
  button.setAttribute("aria-disabled", button.disabled ? "true" : "false");
  if (actionState === "update") {
    button.innerHTML = `${uiIcon("refresh")}<span>Update AC EVO Thumbnail</span>`;
    button.title = "A newer AC EVO garage thumbnail is available";
    if (status) {
      status.className = "status partial";
      status.textContent = "New AC EVO thumbnail available";
    }
  } else if (actionState === "current") {
    button.innerHTML = `${uiIcon("check")}<span>AC EVO Thumbnail Current</span>`;
    button.title = "The local preview already uses the current AC EVO thumbnail";
    if (status) {
      status.className = "status complete";
      status.textContent = "AC EVO thumbnail current";
    }
  } else if (actionState === "checking") {
    button.innerHTML = `<span class="button-spinner" aria-hidden="true"></span><span>Checking Thumbnail...</span>`;
    button.title = "Checking whether AC EVO has written a newer thumbnail";
  } else {
    button.innerHTML = `${uiIcon("import")}<span>Use AC EVO Thumbnail</span>`;
    button.title = canUseThumbnail ? "Use the detected AC EVO thumbnail" : "Preview editing is unavailable for this livery";
  }
}

async function hydrateMySkinAceThumbnailActionState(skin = {}) {
  const button = $("useMySkinAceThumbnail");
  const status = $("mySkinAceThumbnailStatus");
  const ace = mySkinAceSnapshot(skin);
  const canUseThumbnail = ace.available === true && mySkinCan(skin, "canEditPreviewImage");
  const appliedPreview = skin.ace_snapshot_preview && typeof skin.ace_snapshot_preview === "object"
    ? skin.ace_snapshot_preview
    : {};
  const appliedSignature = String(appliedPreview.source_signature || "").trim();
  if (!button || !canUseThumbnail || !appliedSignature) return;

  setMySkinAceThumbnailActionState(button, status, "checking", canUseThumbnail);
  let current;
  try {
    current = await readMySkinAceThumbnailSignature(skin);
  } catch (_error) {
    current = { ready: false };
  }
  if (!button.isConnected || state.mySkinSelectedKey !== skin.key) return;
  if (!current.ready) {
    setMySkinAceThumbnailActionState(button, status, "use", canUseThumbnail);
    return;
  }
  if (current.signature !== appliedSignature) {
    setMySkinAceThumbnailActionState(button, status, "update", canUseThumbnail);
    return;
  }
  const previewSource = String(skin.preview_source || "auto").trim().toLowerCase();
  setMySkinAceThumbnailActionState(button, status, previewSource === "ace" ? "current" : "use", canUseThumbnail);
}

async function waitForMySkinAceThumbnail(skin = {}, { timeoutMs = 8000, intervalMs = 750 } = {}) {
  const paths = mySkinAceThumbnailPaths(skin);
  if (!paths.ready) return paths;
  const deadline = Date.now() + Math.max(0, Number(timeoutMs) || 0);
  const appliedPreview = skin.ace_snapshot_preview && typeof skin.ace_snapshot_preview === "object"
    ? skin.ace_snapshot_preview
    : {};
  const appliedSignature = String(appliedPreview.source_signature || "").trim();
  let previousSignature = "";
  let stableChecks = 0;
  let newerContentSeen = !appliedSignature;
  do {
    const current = await readMySkinAceThumbnailSignature(skin);
    const signature = current.ready ? current.signature : "";
    if (signature && signature !== appliedSignature) newerContentSeen = true;
    if (signature && signature === previousSignature) stableChecks += 1;
    else stableChecks = 0;
    previousSignature = signature;
    if (newerContentSeen && stableChecks >= 1) return { ...paths, ready: true, signature };
    const remaining = deadline - Date.now();
    if (remaining <= 0) break;
    await delay(Math.min(Math.max(250, Number(intervalMs) || 750), remaining));
  } while (Date.now() <= deadline);
  return {
    ready: false,
    reason: appliedSignature && !newerContentSeen
      ? "AC EVO has not written a newer thumbnail yet."
      : "Thumbnail files were not ready before the refresh timeout.",
  };
}

function mySkinPolicy(skin = {}) {
  const policy = skin?.policy;
  if (policy && typeof policy === "object") return policy;
  const origin = String(skin.origin || skin.skin_origin || "").trim().toLowerCase();
  const projectState = String(skin.project_state || skin.projectState || "").trim().toLowerCase();
  const projectType = String(skin.project_type || skin.projectType || "advanced_extracted_content").trim().toLowerCase() || "advanced_extracted_content";
  const officialExternal = projectType === "official_external_livery";
  const officialExternalRole = officialExternalRoleForSkin(skin);
  const officialInfo = skin.official_external && typeof skin.official_external === "object" ? skin.official_external : {};
  const registrationState = String(
    skin.registration_state
    || skin.registrationState
    || officialInfo.registrationState
    || ""
  ).trim().toLowerCase();
  const unregisteredExternal = officialExternal && (skin.registered === false || registrationState === "unregistered");
  const derivedAdvanced = skin.derived_advanced && typeof skin.derived_advanced === "object" ? skin.derived_advanced : {};
  const lockedOfficialArtwork = String(derivedAdvanced.kind || "").trim().toLowerCase() === "official_package_baked"
    && derivedAdvanced.artworkLocked === true;
  const identityLocked = skin.identity_locked === true || skin.identityLocked === true
    || (skin.official_external && typeof skin.official_external === "object" && (skin.official_external.identityLocked === true || skin.official_external.identity_locked === true));
  const capabilities = skin.capabilities && typeof skin.capabilities === "object" ? skin.capabilities : {};
  const previewAssetsReady = capabilities.preview_assets_ready === true;
  const editable = origin === "created" || (!origin && String(skin.managed_by || "").toLowerCase() === "liverylab");
  const projectEditable = !projectState || projectState === "editable";
  const canEdit = editable && projectEditable && (!officialExternal || officialExternalRole === "project");
  const canUseEditablePreviewTools = officialExternal ? (canEdit && previewAssetsReady) : canEdit;
  const canUseAdvancedFeature = officialExternal ? false : canEdit;
  const result = {
    origin: origin || (editable ? "created" : "unknown"),
    projectState: projectState || (canEdit ? "editable" : "none"),
    projectType,
    officialExternalRole: officialExternal ? officialExternalRole : "",
    deploymentTarget: officialExternal ? String(skin.deployment_target || skin.deploymentTarget || "saved_games_external_liveries") : "",
    editable: canEdit,
    readOnly: !canEdit,
    readOnlyReason: canEdit ? "" : (officialExternal
      ? (origin === "package_import"
        ? "Installed read-only Official packages remain read-only."
        : "External installs are kept as read-only library copies.")
      : "Installed package skins are read-only."),
    identityLocked,
    canEditMetadata: canEdit && !identityLocked,
    canEditProjectMetadata: canEdit,
    canEditPreviewImage: !unregisteredExternal,
    canEditTyres: canUseAdvancedFeature,
    canEditDriver: canUseAdvancedFeature,
    canEditNumberplate: canUseAdvancedFeature,
    canEditMaterialFinish: officialExternal ? canEdit : canUseAdvancedFeature,
    canExportPackage: canEdit,
    canUseEditablePreviewTools,
    canUseReadOnlyPreview: true,
    registrationState: registrationState || (unregisteredExternal ? "unregistered" : "registered"),
    registered: !unregisteredExternal,
    canActivateInGame: officialExternal ? ["project", "library"].includes(officialExternalRole) : true,
    canDeactivateInGame: true,
    canRemove: true,
    canTrash: !officialExternal || officialExternalRole === "project",
    canDuplicateProject: canEdit,
    canCreateEditableProject: !officialExternal && !canEdit,
    canRegisterExternal: unregisteredExternal,
  };
  if (lockedOfficialArtwork && derivedAdvanced.allowAdvancedDependencies === true) {
    Object.assign(result, {
      readOnlyReason: "Baked Official artwork is locked. Advanced tyre, driver and numberplate add-ons remain editable.",
      canEditPreviewImage: true,
      canEditTyres: true,
      canEditDriver: true,
      canEditNumberplate: true,
      canCreateEditableProject: false,
    });
  }
  if (unregisteredExternal) {
    Object.assign(result, {
      editable: false,
      readOnly: true,
      readOnlyReason: "This ExternalLivery is detected in Saved Games but is not registered in LiveryLab yet.",
      canEditMetadata: false,
      canEditProjectMetadata: false,
      canEditPreviewImage: false,
      canEditTyres: false,
      canEditDriver: false,
      canEditNumberplate: false,
      canEditMaterialFinish: false,
      canExportPackage: false,
      canUseEditablePreviewTools: false,
      canUseReadOnlyPreview: false,
      canActivateInGame: false,
      canDeactivateInGame: false,
      canRemove: false,
      canTrash: false,
      canDuplicateProject: false,
      canCreateEditableProject: false,
    });
  }
  return result;
}

function mySkinCan(skin = {}, permission = "") {
  return mySkinPolicy(skin)[permission] === true;
}

function mySkinReadOnlyReason(skin = {}) {
  return String(mySkinPolicy(skin).readOnlyReason || "This installed livery is read-only.").trim();
}

function projectTypeLabel(value = "") {
  const key = String(value || "advanced_extracted_content").trim().toLowerCase();
  if (key === "official_external_livery") return "Official External Livery";
  return "Advanced / Extracted Content";
}

function normalizeWorkflowScope(value = "", fallback = "advanced") {
  const key = String(value || "").trim().toLowerCase().replace(/[-\s]+/g, "_");
  if (["advanced", "official", "all"].includes(key)) return key;
  if (["legacy", "liverylab", "advanced_extracted_content"].includes(key)) return "advanced";
  if (["official_external", "official_external_livery", "external_livery"].includes(key)) return "official";
  return fallback;
}

function workflowScopeLabel(scope = state.workflowScope) {
  const key = normalizeWorkflowScope(scope);
  if (key === "official") return "Official External Liveries";
  if (key === "all") return "All Liveries";
  return "Advanced LiveryLab";
}

function updateMySkinsScopeButtons() {
  const scope = normalizeWorkflowScope(state.workflowScope);
  document.querySelectorAll(".my-skins-scope-tabs").forEach((tabs) => {
    tabs.hidden = true;
  });
  document.querySelectorAll("[data-my-skins-scope]").forEach((button) => {
    const active = normalizeWorkflowScope(button.dataset.mySkinsScope) === scope;
    button.classList.toggle("active", active);
    button.setAttribute("aria-pressed", active ? "true" : "false");
  });
}

async function setMySkinsWorkflowScope(scope = "advanced") {
  const nextScope = normalizeWorkflowScope(scope);
  if (nextScope === state.workflowScope) {
    updateMySkinsScopeButtons();
    return;
  }
  state.workflowScope = nextScope;
  await setDiagnosticWorkflowScope(nextScope);
  state.mySkinSelectedKey = "";
  state.liveryCategorySearch = "";
  state.mySkinsLoaded = false;
  state.mySkinsLoadedScope = "";
  state.mySkins = [];
  clearMySkinThumbnailCache();
  updateMySkinsScopeButtons();
  await loadMySkins();
}

function isOfficialExternalSkin(skin = {}) {
  return mySkinPolicy(skin).projectType === "official_external_livery";
}

function isOfficialWorkflowReference(skin = {}) {
  const reference = skin?.workflow_reference;
  return Boolean(reference && typeof reference === "object"
    && normalizeWorkflowScope(reference.targetScope || reference.target_scope || "") === "official"
    && reference.passive === true);
}

function isInactiveMigratedAdvancedCopy(skin = {}) {
  const marker = skin?.legacy_workspace_migration;
  return skin?.active === false
    && marker && typeof marker === "object"
    && Boolean(String(marker.migrationId || "").trim())
    && mySkinPolicy(skin).projectType === "advanced_extracted_content";
}

function isUnregisteredOfficialExternalSkin(skin = {}) {
  if (!isOfficialExternalSkin(skin)) return false;
  const policy = mySkinPolicy(skin);
  return policy.registered === false || String(policy.registrationState || "").toLowerCase() === "unregistered";
}

function mySkinNeedsAdvancedRuntimeGuard(skin = {}) {
  return !isOfficialExternalSkin(skin);
}

function mySkinAdvancedRuntimeGuard(skin = {}) {
  if (!mySkinNeedsAdvancedRuntimeGuard(skin)) {
    return { ready: true, runtime: "", label: "", message: "", details: [] };
  }
  const profile = state.runtimeProfile && typeof state.runtimeProfile === "object" ? state.runtimeProfile : null;
  const runtime = String(profile?.runtime || "unknown").trim().toLowerCase();
  const label = profile ? runtimeProfileLabel(runtime) : "Runtime not checked";
  const ready = profile?.readyForAdvancedGameWrites === true;
  const blockers = profile && Array.isArray(profile.blockers) ? profile.blockers : [];
  const warnings = profile && Array.isArray(profile.warnings) ? profile.warnings : [];
  const details = [
    ...blockers.map((row) => `Blocker: ${row}`),
    ...warnings.map((row) => `Warning: ${row}`),
  ];
  const message = ready
    ? ""
    : "Advanced game writes require Advanced/extracted mode. Current runtime must be switched or cleaned up before Activate, Update or Deactivate in Game.";
  return { ready, runtime, label, message, details };
}

function mySkinOfficialRuntimeGuard(skin = {}) {
  if (!isOfficialExternalSkin(skin)) {
    return { ready: true, runtime: "", label: "", message: "", details: [] };
  }
  const profile = state.runtimeProfile && typeof state.runtimeProfile === "object" ? state.runtimeProfile : null;
  const runtime = String(profile?.runtime || "unknown").trim().toLowerCase();
  const label = profile ? runtimeProfileLabel(runtime) : "Runtime not checked";
  const ready = profile?.readyForOfficialGameWrites === true;
  const blockers = profile && Array.isArray(profile.blockers) ? profile.blockers : [];
  const warnings = profile && Array.isArray(profile.warnings) ? profile.warnings : [];
  const details = [
    ...blockers.map((row) => `Blocker: ${row}`),
    ...warnings.map((row) => `Warning: ${row}`),
  ];
  const message = ready
    ? ""
    : "Official game writes require active content.kspkg and reachable Saved Games folders. Loose extracted content/cars is allowed and only shown as a warning.";
  return { ready, runtime, label, message, details };
}

function mySkinRuntimeGuard(skin = {}) {
  return isOfficialExternalSkin(skin) ? mySkinOfficialRuntimeGuard(skin) : mySkinAdvancedRuntimeGuard(skin);
}

function mySkinAdvancedRuntimeNoticeHtml(skin = {}) {
  if (isUnregisteredOfficialExternalSkin(skin)) return "";
  const guard = mySkinRuntimeGuard(skin);
  if (guard.ready) return "";
  return `
    <div class="my-skin-sync-notice danger" data-tutorial-id="my-skins-runtime-guard">
      <strong>Runtime blocks game write</strong>
      <span>${escapeHtml(guard.message)} ${escapeHtml(guard.label ? `Current runtime: ${guard.label}.` : "")}</span>
      ${guard.details.length ? `
        <ul class="my-skin-sync-list">
          ${guard.details.slice(0, 6).map((row) => `<li>${escapeHtml(row)}</li>`).join("")}
        </ul>
      ` : ""}
    </div>
  `;
}

function officialExternalRoleForSkin(skin = {}) {
  const info = officialExternalInfo(skin);
  const raw = String(
    info.role
    || skin.official_external_role
    || skin.officialExternalRole
    || skin.storage_role
    || skin.storageRole
    || "library"
  ).trim().toLowerCase().replace(/[-\s]+/g, "_");
  if (["project", "editable", "editable_project", "livery_project"].includes(raw)) return "project";
  if (["deployment", "deployed", "saved_games", "saved_games_external_liveries", "game"].includes(raw)) return "deployment";
  return "library";
}

function officialExternalRoleLabel(role = "") {
  const key = String(role || "library").trim().toLowerCase();
  if (key === "project") return "Editable project";
  if (key === "deployment") return "Saved Games deployment";
  return "Read-only library copy";
}

function mySkinCapabilityRows(skin = {}) {
  const rows = Array.isArray(skin.capabilityRows) ? skin.capabilityRows : [];
  if (rows.length) return rows;
  const caps = skin.capabilities && typeof skin.capabilities === "object" ? skin.capabilities : {};
  const labels = {
    official_paths_ready: "Official folders",
    saved_cars_ready: "Saved cars",
    preview_assets_ready: "Preview assets",
    full_extracted_content_ready: "Full extracted content",
    advanced_tools_ready: "Advanced tools",
    official_folder_custom_assets_supported: "Official custom assets",
  };
  return Object.keys(labels).map((key) => ({
    key,
    label: labels[key],
    ready: caps[key] === true,
  }));
}

function mySkinPreviewAssetDetails(skin = {}) {
  const details = skin.capabilityDetails && typeof skin.capabilityDetails === "object" ? skin.capabilityDetails : {};
  const preview = details.previewAssets && typeof details.previewAssets === "object" ? details.previewAssets : {};
  const source = String(preview.source || "").trim();
  const sourceLabels = {
    official_livery_library: "Official local PreviewAssets",
    livery_library: "Legacy local PreviewAssets",
    game_root: "Extracted game content",
  };
  let path = "";
  if (source === "official_livery_library") {
    path = String(preview.officialLibrary?.root || preview.library?.root || "").trim();
  } else if (source === "livery_library") {
    path = String(preview.legacyLibrary?.root || preview.library?.root || "").trim();
  } else if (source === "game_root") {
    path = String(preview.gameRoot?.carContentPath || preview.gameRoot?.root || "").trim();
  } else {
    path = String(preview.officialLibrary?.root || preview.legacyLibrary?.root || preview.library?.root || "").trim();
  }
  return {
    ready: preview.ready === true,
    source,
    sourceLabel: sourceLabels[source] || "No preview asset source",
    carId: String(preview.carId || "").trim(),
    path,
  };
}

function mySkinCapabilityRowText(skin = {}, row = {}) {
  const label = row.label || row.key || "Capability";
  const stateText = row.ready === true ? "ready" : "missing";
  if (String(row.key || "") !== "preview_assets_ready") return `${label}: ${stateText}`;
  const preview = mySkinPreviewAssetDetails(skin);
  const parts = [preview.sourceLabel];
  if (preview.carId) parts.push(`car ${preview.carId}`);
  if (preview.path) parts.push(preview.path);
  return `${label}: ${stateText} - ${parts.join(" - ")}`;
}

function mySkinCapabilityStatusHtml(skin = {}) {
  const policy = mySkinPolicy(skin);
  const rows = mySkinCapabilityRows(skin);
  if (!rows.length) return "";
  const readyCount = rows.filter((row) => row.ready === true).length;
  return `
    <details class="my-skin-sync-notice" data-tutorial-id="my-skins-capabilities">
      <summary>
        <strong>Project workflow</strong>
        <span>${escapeHtml(projectTypeLabel(policy.projectType || skin.projectType || skin.project_type))} - ${readyCount}/${rows.length} capabilities ready</span>
      </summary>
      <ul class="my-skin-sync-list">
        ${rows.map((row) => `
          <li>${escapeHtml(mySkinCapabilityRowText(skin, row))}</li>
        `).join("")}
      </ul>
    </details>
  `;
}

function showMySkinReadOnlyBlocked(skin = {}, action = "This action") {
  showStatusModal({
    type: "warning",
    title: "Read-only livery",
    subtitle: "Action not available",
    message: `${action} is disabled for "${skin.display_name || skin.skin_id || "this livery"}".`,
    details: mySkinReadOnlyReason(skin),
  });
}

function mySkinGameSync(skin = {}) {
  const sync = skin?.game_sync;
  return sync && typeof sync === "object" ? sync : {};
}

function mySkinNeedsGameUpdate(skin = {}) {
  if (mySkinHasMissingCustomRearPlate(skin)) return false;
  if (mySkinHasMissingProjectAssetCritical(skin)) return false;
  return skin?.active !== false && mySkinGameSync(skin).needs_update === true;
}

function normalizeMySkinDetailTab(value = "") {
  const key = String(value || "").trim().toLowerCase();
  return key === "diagnostics" ? "diagnostics" : "overview";
}

function setMySkinDetailTab(value = "") {
  const next = normalizeMySkinDetailTab(value);
  if (state.mySkinDetailTab === next) return;
  state.mySkinDetailTab = next;
  renderMySkins();
}

function mySkinHasMissingCustomRearPlate(skin = {}) {
  return roadPlateSource(skin) === "custom"
    && roadPlateInfo(skin).projectPngExists === false
    && mySkinGameSyncChanges(skin).some((change) => /custom rear plate image missing/i.test(change));
}

function mySkinProjectAssetStatus(skin = {}) {
  const status = skin?.project_asset_status;
  return status && typeof status === "object" ? status : {};
}

function mySkinProjectAssetCritical(skin = {}) {
  const status = mySkinProjectAssetStatus(skin);
  const rows = Array.isArray(status.critical) ? status.critical : [];
  return rows.filter((row) => row && typeof row === "object");
}

function mySkinProjectAssetWarnings(skin = {}) {
  const status = mySkinProjectAssetStatus(skin);
  const rows = Array.isArray(status.warnings) ? status.warnings : [];
  return rows.filter((row) => row && typeof row === "object");
}

function mySkinHasMissingProjectAssetCritical(skin = {}) {
  return mySkinProjectAssetCritical(skin).length > 0;
}

function syncLiveryPreviewGameUpdateAction() {
  const button = $("liveryPreviewBack");
  if (!button) return;
  const skinKey = state.liveryPreviewSkinKey || "";
  const skin = (state.mySkins || []).find((row) => row.key === skinKey);
  const needsUpdate = Boolean(skinKey && skin && mySkinNeedsGameUpdate(skin));
  button.classList.toggle("my-skin-game-sync-pulse", needsUpdate);
  button.classList.toggle("primary", needsUpdate);
  button.title = needsUpdate ? "Return to My Liveries to update the game files." : "Back to My Liveries";
}

async function markLiveryPreviewGameUpdateRequired(reason, workflow) {
  const skinKey = state.liveryPreviewSkinKey || "";
  if (!skinKey || !window.pywebview?.api?.mark_my_skin_game_sync_required) return false;
  const result = await window.pywebview.api.mark_my_skin_game_sync_required({
    skinKey,
    reason: reason || "3D preview changed",
    workflow: workflow || "preview3d_change",
  });
  if (!result?.ok) {
    log(result?.message || "Could not mark game update requirement", true);
    return false;
  }
  state.mySkinSelectedKey = skinKey;
  await loadMySkins();
  syncLiveryPreviewGameUpdateAction();
  return true;
}

function mySkinGameSyncReason(skin = {}) {
  return mySkinGameSyncChanges(skin).join(", ");
}

function mySkinGameSyncChanges(skin = {}) {
  const sync = mySkinGameSync(skin);
  const changes = [];
  const seen = new Set();
  const seenLabels = new Set();
  const addChange = (reason, workflow = "") => {
    const label = String(reason || workflow || "").trim();
    if (!label) return;
    const key = `${String(workflow || "").trim().toLowerCase()}|${label.toLowerCase()}`;
    const labelKey = label.toLowerCase();
    if (seen.has(key) || seenLabels.has(labelKey)) return;
    seen.add(key);
    seenLabels.add(labelKey);
    changes.push(label);
  };
  if (Array.isArray(sync.changes)) {
    sync.changes.forEach((change) => {
      if (!change || typeof change !== "object") return;
      addChange(change.reason, change.workflow);
    });
  }
  addChange(sync.reason, sync.workflow);
  return changes;
}

function mySkinGameSyncBadgeHtml(skin = {}) {
  if (!mySkinNeedsGameUpdate(skin)) return "";
  return `<span class="status game-sync">${escapeHtml(t("game_update_needed"))}</span>`;
}

function officialExternalInfo(skin = {}) {
  const info = skin.official_external;
  return info && typeof info === "object" ? info : {};
}

function officialExternalStatusLabel(status = "") {
  const key = String(status || "").trim().toLowerCase();
  if (key === "matches_source") return "Synced";
  if (key === "local_differs_from_source") return "Saved Games differs";
  if (key === "source_missing") return "Saved Games missing";
  if (key === "local_missing") return "Local copy missing";
  if (key === "project_not_deployed") return "Project";
  return key ? key.replace(/_/g, " ") : "Official external";
}

function officialExternalBadgeRoleLabel(role = "") {
  const key = String(role || "library").trim().toLowerCase();
  if (key === "project") return "Official Project";
  if (key === "deployment") return "Official deployed";
  return "Official Library";
}

function mySkinOfficialExternalBadgeHtml(skin = {}) {
  if (!isOfficialExternalSkin(skin)) return "";
  const info = officialExternalInfo(skin);
  const status = String(info.deploymentStatus || "").trim().toLowerCase();
  const good = status === "matches_source";
  const missing = status === "source_missing" || status === "local_missing";
  const css = good ? "complete" : (missing ? "missing" : "game-sync");
  const role = officialExternalRoleForSkin(skin);
  return `<span class="status ${css}">${escapeHtml(`${officialExternalBadgeRoleLabel(role)}: ${officialExternalStatusLabel(status)}`)}</span>`;
}

function mySkinOfficialExternalNoticeHtml(skin = {}) {
  if (!isOfficialExternalSkin(skin)) return "";
  const info = officialExternalInfo(skin);
  const inspection = skin.official_external_inspection && typeof skin.official_external_inspection === "object"
    ? skin.official_external_inspection
    : {};
  const policy = mySkinPolicy(skin);
  const role = officialExternalRoleForSkin(skin);
  const status = String(info.deploymentStatus || "").trim().toLowerCase();
  const source = String(info.sourceFolder || "").trim();
  const local = String(skin.local_root || "").trim();
  const deploymentTarget = String(info.deploymentTarget || policy.deploymentTarget || skin.deployment_target || "").trim();
  const savedCarCount = Array.isArray(info.savedCarPaths) ? info.savedCarPaths.length : 0;
  const batches = Array.isArray(inspection.materialBatchNames) ? inspection.materialBatchNames : [];
  const slots = Array.isArray(inspection.textureSlots) ? inspection.textureSlots : [];
  const slotRows = slots.slice(0, 8).map((slot) => {
    const label = slot.label || slot.slot || "Texture";
    const path = slot.path || "";
    const exists = slot.exists === true;
    const referenced = slot.referenced === true;
    return { label, path, exists, referenced };
  });
  const staleCount = Number(inspection.staleGeneratedCount || 0);
  const missingRuntimeCount = Number(inspection.missingRuntimeCount || 0);
  return `
    <div class="my-skin-sync-notice" data-tutorial-id="my-skins-official-external">
      <strong>Official external livery</strong>
      <span>${escapeHtml(`${officialExternalRoleLabel(role)} - ${officialExternalStatusLabel(status)}`)}</span>
      <ul class="my-skin-sync-list">
        <li>Workflow role: ${escapeHtml(officialExternalRoleLabel(role))}</li>
        <li>Deployment target: ${escapeHtml(deploymentTarget || "saved_games_external_liveries")}</li>
        <li>${escapeHtml(role === "project" ? "Official project folder" : "Read-only library copy")}: ${escapeHtml(local || "-")}</li>
        <li>${escapeHtml(role === "project" ? "Saved Games deployment/source" : "Saved Games source")}: ${escapeHtml(source || "-")}</li>
        <li>Linked saved cars: ${escapeHtml(String(savedCarCount))}</li>
        <li>Source PNGs: ${escapeHtml(String(inspection.sourceTextureCount || 0))}</li>
        <li>Generated runtime files: ${escapeHtml(String((inspection.generatedTextureCount || 0) + (inspection.generatedTextureMipCount || 0)))}</li>
        <li>Generated files older than source PNGs: ${escapeHtml(String(staleCount))}</li>
        <li>Missing generated runtime pairs: ${escapeHtml(String(missingRuntimeCount))}</li>
        <li>Material batches: ${escapeHtml(batches.length ? batches.join(", ") : "-")}</li>
      </ul>
      ${slotRows.length ? `
        <details>
          <summary>Texture slots</summary>
          <ul class="my-skin-sync-list">
            ${slotRows.map((row) => `
              <li>
                ${escapeHtml(`${row.label}: ${row.path || "-"} (${row.exists ? "found" : "missing"}${row.referenced ? ", referenced" : ""})`)}
              </li>
            `).join("")}
            ${slots.length > slotRows.length ? `<li>${escapeHtml(`${slots.length - slotRows.length} more slot(s)`)}</li>` : ""}
          </ul>
        </details>
      ` : ""}
    </div>
  `;
}

function mySkinOfficialExternalDetailsHtml(skin = {}) {
  const body = mySkinOfficialExternalNoticeHtml(skin);
  if (!body) return "";
  return `
    <details class="my-skin-technical-details my-skin-official-details">
      <summary>Official External Details</summary>
      <div class="my-skin-technical-body">
        ${body}
      </div>
    </details>
  `;
}

function officialExternalDeploymentPreflight(skin = {}) {
  const camel = skin.deploymentPreflight;
  if (camel && typeof camel === "object") return camel;
  const snake = skin.deployment_preflight;
  return snake && typeof snake === "object" ? snake : {};
}

function mySkinProjectMetadata(skin = {}) {
  const ownMetadata = skin.metadata && typeof skin.metadata === "object" ? skin.metadata : {};
  const officialInfo = skin.official_external && typeof skin.official_external === "object" ? skin.official_external : {};
  const officialMetadata = officialInfo.metadata && typeof officialInfo.metadata === "object" ? officialInfo.metadata : {};
  return {
    author: String(ownMetadata.author || officialMetadata.author || skin.author || "").trim(),
    description: String(ownMetadata.description || officialMetadata.description || skin.description || "").trim().slice(0, LIVERY_DESCRIPTION_MAX_LENGTH).trim(),
  };
}

function mySkinProjectMetadataHtml(skin = {}) {
  const metadata = mySkinProjectMetadata(skin);
  if (!metadata.author && !metadata.description) return "";
  return `
    <div class="my-skin-project-metadata" data-tutorial-id="my-skins-project-metadata">
      ${metadata.author ? `<span><strong>Author</strong>${escapeHtml(metadata.author)}</span>` : ""}
      ${metadata.description ? `<span><strong>Description</strong>${escapeHtml(metadata.description)}</span>` : ""}
    </div>
  `;
}

function officialExternalLocalValidation(skin = {}) {
  const camel = skin.officialExternalValidation;
  if (camel && typeof camel === "object") return camel;
  const snake = skin.official_external_validation;
  if (snake && typeof snake === "object") return snake;
  const inspection = skin.official_external_inspection && typeof skin.official_external_inspection === "object"
    ? skin.official_external_inspection
    : {};
  const nested = inspection.localValidation;
  return nested && typeof nested === "object" ? nested : {};
}

function officialExternalHealthSeverityLabel(severity = "") {
  const key = String(severity || "").trim().toLowerCase();
  if (key === "error") return "Error";
  if (key === "warning") return "Warning";
  if (key === "ok") return "Ready";
  return "Info";
}

function officialExternalHealthPanelClass(status = "") {
  const key = String(status || "").trim().toLowerCase();
  if (key === "blocked") return "danger";
  if (key === "warning") return "warning";
  if (key === "ready") return "ready";
  return "idle";
}

function mySkinOfficialExternalHealthHtml(skin = {}) {
  if (!isOfficialExternalSkin(skin)) return "";
  const health = officialExternalLocalValidation(skin);
  const rows = Array.isArray(health.rows) ? health.rows.filter((row) => row && typeof row === "object") : [];
  const status = String(health.status || "").trim().toLowerCase();
  if (!rows.length) {
    return `
      <div class="my-skin-official-health is-idle" data-tutorial-id="my-skins-official-health">
        <div class="my-skin-official-health-head">
          <strong>Official local validation</strong>
          <span>Not checked yet</span>
        </div>
        <p>Run validation to check the local Official JSON, materials, PNG references and generated texture cache.</p>
      </div>
    `;
  }
  const counts = health.counts && typeof health.counts === "object" ? health.counts : {};
  const summaryParts = [
    Number(counts.error || 0) ? `${Number(counts.error || 0)} error(s)` : "",
    Number(counts.warning || 0) ? `${Number(counts.warning || 0)} warning(s)` : "",
    Number(counts.info || 0) ? `${Number(counts.info || 0)} info` : "",
  ].filter(Boolean);
  const panelClass = officialExternalHealthPanelClass(status);
  return `
    <div class="my-skin-official-health is-${escapeHtml(panelClass)}" data-tutorial-id="my-skins-official-health">
      <div class="my-skin-official-health-head">
        <strong>Official local validation</strong>
        <span>${escapeHtml(health.summary || officialExternalStatusLabel(status || "official external"))}${summaryParts.length ? ` - ${escapeHtml(summaryParts.join(", "))}` : ""}</span>
      </div>
      <div class="my-skin-official-health-grid">
        ${rows.map((row) => {
          const severity = String(row.severity || "info").toLowerCase();
          const rowClass = ["error", "warning", "ok"].includes(severity) ? severity : "info";
          const fix = String(row.fix || "").trim();
          return `
            <div class="my-skin-official-health-row is-${escapeHtml(rowClass)}">
              <span>${escapeHtml(officialExternalHealthSeverityLabel(rowClass))}</span>
              <div>
                <strong>${escapeHtml(row.title || row.key || "Check")}</strong>
                <p>${escapeHtml(row.message || "")}</p>
                ${fix ? `<em>${escapeHtml(fix)}</em>` : ""}
              </div>
            </div>
          `;
        }).join("")}
      </div>
    </div>
  `;
}

function officialExternalValidationInspectText(source = {}) {
  const data = source?.inspection ? source : { inspection: source.official_external_inspection || {}, validation: officialExternalLocalValidation(source), role: officialExternalRoleForSkin(source), localRoot: source.local_root || "" };
  const inspection = data.inspection && typeof data.inspection === "object" ? data.inspection : {};
  const health = data.validation && typeof data.validation === "object" ? data.validation : {};
  const healthRows = Array.isArray(health.rows) ? health.rows : [];
  const warnings = Array.isArray(inspection.warnings) ? inspection.warnings : [];
  const errors = Array.isArray(inspection.errors) ? inspection.errors : [];
  const batches = Array.isArray(inspection.materialBatchNames) ? inspection.materialBatchNames : [];
  const slots = Array.isArray(inspection.textureSlots) ? inspection.textureSlots : [];
  return [
    "Official local validation",
    "",
    `Status: ${health.status || "-"}`,
    `Health: ${health.summary || "-"}`,
    `Role: ${data.role || officialExternalRoleForSkin(source) || "-"}`,
    `Local root: ${data.localRoot || inspection.localRoot || source.local_root || "-"}`,
    `external_livery.json: ${inspection.externalLiveryJson || "-"}`,
    `car_guid: ${inspection.carGuid || source.car_guid || "-"}`,
    `Materials: ${batches.length ? batches.join(", ") : "-"}`,
    `Referenced texture slots: ${slots.filter((slot) => slot?.referenced).length}`,
    `Missing texture references: ${Number(inspection.missingTextureReferenceCount || 0)}`,
    `Generated runtime files: ${Number(inspection.generatedTextureCount || 0) + Number(inspection.generatedTextureMipCount || 0)}`,
    ...(healthRows.length ? ["", "Health checks:", ...healthRows.map((row) => `- ${officialExternalHealthSeverityLabel(row.severity)}: ${row.title || row.key} - ${row.message || ""}`)] : []),
    ...(errors.length ? ["", "Errors:", ...errors.map((row) => `- ${row}`)] : []),
    ...(warnings.length ? ["", "Warnings:", ...warnings.map((row) => `- ${row}`)] : []),
  ].join("\n");
}

function mySkinOfficialSavedCarLinkHtml(skin = {}) {
  if (!isOfficialExternalSkin(skin) || officialExternalRoleForSkin(skin) !== "project") return "";
  const info = officialExternalInfo(skin);
  const preflight = officialExternalDeploymentPreflight(skin);
  const savedCars = Array.isArray(preflight.savedCars) ? preflight.savedCars : [];
  const linked = info.linkedSavedCar && typeof info.linkedSavedCar === "object" ? info.linkedSavedCar : {};
  const paths = Array.isArray(info.savedCarPaths) ? info.savedCarPaths : [];
  const generated = info.generatedSavedCar && typeof info.generatedSavedCar === "object" ? info.generatedSavedCar : {};
  const deployment = preflight.savedCarDeployment && typeof preflight.savedCarDeployment === "object" ? preflight.savedCarDeployment : {};
  const guid = String(info.linkedSavedCarGuid || skin.car_guid || preflight.carGuid || "").trim();
  const found = savedCars.length > 0 || paths.length > 0 || linked.exists === true;
  const primaryPath = String(savedCars[0]?.path || linked.path || paths[0] || "").trim();
  const preparedPath = String(deployment.source || generated.path || generated.relativePath || "").trim();
  const automatic = String(info.savedCarSource || generated.sourceType || "").trim() === "liverylab_generated";
  const status = found
    ? "SavedCar installed in the active profile"
    : automatic && preparedPath
      ? "SavedCar prepared locally for first activation"
      : guid
        ? "GUID linked, SavedCar file not found"
        : "No SavedCar linked";
  return `
    <div class="my-skin-sync-notice" data-tutorial-id="my-skins-official-saved-car">
      <strong>${escapeHtml(automatic ? "Automatic SavedCar" : "Linked SavedCar")}</strong>
      <span>${escapeHtml(status)}</span>
      <ul class="my-skin-sync-list">
        <li>car_guid: ${escapeHtml(guid || "-")}</li>
        <li>Profile SavedCar: ${escapeHtml(primaryPath || "not installed yet")}</li>
        ${automatic ? `<li>Project source: ${escapeHtml(preparedPath || "-")}</li>` : ""}
      </ul>
    </div>
  `;
}

function officialSavedCarTemplateKey(carModel = "") {
  return String(carModel || "").trim().toLowerCase() || "unknown";
}

function officialSavedCarTemplateRecord(carModel = "") {
  const key = officialSavedCarTemplateKey(carModel);
  const records = state.officialSavedCarTemplateStatus && typeof state.officialSavedCarTemplateStatus === "object"
    ? state.officialSavedCarTemplateStatus
    : {};
  return records[key] && typeof records[key] === "object" ? records[key] : {};
}

function officialSavedCarTemplatePanelClass(record = {}) {
  if (record.loading) return "idle";
  if (record.error) return "danger";
  const data = record.data && typeof record.data === "object" ? record.data : {};
  if (!record.loaded) return "idle";
  if (Number(data.readyCount || 0) > 0) return "ready";
  const items = Array.isArray(data.items) ? data.items : [];
  if (items.some((item) => Array.isArray(item?.errors) && item.errors.length)) return "danger";
  if (items.length) return "warning";
  return "idle";
}

function officialSavedCarTemplateSummary(record = {}, carModel = "") {
  if (record.loading) return "Checking local templates...";
  if (record.error) return record.error;
  const data = record.data && typeof record.data === "object" ? record.data : {};
  if (!record.loaded) return "Not checked yet";
  if (Number(data.readyCount || 0) > 0) {
    return `${Number(data.readyCount || 0)} ready template(s), ${Number(data.cloneCandidateCount || 0)} clone candidate(s)`;
  }
  if (data.status === "missing") return "SavedCar template folder not found";
  if (data.status === "empty") return `No SavedCar template found for ${carModel || "this car"}`;
  return "No ready SavedCar template";
}

function mySkinSavedCarTemplateStatusHtml(skin = {}) {
  if (!isOfficialExternalSkin(skin)) return "";
  if (String(officialExternalInfo(skin).savedCarSource || "").trim() === "liverylab_generated") return "";
  const carModel = String(skin.car_model || skin.carModel || "").trim();
  const record = officialSavedCarTemplateRecord(carModel);
  const data = record.data && typeof record.data === "object" ? record.data : {};
  const items = Array.isArray(data.items) ? data.items : [];
  const readyItems = Array.isArray(data.readyItems) ? data.readyItems : items.filter((item) => item?.readyForLinking);
  const displayItems = (readyItems.length ? readyItems : items).slice(0, 4);
  const panelClass = officialSavedCarTemplatePanelClass(record);
  const summary = officialSavedCarTemplateSummary(record, carModel);
  const root = String(data.templatesRoot || "").trim();
  const expectedFolder = root && carModel ? `${root}\\${carModel}` : root;
  return `
    <div class="my-skin-official-health is-${escapeHtml(panelClass)}" data-tutorial-id="my-skins-saved-car-templates">
      <div class="my-skin-official-health-head">
        <strong>SavedCar template readiness</strong>
        <span>${escapeHtml(summary)}</span>
      </div>
      <div class="my-skin-official-health-grid">
        <div class="my-skin-official-health-row is-${Number(data.readyCount || 0) > 0 ? "ok" : (panelClass === "danger" ? "error" : "info")}">
          <span>${escapeHtml(Number(data.readyCount || 0) > 0 ? "Ready" : (panelClass === "danger" ? "Error" : "Info"))}</span>
          <div>
            <strong>${escapeHtml(carModel || "Unknown car")}</strong>
            <p>${escapeHtml(root ? `Template root: ${expectedFolder || root}` : "Template root is not available yet.")}</p>
          </div>
        </div>
        ${displayItems.map((item) => {
          const errors = Array.isArray(item.errors) ? item.errors : [];
          const warnings = Array.isArray(item.warnings) ? item.warnings : [];
          const rowClass = item.readyForLinking ? "ok" : (errors.length ? "error" : "warning");
          const label = item.sourceLabel || item.sourceType || item.name || "SavedCar template";
          return `
            <div class="my-skin-official-health-row is-${escapeHtml(rowClass)}">
              <span>${escapeHtml(item.readyForLinking ? "Ready" : (errors.length ? "Error" : "Check"))}</span>
              <div>
                <strong>${escapeHtml(label)}</strong>
                <p>${escapeHtml([item.name || "", item.carGuid ? `GUID ${item.carGuid}` : "", item.path || ""].filter(Boolean).join(" - "))}</p>
                ${errors.length ? `<em>${escapeHtml(errors[0])}</em>` : ""}
                ${!errors.length && warnings.length ? `<em>${escapeHtml(warnings[0])}</em>` : ""}
              </div>
            </div>
          `;
        }).join("")}
        ${items.length > displayItems.length ? `
          <div class="my-skin-official-health-row is-info">
            <span>Info</span>
            <div>
              <strong>${escapeHtml(`${items.length - displayItems.length} more template(s)`)}</strong>
              <p>Only the first templates are shown here.</p>
            </div>
          </div>
        ` : ""}
      </div>
    </div>
  `;
}

async function ensureOfficialSavedCarTemplatesLoaded(carModel = "") {
  const cleanCar = String(carModel || "").trim();
  if (!cleanCar || typeof window.pywebview?.api?.list_official_saved_car_templates !== "function") return false;
  const key = officialSavedCarTemplateKey(cleanCar);
  const current = officialSavedCarTemplateRecord(cleanCar);
  if (current.loading || current.loaded) return false;
  const requestId = ++state.officialSavedCarTemplateRequestId;
  state.officialSavedCarTemplateStatus = {
    ...(state.officialSavedCarTemplateStatus || {}),
    [key]: { loading: true, loaded: false, data: null, error: "", requestId },
  };
  renderMySkins();
  try {
    const result = await window.pywebview.api.list_official_saved_car_templates({ carModel: cleanCar });
    const nextRecord = result?.ok
      ? { loading: false, loaded: true, data: result.data || {}, error: "", requestId }
      : { loading: false, loaded: true, data: result?.data || {}, error: result?.message || "SavedCar template scan failed", requestId };
    state.officialSavedCarTemplateStatus = {
      ...(state.officialSavedCarTemplateStatus || {}),
      [key]: nextRecord,
    };
  } catch (error) {
    state.officialSavedCarTemplateStatus = {
      ...(state.officialSavedCarTemplateStatus || {}),
      [key]: { loading: false, loaded: true, data: null, error: error?.message || String(error), requestId },
    };
  }
  const selected = (state.mySkins || []).find((row) => row.key === state.mySkinSelectedKey) || {};
  if (officialSavedCarTemplateKey(selected.car_model || selected.carModel || "") === key) renderMySkins();
  return true;
}

function mySkinPolicyBadgeHtml(skin = {}) {
  return mySkinPolicy(skin).readOnly ? `<span class="status missing">Read-only</span>` : "";
}

function mySkinExternalInstallBadgeHtml(skin = {}) {
  const policy = mySkinPolicy(skin);
  return isOfficialExternalSkin(skin) && policy.origin === "official_external"
    ? `<span class="status">External install</span>`
    : "";
}

function mySkinExternalRegistrationBadgeHtml(skin = {}) {
  if (!isUnregisteredOfficialExternalSkin(skin)) return "";
  const blockers = Array.isArray(skin.registration_blockers)
    ? skin.registration_blockers
    : (Array.isArray(officialExternalInfo(skin).registrationBlockers) ? officialExternalInfo(skin).registrationBlockers : []);
  const title = blockers.length
    ? blockers.join(" ")
    : "Installed in AC EVO, but not registered in LiveryLab.";
  return `<span class="status game-sync" title="${escapeHtml(title)}">Not registered</span>`;
}

function mySkinDependencyCritical(skin = {}) {
  const dependencies = skin.activation_dependencies && typeof skin.activation_dependencies === "object"
    ? skin.activation_dependencies
    : {};
  const critical = Array.isArray(dependencies.critical) ? dependencies.critical : [];
  return critical.filter((row) => row && typeof row === "object");
}

function mySkinDependencyErrorBadgeHtml(skin = {}) {
  const critical = mySkinDependencyCritical(skin);
  if (!critical.length) return "";
  const firstMessage = String(critical[0]?.message || "Required livery assets are missing.").trim();
  return `<span class="status missing" title="${escapeHtml(firstMessage)}">Missing assets</span>`;
}

function mySkinProjectAssetBadgeHtml(skin = {}) {
  const critical = mySkinProjectAssetCritical(skin);
  if (!critical.length) return "";
  const firstMessage = String(critical[0]?.message || "Required LiveryProject files are missing.").trim();
  return `<span class="status missing" title="${escapeHtml(firstMessage)}">Project files missing</span>`;
}

function mySkinActiveBadgeHtml(skin = {}) {
  if (isUnregisteredOfficialExternalSkin(skin)) {
    return `<span class="status complete">Detected in Saved Games</span>`;
  }
  const isActive = skin.active !== false;
  return `<span class="status ${isActive ? "complete" : "missing"}">${escapeHtml(isActive ? t("active_in_game") : t("inactive_in_game"))}</span>`;
}

function liveryCategoryDefault() {
  return {
    id: "uncategorized",
    name: "Uncategorized",
    collapsed: Boolean(state.liveryDefaultCategoryCollapsed),
    sortOrder: 0,
  };
}

function liveryFavoritesCategory() {
  return {
    id: "favorites",
    name: "Favorites",
    collapsed: Boolean(state.liveryFavoritesCollapsed),
    sortOrder: -10,
    virtual: true,
  };
}

function liveryFavoriteKeySet() {
  return new Set((Array.isArray(state.liveryFavoriteSkinKeys) ? state.liveryFavoriteSkinKeys : [])
    .map((key) => String(key || "").trim().toLowerCase())
    .filter(Boolean));
}

function mySkinIsFavorite(skin = {}) {
  return liveryFavoriteKeySet().has(String(skin.key || "").trim().toLowerCase());
}

function normalizeLiveryCategorySortMode(value = "") {
  const key = String(value || "").trim().toLowerCase().replace(/[-\s]+/g, "_");
  if (["alphabetical", "alpha", "a_z", "az", "name"].includes(key)) return "alphabetical";
  if (["custom", "manual", "order"].includes(key)) return "custom";
  return "created";
}

function liveryCategorySortModeLabel(mode = state.liveryCategorySortMode) {
  const key = normalizeLiveryCategorySortMode(mode);
  if (key === "alphabetical") return "A-Z";
  if (key === "custom") return "Custom";
  return "Created";
}

function liveryCategoryScopePayload(extra = {}) {
  return {
    scope: normalizeWorkflowScope(state.workflowScope),
    ...extra,
  };
}

function applyLiveryCategoryState(data = {}, { fallback = false } = {}) {
  if (!data || typeof data !== "object") return;
  if (Array.isArray(data.categories)) state.liveryCategories = data.categories;
  else if (!fallback) state.liveryCategories = [];
  if (data.assignments && typeof data.assignments === "object") state.liveryCategoryAssignments = data.assignments;
  else if (!fallback) state.liveryCategoryAssignments = {};
  if (Array.isArray(data.favorites)) state.liveryFavoriteSkinKeys = data.favorites;
  else if (!fallback) state.liveryFavoriteSkinKeys = [];
  state.liveryCategorySortMode = normalizeLiveryCategorySortMode(data.sortMode || state.liveryCategorySortMode);
}

function normalizedLiveryCategories() {
  const rows = Array.isArray(state.liveryCategories) ? state.liveryCategories : [];
  const seen = new Set();
  const sortMode = normalizeLiveryCategorySortMode(state.liveryCategorySortMode);
  const categories = [];
  rows.forEach((row, index) => {
    if (!row || typeof row !== "object") return;
    const id = String(row.id || "").trim();
    if (!id || id === "uncategorized" || seen.has(id)) return;
    seen.add(id);
    categories.push({
      id,
      name: String(row.name || `Category ${index + 1}`).trim() || `Category ${index + 1}`,
      collapsed: Boolean(row.collapsed),
      sortOrder: Number(row.sortOrder ?? row.order ?? ((index + 1) * 10)) || ((index + 1) * 10),
      createdOrder: Number(row.createdOrder ?? row.created_order ?? row.sortOrder ?? row.order ?? ((index + 1) * 10)) || ((index + 1) * 10),
    });
  });
  categories.sort((a, b) => {
    if (sortMode === "alphabetical") return a.name.localeCompare(b.name) || a.id.localeCompare(b.id);
    if (sortMode === "created") return (a.createdOrder - b.createdOrder) || a.name.localeCompare(b.name) || a.id.localeCompare(b.id);
    return (a.sortOrder - b.sortOrder) || a.name.localeCompare(b.name) || a.id.localeCompare(b.id);
  });
  return categories;
}

function liveryCategoryOptionsHtml(selectedCategoryId = "uncategorized") {
  const selectedId = String(selectedCategoryId || "uncategorized");
  const options = [liveryCategoryDefault(), ...normalizedLiveryCategories()];
  return options.map((category) => `
    <option value="${escapeHtml(category.id)}"${category.id === selectedId ? " selected" : ""}>${escapeHtml(category.name)}</option>
  `).join("");
}

function liveryCategoryIdForSkin(skin = {}) {
  const key = String(skin.key || "").trim();
  const assignments = state.liveryCategoryAssignments && typeof state.liveryCategoryAssignments === "object"
    ? state.liveryCategoryAssignments
    : {};
  const categoryId = String(assignments[key] || "").trim();
  if (!categoryId) return "uncategorized";
  if (!normalizedLiveryCategories().some((category) => category.id === categoryId)) return "uncategorized";
  return categoryId;
}

function revealMySkinInCategory(skinKey = "") {
  const key = String(skinKey || "").trim();
  if (!key) return;
  const skin = (Array.isArray(state.mySkins) ? state.mySkins : []).find((row) => String(row.key || "") === key);
  if (!skin) return;
  state.mySkinSelectedKey = key;
  state.mySkinDetailTab = "overview";
  state.liveryCategorySearch = "";
  const categoryId = liveryCategoryIdForSkin(skin);
  if (categoryId === "favorites") {
    state.liveryFavoritesCollapsed = false;
    return;
  }
  if (categoryId === "uncategorized") {
    state.liveryDefaultCategoryCollapsed = false;
    return;
  }
  state.liveryCategories = (Array.isArray(state.liveryCategories) ? state.liveryCategories : []).map((category) => {
    if (String(category?.id || "") !== categoryId) return category;
    return { ...category, collapsed: false };
  });
}

function liveryCategoryMatchesSearch(skin = {}, query = "") {
  const q = String(query || "").trim().toLowerCase();
  if (!q) return true;
  return [
    skin.display_name,
    skin.skin_id,
    skin.car_model,
    skin.car_name,
    skin.carName,
    skin.key,
  ].some((value) => String(value || "").toLowerCase().includes(q));
}

function groupedMySkins(skins = [], query = "") {
  const managedSkins = skins.filter((skin) => !isOfficialWorkflowReference(skin));
  const officialReferences = skins.filter((skin) => isOfficialWorkflowReference(skin));
  const filteredSkins = managedSkins.filter((skin) => liveryCategoryMatchesSearch(skin, query));
  const filteredOfficialReferences = officialReferences.filter((skin) => liveryCategoryMatchesSearch(skin, query));
  const favorites = liveryFavoritesCategory();
  const normalCategories = [...normalizedLiveryCategories(), liveryCategoryDefault()];
  const favoriteGroup = {
    ...favorites,
    rows: filteredSkins.filter((skin) => mySkinIsFavorite(skin)),
    totalCount: managedSkins.filter((skin) => mySkinIsFavorite(skin)).length,
    collapsed: query ? false : Boolean(favorites.collapsed),
  };
  favoriteGroup.visibleCount = favoriteGroup.rows.length;
  const groups = normalCategories.map((category) => {
    const rows = filteredSkins.filter((skin) => liveryCategoryIdForSkin(skin) === category.id);
    const totalCount = managedSkins.filter((skin) => liveryCategoryIdForSkin(skin) === category.id).length;
    return {
      ...category,
      rows,
      totalCount,
      visibleCount: rows.length,
      collapsed: query ? false : Boolean(category.collapsed),
    };
  });
  const visibleGroups = [favoriteGroup, ...groups].filter((group) => group.visibleCount || (!query && group.id !== "uncategorized"));
  if (officialReferences.length && (!query || filteredOfficialReferences.length) && normalizeWorkflowScope(state.workflowScope) === "advanced") {
    visibleGroups.push({
      id: "official_references",
      name: "Official Liveries",
      rows: filteredOfficialReferences,
      totalCount: officialReferences.length,
      visibleCount: filteredOfficialReferences.length,
      collapsed: query ? false : Boolean(state.officialReferencesCollapsed),
      workflowReference: true,
    });
  }
  return visibleGroups;
}

function mySkinOfficialReferenceBadgeHtml(skin = {}) {
  return isOfficialWorkflowReference(skin) ? '<span class="status">Official Livery</span>' : "";
}

function mySkinCardHtml(skin = {}, mySkinsActionBusy = false) {
  const needsGameUpdate = mySkinNeedsGameUpdate(skin);
  const favorite = mySkinIsFavorite(skin);
  const officialReference = isOfficialWorkflowReference(skin);
  const classes = [
    "my-skin-card",
    skin.key === state.mySkinSelectedKey ? "active" : "",
    skin.active === false ? "inactive" : "",
    needsGameUpdate ? "needs-game-sync" : "",
  ].filter(Boolean).join(" ");
  return `
    <div class="${classes}" data-my-skin-key="${escapeHtml(skin.key || "")}" role="button" tabindex="${mySkinsActionBusy ? "-1" : "0"}"${mySkinsActionBusy ? " aria-disabled=\"true\"" : ""}>
      ${isUnregisteredOfficialExternalSkin(skin) || officialReference ? "" : `
      <span class="my-skin-favorite-toggle ${favorite ? "is-active" : ""}" role="button" tabindex="0" aria-label="${favorite ? "Remove favorite" : "Add favorite"}" data-my-skin-favorite-key="${escapeHtml(skin.key || "")}">${favorite ? "★" : "☆"}</span>
      `}
      <div class="my-skin-card-top">
        <span class="my-skin-thumb-frame">
          <img class="my-skin-thumb" data-skin-thumb-path="${escapeHtml(mySkinThumbnailPath(skin))}" data-skin-thumb-version="${escapeHtml(mySkinThumbnailVersion(skin))}" alt="" src="${mySkinPlaceholderSrc()}" />
        </span>
        <div class="my-skin-card-copy">
          <strong>${escapeHtml(skin.display_name || skin.skin_id || "Skin")}</strong>
          <span class="small">${escapeHtml(skin.car_model || "")}</span>
          <span class="small skin-id">${escapeHtml(skin.skin_id || "")}</span>
        </div>
      </div>
      <div class="my-skin-meta">
        ${officialReference ? mySkinOfficialReferenceBadgeHtml(skin) : `
          ${mySkinActiveBadgeHtml(skin)}
          ${mySkinDependencyErrorBadgeHtml(skin)}
          ${mySkinProjectAssetBadgeHtml(skin)}
          ${mySkinGameSyncBadgeHtml(skin)}
          ${devModeEnabled() ? mySkinOfficialExternalBadgeHtml(skin) : ""}
          ${mySkinExternalRegistrationBadgeHtml(skin)}
          ${mySkinExternalInstallBadgeHtml(skin)}
          ${mySkinPolicyBadgeHtml(skin)}
        `}
      </div>
    </div>
  `;
}

function renderMySkinsList(list, skins = []) {
  const query = String(state.liveryCategorySearch || "").trim();
  const groups = groupedMySkins(skins, query);
  const visibleCount = groups.reduce((sum, group) => sum + group.visibleCount, 0);
  const categories = normalizedLiveryCategories();
  const customCategoryIds = categories.map((category) => category.id);
  const sortMode = normalizeLiveryCategorySortMode(state.liveryCategorySortMode);
  const customSortActive = sortMode === "custom";
  const categoryCount = categories.length;
  const officialReferenceGroupCount = groups.some((group) => group.workflowReference) ? 1 : 0;
  const summary = query
    ? `${visibleCount} of ${skins.length} liveries`
    : `${skins.length} liveries / ${categoryCount + 2 + officialReferenceGroupCount} groups`;
  list.innerHTML = `
    <div class="my-skins-library-tools">
      <div class="my-skins-category-actions">
        <button id="addLiveryCategory" type="button" class="icon-text">${uiIcon("plus")}Add category</button>
        <button id="clearLiverySearch" type="button" class="icon-text"${query ? "" : " disabled"}>${uiIcon("x")}Clear search</button>
      </div>
      <div class="my-skins-category-sort" aria-label="Category sorting">
        <span>Sort categories</span>
        ${["created", "alphabetical", "custom"].map((mode) => `
          <button type="button" class="${sortMode === mode ? "active" : ""}" data-livery-category-sort-mode="${mode}" aria-pressed="${sortMode === mode ? "true" : "false"}">${escapeHtml(liveryCategorySortModeLabel(mode))}</button>
        `).join("")}
      </div>
      <div class="my-skins-category-add-row"${state.liveryCategoryAddOpen ? "" : " hidden"}>
        <label for="newLiveryCategoryName">Category name</label>
        <div class="my-skins-category-add-controls">
          <input id="newLiveryCategoryName" type="text" spellcheck="false" placeholder="Category name" value="${escapeHtml(state.liveryCategoryDraftName || "")}" />
          <button id="saveNewLiveryCategory" type="button" class="primary icon-text">${uiIcon("save")}Save</button>
        </div>
      </div>
      <div class="my-skins-category-search">
        <label for="liveryCategorySearch">Search liveries</label>
        <input id="liveryCategorySearch" type="search" spellcheck="false" placeholder="Search liveries..." value="${escapeHtml(state.liveryCategorySearch || "")}" />
        <span>${escapeHtml(summary)}</span>
      </div>
    </div>
    <div class="my-skins-category-list">
      ${groups.length ? groups.map((group) => `
        <section class="my-skins-category-group ${group.collapsed ? "is-collapsed" : ""}" data-livery-category-id="${escapeHtml(group.id)}">
          <div class="my-skins-category-head ${group.workflowReference ? "is-workflow-reference" : ""} ${!group.workflowReference && customSortActive && group.id !== "uncategorized" && group.id !== "favorites" ? "is-custom-sort" : ""}">
            <button class="my-skins-category-icon-button" type="button" data-livery-category-action="toggle" title="${group.collapsed ? "Expand category" : "Collapse category"}" aria-label="${group.collapsed ? "Expand category" : "Collapse category"}">${uiIcon(group.collapsed ? "plus" : "minus")}</button>
            ${state.liveryCategoryEditingId === group.id ? `
              <input class="my-skins-category-rename-input" type="text" spellcheck="false" value="${escapeHtml(state.liveryCategoryEditingName || group.name || "")}" data-livery-category-rename-input />
            ` : `<strong>${escapeHtml(`${group.name || group.id} [${query ? `${group.visibleCount}/${group.totalCount}` : group.totalCount}]`)}</strong>`}
            ${!group.workflowReference && customSortActive && group.id !== "uncategorized" && group.id !== "favorites" ? `
              <button class="my-skins-category-icon-button" type="button" data-livery-category-action="move-up" title="Move category up" aria-label="Move category up" ${customSortActive && customCategoryIds.indexOf(group.id) > 0 ? "" : "disabled"}>${uiIcon("up")}</button>
              <button class="my-skins-category-icon-button" type="button" data-livery-category-action="move-down" title="Move category down" aria-label="Move category down" ${customSortActive && customCategoryIds.indexOf(group.id) >= 0 && customCategoryIds.indexOf(group.id) < customCategoryIds.length - 1 ? "" : "disabled"}>${uiIcon("down")}</button>
            ` : ""}
            ${group.workflowReference ? "" : `
              <button class="my-skins-category-icon-button" type="button" data-livery-category-action="rename" title="Rename category" aria-label="Rename category" ${group.id === "uncategorized" || group.id === "favorites" ? "disabled" : ""}>${uiIcon("edit")}</button>
              <button class="my-skins-category-icon-button" type="button" data-livery-category-action="delete" title="Delete category" aria-label="Delete category" ${group.id === "uncategorized" || group.id === "favorites" ? "disabled" : ""}>${uiIcon("trash")}</button>
            `}
          </div>
          <div class="my-skins-category-body">
            ${group.rows.length
              ? group.rows.map((skin) => mySkinCardHtml(skin, state.mySkinsActionBusy === true)).join("")
              : `<div class="my-skins-empty small">No liveries in this category</div>`}
          </div>
        </section>
      `).join("") : `<div class="my-skins-empty">No liveries found for "${escapeHtml(query)}".</div>`}
    </div>
  `;
  wireMySkinsListEvents(list);
  syncMySkinsInlineInputFocus();
}

async function loadLiveryCategories() {
  if (!window.pywebview?.api?.get_livery_categories) return;
  const result = await window.pywebview.api.get_livery_categories(liveryCategoryScopePayload());
  if (!result?.ok) {
    log(result?.message || "Livery categories could not be loaded", true);
    return;
  }
  applyLiveryCategoryState(result.data);
}

async function saveNewLiveryCategory() {
  const cleanName = String($("newLiveryCategoryName")?.value || state.liveryCategoryDraftName || "").trim();
  if (!cleanName) return;
  const result = await call("create_livery_category", liveryCategoryScopePayload({ name: cleanName }));
  if (!result.ok) return;
  applyLiveryCategoryState(result.data, { fallback: true });
  state.liveryCategoryAddOpen = false;
  state.liveryCategoryDraftName = "";
  renderMySkins();
}

async function updateLiveryCategory(categoryId, patch = {}) {
  const result = await call("update_livery_category", liveryCategoryScopePayload({ categoryId, patch }));
  if (!result.ok) return false;
  applyLiveryCategoryState(result.data, { fallback: true });
  renderMySkins();
  return true;
}

async function updateLiveryCategorySettings(patch = {}) {
  const result = await call("update_livery_category_settings", liveryCategoryScopePayload({ patch }));
  if (!result.ok) return false;
  applyLiveryCategoryState(result.data, { fallback: true });
  renderMySkins();
  return true;
}

async function renameLiveryCategory(categoryId) {
  const category = normalizedLiveryCategories().find((row) => row.id === categoryId);
  if (!category) return;
  const cleanName = String(state.liveryCategoryEditingName || "").trim();
  if (!cleanName || cleanName === category.name) return;
  if (await updateLiveryCategory(categoryId, { name: cleanName })) {
    state.liveryCategoryEditingId = "";
    state.liveryCategoryEditingName = "";
  }
}

async function deleteLiveryCategory(categoryId) {
  const category = normalizedLiveryCategories().find((row) => row.id === categoryId);
  if (!category) return;
  const assignedCount = (state.mySkins || []).filter((skin) => liveryCategoryIdForSkin(skin) === categoryId).length;
  const confirmed = await showConfirmModal({
    title: "Delete category",
    message: `Delete "${category.name}"?`,
    details: assignedCount
      ? `${assignedCount} livery${assignedCount === 1 ? "" : "ies"} will move back to Uncategorized. No livery files are deleted.`
      : "No livery files are deleted.",
    confirmLabel: "Delete category",
    loadingLabel: "Deleting...",
    onConfirm: () => call("delete_livery_category", liveryCategoryScopePayload({ categoryId })),
  });
  if (!confirmed?.ok) return;
  applyLiveryCategoryState(confirmed.data, { fallback: true });
  renderMySkins();
}

async function assignSelectedLiveryCategory(selected = {}) {
  const categoryId = $("mySkinCategorySelect")?.value || "uncategorized";
  const result = await call("assign_livery_category", liveryCategoryScopePayload({ skinKey: selected.key, categoryId }));
  if (!result.ok) return;
  applyLiveryCategoryState(result.data, { fallback: true });
  renderMySkins();
}

async function setMySkinFavorite(skinKey, favorite) {
  const result = await call("set_livery_favorite", liveryCategoryScopePayload({ skinKey, favorite }));
  if (!result.ok) return;
  applyLiveryCategoryState(result.data, { fallback: true });
  renderMySkins();
}

function wireMySkinsListEvents(list) {
  $("liveryCategorySearch")?.addEventListener("input", (event) => {
    const cursor = Number(event.target.selectionStart ?? event.target.value.length);
    state.liveryCategorySearch = event.target.value || "";
    renderMySkins();
    window.requestAnimationFrame(() => {
      const search = $("liveryCategorySearch");
      if (!search) return;
      search.focus();
      search.setSelectionRange(cursor, cursor);
    });
  });
  $("clearLiverySearch")?.addEventListener("click", () => {
    state.liveryCategorySearch = "";
    renderMySkins();
  });
  list.querySelectorAll("[data-livery-category-sort-mode]").forEach((button) => {
    button.addEventListener("click", () => {
      const sortMode = normalizeLiveryCategorySortMode(button.dataset.liveryCategorySortMode || "");
      if (sortMode === normalizeLiveryCategorySortMode(state.liveryCategorySortMode)) return;
      updateLiveryCategorySettings({ sortMode });
    });
  });
  $("addLiveryCategory")?.addEventListener("click", () => {
    state.liveryCategoryAddOpen = true;
    renderMySkins();
  });
  $("newLiveryCategoryName")?.addEventListener("input", (event) => {
    state.liveryCategoryDraftName = event.target.value || "";
  });
  $("newLiveryCategoryName")?.addEventListener("keydown", (event) => {
    if (event.key === "Enter") {
      event.preventDefault();
      saveNewLiveryCategory();
    } else if (event.key === "Escape") {
      event.preventDefault();
      state.liveryCategoryAddOpen = false;
      state.liveryCategoryDraftName = "";
      renderMySkins();
    }
  });
  $("saveNewLiveryCategory")?.addEventListener("click", () => saveNewLiveryCategory());
  list.querySelectorAll("[data-livery-category-rename-input]").forEach((input) => {
    input.addEventListener("input", (event) => {
      state.liveryCategoryEditingName = event.target.value || "";
    });
    input.addEventListener("keydown", (event) => {
      if (event.key === "Enter") {
        event.preventDefault();
        renameLiveryCategory(state.liveryCategoryEditingId);
      } else if (event.key === "Escape") {
        event.preventDefault();
        state.liveryCategoryEditingId = "";
        state.liveryCategoryEditingName = "";
        renderMySkins();
      }
    });
    input.addEventListener("blur", () => {
      if (!state.liveryCategoryEditingId) return;
      state.liveryCategoryEditingId = "";
      state.liveryCategoryEditingName = "";
      renderMySkins();
    });
  });
  list.querySelectorAll("[data-my-skin-key]").forEach((button) => {
    button.addEventListener("click", () => {
      if (state.mySkinsActionBusy) return;
      const nextKey = button.dataset.mySkinKey || "";
      if (state.mySkinSelectedKey !== nextKey) state.mySkinDetailTab = "overview";
      state.mySkinSelectedKey = nextKey;
      renderMySkins();
    });
    button.addEventListener("keydown", (event) => {
      if (event.key !== "Enter" && event.key !== " ") return;
      if (state.mySkinsActionBusy) return;
      event.preventDefault();
      const nextKey = button.dataset.mySkinKey || "";
      if (state.mySkinSelectedKey !== nextKey) state.mySkinDetailTab = "overview";
      state.mySkinSelectedKey = nextKey;
      renderMySkins();
    });
  });
  list.querySelectorAll("[data-my-skin-favorite-key]").forEach((star) => {
    const toggle = () => {
      const skinKey = star.dataset.mySkinFavoriteKey || "";
      const skin = (state.mySkins || []).find((row) => row.key === skinKey) || {};
      setMySkinFavorite(skinKey, !mySkinIsFavorite(skin));
    };
    star.addEventListener("click", (event) => {
      event.preventDefault();
      event.stopPropagation();
      toggle();
    });
    star.addEventListener("keydown", (event) => {
      if (event.key !== "Enter" && event.key !== " ") return;
      event.preventDefault();
      event.stopPropagation();
      toggle();
    });
  });
  list.querySelectorAll("[data-livery-category-action]").forEach((button) => {
    button.addEventListener("click", () => {
      const group = button.closest("[data-livery-category-id]");
      const categoryId = group?.dataset?.liveryCategoryId || "";
      const action = button.dataset.liveryCategoryAction || "";
      if (!categoryId) return;
      if (action === "toggle") {
        if (categoryId === "official_references") {
          state.officialReferencesCollapsed = !state.officialReferencesCollapsed;
          renderMySkins();
        } else if (categoryId === "favorites") {
          state.liveryFavoritesCollapsed = !state.liveryFavoritesCollapsed;
          renderMySkins();
        } else if (categoryId === "uncategorized") {
          state.liveryDefaultCategoryCollapsed = !state.liveryDefaultCategoryCollapsed;
          renderMySkins();
        } else {
          const category = normalizedLiveryCategories().find((row) => row.id === categoryId);
          updateLiveryCategory(categoryId, { collapsed: !(category?.collapsed === true) });
        }
      } else if (action === "rename") {
        const category = normalizedLiveryCategories().find((row) => row.id === categoryId);
        if (!category) return;
        state.liveryCategoryEditingId = categoryId;
        state.liveryCategoryEditingName = category.name || "";
        renderMySkins();
      } else if (action === "delete") {
        deleteLiveryCategory(categoryId);
      } else if (action === "move-up" || action === "move-down") {
        updateLiveryCategory(categoryId, { move: action === "move-up" ? "up" : "down" });
      }
    });
  });
}

function syncMySkinsInlineInputFocus() {
  if (state.liveryCategoryEditingId) {
    const input = document.querySelector("[data-livery-category-rename-input]");
    if (input && document.activeElement !== input) {
      input.focus();
      input.select();
    }
    return;
  }
  if (state.liveryCategoryAddOpen) {
    const input = $("newLiveryCategoryName");
    if (input && document.activeElement !== input) {
      input.focus();
      input.setSelectionRange(input.value.length, input.value.length);
    }
    return;
  }
  const search = $("liveryCategorySearch");
  if (search && document.activeElement?.id === "liveryCategorySearch") {
    search.focus();
    search.setSelectionRange(search.value.length, search.value.length);
  }
}

function mySkinsLoadingHtml(label = "Loading My Liveries...") {
  return `
    <div class="my-skins-loading">
      <span class="button-spinner" aria-hidden="true"></span>
      <strong>${escapeHtml(label)}</strong>
    </div>
  `;
}

function renderMySkins() {
  updateMySkinsScopeButtons();
  const list = $("mySkinsList");
  const detail = $("mySkinsDetail");
  if (!list || !detail) return;
  list.innerHTML = "";
  const skins = state.mySkins || [];
  if (state.mySkinsLoading && !skins.length) {
    list.innerHTML = mySkinsLoadingHtml();
    detail.innerHTML = mySkinsLoadingHtml();
    stopMySkinThumbnailWatcher();
    stopMySkinRoadPlateWatcher();
    return;
  }
  if (!skins.length) {
    const scopeLabel = workflowScopeLabel();
    list.innerHTML = `<div class="my-skins-empty">${escapeHtml(`No ${scopeLabel} liveries registered yet.`)}</div>`;
    detail.innerHTML = `<div class="my-skins-empty">${escapeHtml(`No ${scopeLabel} liveries found in this workflow.`)}</div>`;
    stopMySkinThumbnailWatcher();
    stopMySkinRoadPlateWatcher();
    return;
  }
  if (!skins.some((row) => row.key === state.mySkinSelectedKey)) {
    state.mySkinSelectedKey = skins[0].key;
  }
  const mySkinsActionBusy = state.mySkinsActionBusy === true;
  renderMySkinsList(list, skins);
  const selected = skins.find((row) => row.key === state.mySkinSelectedKey) || skins[0];
  if (!selected) return;
  if (isOfficialWorkflowReference(selected)) {
    detail.innerHTML = `
      <div class="my-skin-detail-head" data-tutorial-id="my-skins-detail-head">
        <div>
          <h2>${escapeHtml(selected.display_name || selected.skin_id || "Official Livery")}</h2>
          <div class="my-skin-subtitle">${escapeHtml([selected.car_model || "", selected.skin_id || ""].filter(Boolean).join(" / "))}</div>
        </div>
        <div class="my-skin-badges">
          ${mySkinOfficialReferenceBadgeHtml(selected)}
        </div>
      </div>
      <div class="my-skin-sync-notice" data-tutorial-id="my-skins-official-workflow-reference">
        <strong>Managed in the Official workflow</strong>
        <span>This installed Official livery is shown in Advanced My Liveries for reference only. Restart LiveryLab and select Official to manage it.</span>
        <span>Importing an Official livery package while using Advanced creates a separate Advanced project. The source package and this installed Official livery remain unchanged.</span>
      </div>
    `;
    stopMySkinThumbnailWatcher();
    stopMySkinRoadPlateWatcher();
    hydrateMySkinsViewThumbnails(list, detail);
    return;
  }
  const selectedPolicy = mySkinPolicy(selected);
  const selectedReadOnly = selectedPolicy.readOnly === true;
  const selectedOfficialExternal = isOfficialExternalSkin(selected);
  const selectedOfficialUnregistered = isUnregisteredOfficialExternalSkin(selected);
  const selectedOfficialExternalRole = officialExternalRoleForSkin(selected);
  const selectedOfficialLibrary = selectedOfficialExternal && selectedOfficialExternalRole === "library";
  const selectedOfficialProject = selectedOfficialExternal && selectedOfficialExternalRole === "project";
  const canEditProjectMetadata = mySkinCan(selected, "canEditProjectMetadata");
  const showProjectMetadataEditor = canEditProjectMetadata && !selectedReadOnly;
  const canEditPreviewImage = mySkinCan(selected, "canEditPreviewImage");
  const canEditTyres = mySkinCan(selected, "canEditTyres");
  const canEditDriver = mySkinCan(selected, "canEditDriver");
  const canEditNumberplate = mySkinCan(selected, "canEditNumberplate");
  const canExportPackage = mySkinCan(selected, "canExportPackage");
  const canDuplicateProject = mySkinCan(selected, "canDuplicateProject");
  const canUseEditablePreviewTools = mySkinCan(selected, "canUseEditablePreviewTools");
  const canOpenAuthoringTools = !selectedOfficialLibrary && canUseEditablePreviewTools;
  const canOpen3dPreview = selectedOfficialUnregistered
    ? false
    : (selectedOfficialLibrary ? mySkinCan(selected, "canUseReadOnlyPreview") : true);
  const canRemoveOrTrash = mySkinCan(selected, "canRemove") || mySkinCan(selected, "canTrash");
  const canRefreshOfficialManifest = selectedOfficialProject
    && mySkinCan(selected, "canActivateInGame");
  const canValidateOfficialExternal = selectedOfficialExternal && !selectedOfficialUnregistered;
  const canRelinkOfficialSavedCar = canRefreshOfficialManifest;
  const registrationBlockers = Array.isArray(selected.registration_blockers)
    ? selected.registration_blockers
    : (Array.isArray(officialExternalInfo(selected).registrationBlockers) ? officialExternalInfo(selected).registrationBlockers : []);
  const registrationReady = selected.registration_ready === true
    || officialExternalInfo(selected).registrationReady === true;
  const canRegisterOfficialExternal = selectedOfficialUnregistered
    && mySkinCan(selected, "canRegisterExternal")
    && registrationReady;
  const mySkinsDetailBusy = state.mySkinsDetailBusy === true;
  const mySkinsDetailBusyButtonId = String(state.mySkinsDetailBusyButtonId || "");
  const mySkinsDetailBusyLabel = String(state.mySkinsDetailBusyLabel || "Working...");
  const detailBusyFor = (buttonId) => mySkinsDetailBusy && mySkinsDetailBusyButtonId === buttonId;
  const disabledAttr = (enabled, buttonId = "") => {
    const busy = buttonId && detailBusyFor(buttonId);
    return [
      enabled && !mySkinsActionBusy && !mySkinsDetailBusy ? "" : " disabled",
      busy ? " aria-busy=\"true\"" : "",
    ].join("");
  };
  const detailBusyClass = (buttonId) => detailBusyFor(buttonId) ? " is-working" : "";
  const detailBusyContent = (buttonId, idleHtml) => detailBusyFor(buttonId)
    ? `<span class="button-spinner" aria-hidden="true"></span><span>${escapeHtml(mySkinsDetailBusyLabel)}</span>`
    : idleHtml;
  const files = selected.files_list || [];
  const locFiles = selected.loc_files || [];
  const numberplate = selected.numberplate_variant || {};
  const numberplateSelector = numberplate.selector ? JSON.stringify(numberplate.selector) : "-";
  const numberplateBake = selected.numberplate_bake && typeof selected.numberplate_bake === "object" ? selected.numberplate_bake : {};
  const numberplateSupport = normalizeNumberplateSupport(selected);
  const numberplateUnsupported = numberplateSupport.supported === false;
  const numberplateUnsupportedMessage = numberplateSupport.message || "This car does not support numberplates yet";
  const numberplateSource = numberplateVariantSource(numberplate);
  const roadPlate = roadPlateInfo(selected);
  const isRoadPlateSkin = roadPlateSupported(selected);
  const currentRoadPlateSource = roadPlateSource(selected);
  const roadPlateProjectPngExists = roadPlate.projectPngExists === true;
  const roadPlateProjectPngPath = String(roadPlate.projectPngPath || roadPlate.projectPng || "plate_r.png").trim();
  const roadPlatePngLocation = String(roadPlate.projectPngLocation || "").trim().toLowerCase();
  const roadPlateProjectPngEditable = roadPlate.projectPngEditable === true || roadPlatePngLocation === "project";
  const roadPlateImportedAssets = selectedReadOnly && !roadPlateProjectPngEditable;
  const roadPlateGeneratedAssets = roadPlate.generatedAssets && typeof roadPlate.generatedAssets === "object" ? roadPlate.generatedAssets : {};
  const roadPlateImportedAssetsComplete = roadPlateGeneratedAssets.coreComplete === true;
  const numberplateEditorSource = isRoadPlateSkin
    ? (currentRoadPlateSource === "custom" ? "road_custom" : "road_default")
    : (numberplateUnsupported || numberplateSource === "none" ? "none" : "acevo");
  const numberplateSourceOptionsHtml = isRoadPlateSkin
    ? `
      <option value="road_default"${numberplateEditorSource === "road_default" ? " selected" : ""}>AC EVO default rear plate</option>
      <option value="road_custom"${numberplateEditorSource === "road_custom" ? " selected" : ""}>Custom rear plate</option>
    `
    : `
      <option value="acevo"${numberplateEditorSource === "acevo" ? " selected" : ""}${numberplateUnsupported ? " disabled" : ""}>AC EVO default numberplate</option>
      <option value="none"${numberplateEditorSource === "none" ? " selected" : ""}>No numberplate</option>
    `;
  const roadPlateCustomHint = roadPlateProjectPngExists
    ? (roadPlateImportedAssets
      ? `This imported skin includes a custom rear plate. Activate or Update in Game to apply that plate in AC EVO.`
      : `Custom rear plate is stored as plate_r.png in the project folder. Edit or replace that PNG, then use Update in Game to apply the changed plate in AC EVO.`)
    : (roadPlateImportedAssets
      ? (roadPlateImportedAssetsComplete
        ? `This imported skin includes custom rear plate assets. Activate or Update in Game to apply them in AC EVO.`
        : `The imported custom rear plate assets are incomplete.`)
      : `Save this setting to create plate_r.png in the project folder. You can then edit or replace it and use Update in Game to apply changes.`);
  const numberplateTemplateId = String(numberplate.template_id || numberplateBake.template_id || "").trim();
  const numberplateTemplateAvailable = !numberplateTemplateId || Boolean(numberplateStudioTemplateById(numberplateTemplateId)) || numberplateBake.template_available === true;
  const numberplateBakedOnly = Boolean(numberplateBake.surfaces && numberplateTemplateId && !numberplateTemplateAvailable);
  const numberplateTemplateLocked = numberplateSource === "template" && numberplateBakedOnly;
  const numberplateStartNumber = String(numberplate.start_number || numberplateBake.start_number || selected.start_number || "").replace(/\D/g, "").slice(0, 3);
  const numberplateModeRaw = String(numberplate.mode || "").toLowerCase();
  const numberplateMode = numberplateModeRaw === "surface" || numberplateModeRaw === "surfaces" || numberplateModeRaw === "per_surface" || numberplateModeRaw === "split"
    ? "surface"
    : "same";
  const numberplateFontPath = numberplate.font_path || numberplate.fontPath || "";
  const numberplateBackgroundPath = numberplate.background_path || numberplate.backgroundPath || "";
  const numberplateHoodBackgroundPath = numberplate.hood_background_path || numberplate.hoodBackgroundPath || "";
  const numberplateLeftBackgroundPath = numberplate.left_background_path || numberplate.leftBackgroundPath || "";
  const numberplateRightBackgroundPath = numberplate.right_background_path || numberplate.rightBackgroundPath || "";
  const availableNumberplateSurfaces = Array.isArray(numberplate.available_numberplate_surfaces) ? numberplate.available_numberplate_surfaces.map((value) => Number(value)) : [];
  const selectedBySurface = numberplate.selected_by_surface && typeof numberplate.selected_by_surface === "object" ? numberplate.selected_by_surface : {};
  const hasHoodNumberplate = numberplate.has_hood_numberplate === true
    || availableNumberplateSurfaces.includes(1)
    || Object.prototype.hasOwnProperty.call(selectedBySurface, "1")
    || Object.prototype.hasOwnProperty.call(selectedBySurface, 1)
    || numberplate.hood_numberplate_index != null;
  const numberplateIndex = numberplateMode !== "same"
    ? ((numberplate.numberplate_indices_1based || []).join(" / ") || "-")
    : (numberplate.numberplate_index_1based || "-");
  const tyreBrand = selected.tyre_brand && typeof selected.tyre_brand === "object" ? selected.tyre_brand : {};
  const tyreSelection = String(tyreBrand.selection || "default").toLowerCase();
  const tyreDefaultLabel = tyreBrand.defaultLabel ? `Default (${tyreBrand.defaultLabel})` : "Default";
  const tyreOptions = Array.isArray(tyreBrand.availableOptions) ? tyreBrand.availableOptions : [];
  const tyreSupported = tyreBrand.supported === true && tyreOptions.length > 1;
  const tyreFamily = String(tyreBrand.family || "").trim();
  const tyreSupportMessage = String(tyreBrand.supportMessage || "Tyre brand selection is not supported for this tyre type.").trim();
  const tyreOptionValues = new Set(tyreOptions.map((option) => String(option.value || "").toLowerCase()).filter(Boolean));
  const tyreSelectionKnown = tyreSelection === "default" || tyreOptionValues.has(tyreSelection);
  const tyreCurrentLabel = tyreSelection === "default" ? tyreDefaultLabel : (tyreBrand.label || tyreSelection || "-");
  const tyreOptionsHtml = tyreOptions.map((option) => {
    const value = String(option.value || "").toLowerCase();
    const label = String(option.label || option.brand || option.name || option.value || "").trim() || value;
    return `<option value="${escapeHtml(value)}"${tyreSelection === value ? " selected" : ""}>${escapeHtml(label)}</option>`;
  }).join("");
  const driverMeta = skinDriverMeta(selected);
  const driverCarSupported = isDriverProfileCarSupported(selected.car_model);
  const driverSupported = driverCarSupported && Boolean(driverMeta);
  const selectedDriverProfile = driverProfileSelection(selected);
  const selectedDriverProfileId = String(selectedDriverProfile.profileId || selectedDriverProfile.id || "").trim();
  const driverProfilesLoading = driverSupported && state.driverProfilesLoadingCars?.has(driverCarKey(selected.car_model));
  const driverProfilesLoaded = driverSupported && state.driverProfilesLoadedCars?.has(driverCarKey(selected.car_model));
  const carDriverProfiles = driverProfilesForCar(selected.car_model);
  const selectedDriverProfileKnown = !selectedDriverProfileId || carDriverProfiles.some((profile) => String(profile.id || "") === selectedDriverProfileId);
  const driverOptionsHtml = carDriverProfiles.map((profile) => {
    const id = String(profile.id || "");
    return `<option value="${escapeHtml(id)}"${selectedDriverProfileId === id ? " selected" : ""}>${escapeHtml(driverProfileOptionLabel(profile))}</option>`;
  }).join("");
  const driverDefaultLabel = driverMeta?.source === "local_visual_preset" ? "Skin driver" : "Default / skin driver";
  const selectedNeedsGameUpdate = mySkinNeedsGameUpdate(selected);
  const selectedGameSyncChanges = mySkinGameSyncChanges(selected);
  const selectedMissingCustomRearPlate = mySkinHasMissingCustomRearPlate(selected);
  const selectedProjectAssetCritical = mySkinProjectAssetCritical(selected);
  const selectedProjectAssetWarnings = mySkinProjectAssetWarnings(selected);
  const selectedMissingProjectAssets = selectedProjectAssetCritical.length > 0;
  const canToggleMySkinActive = !(selected.active === false && (selectedMissingCustomRearPlate || selectedMissingProjectAssets));
  const selectedCategoryId = liveryCategoryIdForSkin(selected);
  const driverSyncReason = selectedGameSyncChanges.join(" ").toLowerCase();
  const driverNeedsGameUpdate = selectedNeedsGameUpdate && (!driverSyncReason || driverSyncReason.includes("driver"));
  const driverStatusText = !selectedDriverProfileId
    ? "Using the skin/default driver."
    : (!selectedDriverProfileKnown
      ? "Selected driver profile is missing."
      : (driverNeedsGameUpdate
        ? "Update in Game required for this driver."
        : (selected.active === false
          ? "Saved locally. Activate in game to apply."
          : "Driver selection is saved.")));
  const activationDependencies = selected.activation_dependencies && typeof selected.activation_dependencies === "object" ? selected.activation_dependencies : {};
  const dependencyCritical = Array.isArray(activationDependencies.critical) ? activationDependencies.critical : [];
  const dependencyWarnings = Array.isArray(activationDependencies.warnings) ? activationDependencies.warnings : [];
  const exportDependencyNotes = mySkinExportDependencyNotes(selected, {
    tyreSelection,
    tyreCurrentLabel,
    tyreBrand,
    numberplateBake,
    numberplateTemplateId,
  });
  const projectMetadata = mySkinProjectMetadata(selected);
  const activeButtonLabel = mySkinsActionBusy
    ? (state.mySkinsActionBusyLabel || "Applying...")
    : selected.active === false
    ? t("activate_in_game")
    : (selectedNeedsGameUpdate ? t("update_in_game") : t("deactivate_in_game"));
  const activeButtonContent = mySkinsActionBusy
    ? `<span class="button-spinner" aria-hidden="true"></span><span>${escapeHtml(activeButtonLabel)}</span>`
    : `${uiIcon("power")}${escapeHtml(activeButtonLabel)}`;
  const paintFinishUpdateRequired = selectedNeedsGameUpdate
    && selectedGameSyncChanges.some((change) => /material finish/i.test(String(change || "")));
  const activeButtonTitle = paintFinishUpdateRequired
    ? "Paint finish changed. Apply the updated material textures to AC EVO."
    : selectedNeedsGameUpdate
      ? t("game_sync_outdated")
      : activeButtonLabel;
  const activeActionIsActivation = selected.active === false || selectedNeedsGameUpdate;
  const canToggleMySkinActiveByPolicy = activeActionIsActivation
    ? mySkinCan(selected, "canActivateInGame")
    : mySkinCan(selected, "canDeactivateInGame");
  const runtimeGuard = mySkinRuntimeGuard(selected);
  const canToggleMySkinActiveByRuntime = runtimeGuard.ready === true;
  if (
    selectedOfficialExternal
    && !selectedOfficialUnregistered
    && !state.runtimeProfileLoading
    && !runtimeProfileMatchesRoot(state.runtimeProfile, selected.game_root || state.root || "")
  ) {
    void refreshRuntimeProfile({ silent: true, root: selected.game_root || state.root || "" });
  }
  const readOnlyActionText = selectedOfficialUnregistered
    ? "Register it in LiveryLab before using preview, activation, deactivation, categories or removal actions."
    : selectedOfficialLibrary
    ? "You can inspect, preview, activate or deactivate this library copy and replace its local thumbnail. Livery artwork and authoring assets stay locked."
    : selectedPolicy.canActivateInGame || selectedPolicy.canDeactivateInGame || selectedPolicy.canRemove || selectedPolicy.canTrash
    ? "You can preview, activate/deactivate, remove or trash it and replace its local thumbnail. Livery editing and package export remain disabled."
    : "You can inspect it here. Game activation, removal and editing are disabled until the official Saved Games workflow is implemented.";
  const showDetailTabs = devModeEnabled();
  const activeDetailTab = showDetailTabs ? normalizeMySkinDetailTab(state.mySkinDetailTab) : "overview";
  detail.innerHTML = `
    <div class="my-skin-detail-head" data-tutorial-id="my-skins-detail-head">
      <div>
        <h2>${escapeHtml(selected.display_name || selected.skin_id || "Skin")}</h2>
        <div class="my-skin-subtitle">${escapeHtml([selected.car_model || "", selected.skin_id || ""].filter(Boolean).join(" / "))}</div>
      </div>
      <div class="my-skin-badges">
        ${mySkinActiveBadgeHtml(selected)}
        ${mySkinGameSyncBadgeHtml(selected)}
        ${devModeEnabled() ? mySkinOfficialExternalBadgeHtml(selected) : ""}
        ${mySkinExternalRegistrationBadgeHtml(selected)}
        ${mySkinExternalInstallBadgeHtml(selected)}
        ${mySkinPolicyBadgeHtml(selected)}
        ${mySkinProjectAssetBadgeHtml(selected)}
        ${mySkinCompatibilityWarningBadgeHtml(selected)}
      </div>
    </div>
    ${showDetailTabs ? `
      <div class="my-skin-detail-tabs" role="tablist" aria-label="Skin detail sections">
        <button id="mySkinDetailTabOverview" type="button" class="${activeDetailTab === "overview" ? "active" : ""}" role="tab" aria-selected="${activeDetailTab === "overview" ? "true" : "false"}" aria-controls="mySkinDetailPanelOverview" data-my-skin-detail-tab="overview">${uiIcon("edit")}Overview</button>
        <button id="mySkinDetailTabDiagnostics" type="button" class="${activeDetailTab === "diagnostics" ? "active" : ""}" role="tab" aria-selected="${activeDetailTab === "diagnostics" ? "true" : "false"}" aria-controls="mySkinDetailPanelDiagnostics" data-my-skin-detail-tab="diagnostics">${uiIcon("inspect")}Diagnostics</button>
      </div>
    ` : ""}
    <div id="mySkinDetailPanelOverview" class="my-skin-detail-panel"${showDetailTabs ? ` role="tabpanel" aria-labelledby="mySkinDetailTabOverview"` : ""}${activeDetailTab === "overview" ? "" : " hidden"}>
    <div class="my-skin-toolbar my-skin-toolbar-primary">
      <button id="openMySkin3dPreview" type="button" class="preview icon-text" data-tutorial-id="${selectedOfficialLibrary ? "my-skins-preview-3d-read-only" : "my-skins-preview-3d"}"${disabledAttr(canOpen3dPreview)}>${uiIcon("cube")}${escapeHtml(selectedOfficialLibrary ? "View in 3D" : "3D Livery Preview")}</button>
      ${selectedOfficialLibrary ? "" : `<button id="openMySkinLayerStudio" type="button" class="magenta icon-text" data-tutorial-id="my-skins-layer-studio"${disabledAttr(canOpenAuthoringTools)}>${uiIcon("texture")}Layer Studio</button>`}
      ${selectedOfficialUnregistered ? `
        <button id="registerOfficialExternalLivery" type="button" class="primary icon-text" title="${escapeHtml(registrationBlockers.join(" ") || "Register this detected ExternalLivery in LiveryLab.")}"${disabledAttr(canRegisterOfficialExternal)}>${uiIcon("plus")}Register in LiveryLab</button>
      ` : `<button id="toggleMySkinActive" type="button" class="icon-text ${selected.active === false || selectedNeedsGameUpdate ? "primary" : ""} ${selectedNeedsGameUpdate ? "my-skin-game-sync-pulse" : ""} ${mySkinsActionBusy ? "is-working" : ""}" data-tutorial-id="my-skins-activate-game" title="${escapeHtml(activeButtonTitle)}"${!canToggleMySkinActive || !canToggleMySkinActiveByPolicy || !canToggleMySkinActiveByRuntime || mySkinsActionBusy ? " disabled" : ""}${mySkinsActionBusy ? " aria-busy=\"true\"" : ""}>${activeButtonContent}</button>`}
      ${selectedReadOnly ? "" : `<button id="openMySkinProjectFolder" type="button" class="icon-text" data-tutorial-id="my-skins-project-folder"${disabledAttr(canOpenAuthoringTools)}>${uiIcon("folder")}${escapeHtml(t("open_project_folder"))}</button>`}
      ${canDuplicateProject ? `<button id="duplicateMySkinProject" type="button" class="icon-text" data-tutorial-id="my-skins-duplicate"${disabledAttr(true)}>${uiIcon("copy")}Duplicate Livery</button>` : ""}
      ${selectedOfficialLibrary ? "" : `<button id="openMySkinExportPackage" type="button" class="icon-text" data-tutorial-id="my-skins-export-package"${disabledAttr(canExportPackage)}>${uiIcon("export")}${escapeHtml(t("export_package"))}</button>`}
      ${selectedOfficialUnregistered ? "" : `<button id="openMySkinLiveryTools" type="button" class="danger-outline icon-text" data-tutorial-id="my-skins-remove"${disabledAttr(canRemoveOrTrash)}>${uiIcon("trash")}Remove</button>`}
    </div>
    ${mySkinAdvancedRuntimeNoticeHtml(selected)}
    ${selectedOfficialUnregistered ? `
      <div class="my-skin-sync-notice" data-tutorial-id="my-skins-official-registration">
        <strong>Not registered</strong>
        <span>This Official livery was detected in Saved Games but is not managed by LiveryLab yet. Registration creates a read-only local copy and preserves its matching SavedCar for later activation and deactivation.</span>
        ${registrationBlockers.length ? `
          <ul class="my-skin-sync-list">
            ${registrationBlockers.map((row) => `<li>${escapeHtml(row)}</li>`).join("")}
          </ul>
        ` : ""}
      </div>
    ` : ""}
    ${selectedReadOnly ? `
      <div class="my-skin-sync-notice" data-tutorial-id="my-skins-read-only">
        <strong>${escapeHtml(selectedOfficialLibrary ? "Read-only official library copy" : "Read-only installed livery")}</strong>
        <span>${escapeHtml(mySkinReadOnlyReason(selected))} ${escapeHtml(readOnlyActionText)}</span>
      </div>
    ` : ""}
    ${selectedNeedsGameUpdate ? `
      <div class="my-skin-sync-notice" data-tutorial-id="my-skins-game-update">
        <strong>${escapeHtml(t("game_update_needed"))}</strong>
        <span>${escapeHtml(t("game_sync_outdated"))}</span>
        ${selectedGameSyncChanges.length ? `
          <ul class="my-skin-sync-list">
            ${selectedGameSyncChanges.map((change) => `<li>${escapeHtml(change)}</li>`).join("")}
          </ul>
        ` : ""}
      </div>
    ` : ""}
    ${selectedMissingCustomRearPlate ? `
      <div class="my-skin-sync-notice danger" data-tutorial-id="my-skins-road-plate-missing">
        <strong>Custom rear plate image missing</strong>
        <span>Restore ${escapeHtml(roadPlateProjectPngPath || "plate_r.png")} in the LiveryProject folder, or switch the rear plate setting back to the AC EVO default rear plate.</span>
      </div>
    ` : ""}
    ${selectedMissingProjectAssets ? `
      <div class="my-skin-sync-notice danger" data-tutorial-id="my-skins-project-assets-missing">
        <strong>Required LiveryProject files missing</strong>
        <span>Restore the missing project files before activating or updating this livery in game.</span>
        <ul class="my-skin-sync-list">
          ${selectedProjectAssetCritical.map((asset) => `<li>${escapeHtml(asset.filename || asset.label || asset.message || "Project file")}</li>`).join("")}
        </ul>
      </div>
    ` : ""}
    ${!selectedMissingProjectAssets && selectedProjectAssetWarnings.length ? `
      <div class="my-skin-sync-notice" data-tutorial-id="my-skins-project-assets-warning">
        <strong>LiveryProject file note</strong>
        <span>${escapeHtml(selectedProjectAssetWarnings[0]?.message || "A non-critical LiveryProject file is missing.")}</span>
      </div>
    ` : ""}
    ${dependencyCritical.length || dependencyWarnings.length ? `
      <div class="my-skin-sync-notice ${dependencyCritical.length ? "danger" : ""}">
        <strong>${dependencyCritical.length ? "Missing required assets" : "Package dependency note"}</strong>
        <span>${escapeHtml((dependencyCritical[0] || dependencyWarnings[0] || {}).message || "Some package dependencies need attention.")}</span>
      </div>
    ` : ""}
    <div class="my-skin-edit-layout">
      <div class="my-skin-edit-card my-skin-edit-card-preview">
        <div class="my-skin-preview-panel">
          <span class="my-skin-preview-frame">
            <img class="my-skin-preview-image" data-skin-thumb-path="${escapeHtml(mySkinThumbnailPath(selected))}" data-skin-thumb-version="${escapeHtml(mySkinThumbnailVersion(selected))}" alt="" src="${mySkinPlaceholderSrc()}" />
          </span>
        </div>
      </div>
      <div class="my-skin-edit-card my-skin-edit-card-edit">
        <div class="my-skin-section is-framed" data-tutorial-id="my-skins-edit-section">
          <div class="my-skin-section-title">${uiIcon(showProjectMetadataEditor ? "edit" : "info")}${escapeHtml(showProjectMetadataEditor ? t("edit") : "Details")}</div>
          <div class="my-skin-edit-grid my-skin-browse-grid">
            ${showProjectMetadataEditor ? `
              <div class="my-skin-edit-name-row my-skin-grid-wide">
                <label>${escapeHtml(t("display_name"))}
                  <input id="mySkinEditDisplayName" type="text" maxlength="${LIVERY_DISPLAY_NAME_MAX_LENGTH}" spellcheck="false" value="${escapeHtml(selected.display_name || "")}" disabled />
                </label>
              </div>
              <div class="my-skin-edit-metadata-row my-skin-grid-wide">
                <label>Author
                  <input id="mySkinEditAuthor" type="text" maxlength="96" spellcheck="false" value="${escapeHtml(projectMetadata.author || "")}"${disabledAttr(canEditProjectMetadata)} />
                </label>
                <label>Description
                  <input id="mySkinEditDescription" type="text" maxlength="${LIVERY_DESCRIPTION_MAX_LENGTH}" spellcheck="false" value="${escapeHtml(projectMetadata.description || "")}"${disabledAttr(canEditProjectMetadata)} />
                </label>
                <div class="my-skin-edit-actions my-skin-edit-actions-inline">
                  <button id="saveMySkinProjectMetadata" type="button" class="primary icon-text${detailBusyClass("saveMySkinProjectMetadata")}" data-tutorial-id="my-skins-save-project-metadata"${disabledAttr(canEditProjectMetadata, "saveMySkinProjectMetadata")}>${detailBusyContent("saveMySkinProjectMetadata", `${uiIcon("save")}Save metadata`)}</button>
                </div>
              </div>
            ` : `
              <div class="my-skin-readonly-metadata my-skin-grid-wide">
                <div>
                  <span>${escapeHtml(t("display_name"))}</span>
                  <strong>${escapeHtml(selected.display_name || selected.skin_id || "-")}</strong>
                </div>
                <div>
                  <span>Author</span>
                  <strong>${escapeHtml(projectMetadata.author || "-")}</strong>
                </div>
                <div class="is-wide">
                  <span>Description</span>
                  <strong>${escapeHtml(projectMetadata.description || "-")}</strong>
                </div>
              </div>
            `}
            ${selectedOfficialUnregistered ? "" : `<div class="my-skin-edit-name-row my-skin-grid-wide">
              <label>Category
                <select id="mySkinCategorySelect">
                  ${liveryCategoryOptionsHtml(selectedCategoryId)}
                </select>
              </label>
              <div class="my-skin-edit-actions my-skin-edit-actions-inline">
                <button id="saveMySkinCategory" type="button" class="primary icon-text" data-tutorial-id="my-skins-save-category">${uiIcon("save")}Save category</button>
              </div>
            </div>`}
            <div class="my-skin-grid-wide">
              ${mySkinAceSnapshotHtml(selected)}
            </div>
          </div>
        </div>
      </div>
      <div class="my-skin-edit-card my-skin-edit-card-tyres"${selectedOfficialExternal ? " hidden" : ""}>
        <div class="my-skin-section is-framed" data-tutorial-id="my-skins-tyres-driver-section">
          <div class="my-skin-section-title">${uiIcon("wheel")}Tyres &amp; Driver</div>
          <div class="my-skin-edit-subsection">
            <div class="my-skin-section-subtitle">Tyres</div>
            ${tyreSupported ? `
              <div class="my-skin-edit-grid my-skin-tyre-grid">
                <label>Tyre brand
                  <select id="mySkinTyreBrand"${disabledAttr(canEditTyres)}>
                    <option value="default"${tyreSelection === "default" ? " selected" : ""}>${escapeHtml(tyreDefaultLabel)}</option>
                    ${!tyreSelectionKnown ? `<option value="" selected disabled>${escapeHtml(tyreCurrentLabel)} (unsupported current override)</option>` : ""}
                    ${tyreOptionsHtml}
                  </select>
                </label>
                <div class="my-skin-edit-actions my-skin-edit-actions-inline">
                  <button id="saveMySkinTyreBrand" type="button" class="primary icon-text${detailBusyClass("saveMySkinTyreBrand")}" data-tutorial-id="my-skins-save-tyres"${disabledAttr(canEditTyres, "saveMySkinTyreBrand")}>${detailBusyContent("saveMySkinTyreBrand", `${uiIcon("save")}Save tyres`)}</button>
                </div>
              </div>
            ` : `
              <div class="my-skins-empty small">
                <strong>Not supported</strong>
                <span>${escapeHtml(tyreSupportMessage)}</span>
              </div>
            `}
          </div>
          <div class="my-skin-edit-subsection">
            <div class="my-skin-section-subtitle">Driver</div>
            ${driverSupported ? `
              <div class="my-skin-edit-grid">
                <label class="my-skin-grid-wide">Driver profile
                  <select id="mySkinDriverProfile"${disabledAttr(canEditDriver)}>
                    <option value=""${selectedDriverProfileId ? "" : " selected"}>${escapeHtml(driverDefaultLabel)}</option>
                    ${driverProfilesLoading ? `<option value="" disabled>Loading driver profiles...</option>` : ""}
                    ${driverProfilesLoaded && !driverProfilesLoading && !carDriverProfiles.length ? `<option value="" disabled>No driver profiles for this car</option>` : ""}
                    ${!selectedDriverProfileKnown ? `<option value="${escapeHtml(selectedDriverProfileId)}" selected disabled>${escapeHtml(selectedDriverProfile.name || selectedDriverProfileId)} (missing profile)</option>` : ""}
                    ${driverOptionsHtml}
                  </select>
                </label>
                <div class="my-skins-empty small my-skin-grid-wide">
                  <strong>${escapeHtml(driverStatusText)}</strong>
                </div>
                <div class="my-skin-edit-actions my-skin-grid-wide">
                  <button id="saveMySkinDriverProfile" type="button" class="primary icon-text${detailBusyClass("saveMySkinDriverProfile")}" data-tutorial-id="my-skins-save-driver"${disabledAttr(canEditDriver, "saveMySkinDriverProfile")}>${detailBusyContent("saveMySkinDriverProfile", `${uiIcon("save")}Save driver`)}</button>
                </div>
              </div>
            ` : `
              <div class="my-skins-empty small">
                <strong>Not supported</strong>
                <span>${escapeHtml(driverCarSupported ? "This skin has no driver final state." : DRIVER_PROFILE_UNSUPPORTED_MESSAGE)}</span>
              </div>
            `}
          </div>
        </div>
      </div>
      <div class="my-skin-edit-card my-skin-edit-card-numberplate"${selectedOfficialExternal ? " hidden" : ""}>
        <div class="my-skin-section is-framed" data-tutorial-id="my-skins-numberplate-section">
          <div class="my-skin-section-title">${uiIcon("plate")}${escapeHtml(t("numberplate_variant"))}</div>
          <fieldset class="my-skin-edit-grid my-skin-numberplate-fields"${disabledAttr(canEditNumberplate)}>
            <label>Numberplate type
              <select id="mySkinNumberplateSource">
                ${numberplateSourceOptionsHtml}
              </select>
            </label>
            <div id="mySkinNumberplateEvoPanel" class="my-skins-empty small my-skin-grid-wide"${numberplateEditorSource !== "acevo" ? " hidden" : ""}>
              Uses the AC EVO numberplate that belongs to the base skin. LiveryLab does not replace its font, background, placement or material.
            </div>
            <div id="mySkinRoadPlateDefaultPanel" class="my-skins-empty small my-skin-grid-wide"${numberplateEditorSource !== "road_default" ? " hidden" : ""}>
              Uses the AC EVO rear plate that belongs to the base car. LiveryLab does not replace the rear plate texture for this livery.
            </div>
            <div id="mySkinRoadPlateCustomPanel" class="my-skins-empty small my-skin-grid-wide"${numberplateEditorSource !== "road_custom" ? " hidden" : ""}>
              <span>${escapeHtml(roadPlateCustomHint)}</span>
              <span>${escapeHtml(roadPlateProjectPngExists ? roadPlateProjectPngPath : "plate_r.png")}</span>
            </div>
            <div id="mySkinNumberplateNonePanel" class="my-skins-empty small my-skin-grid-wide"${numberplateEditorSource !== "none" ? " hidden" : ""}>
              <span>${escapeHtml(numberplateUnsupported ? "This car has no AC EVO numberplate. You can still set the HUD/start number here." : "For custom numberplates, export PNGs from Numberplate Studio and place them directly on the livery texture for correct paint finish and material behavior.")}</span>
              <label>Start number
                <input id="mySkinNumberplateNoPlateNumber" type="text" inputmode="numeric" maxlength="3" spellcheck="false" value="${escapeHtml(numberplateStartNumber)}" />
              </label>
            </div>
            <div class="my-skin-edit-actions my-skin-grid-wide">
              <button id="saveMySkinNumberplate" type="button" class="primary icon-text${detailBusyClass("saveMySkinNumberplate")}" data-tutorial-id="my-skins-save-numberplate"${disabledAttr(canEditNumberplate, "saveMySkinNumberplate")}>${detailBusyContent("saveMySkinNumberplate", `${uiIcon("save")}${escapeHtml(t("save_numberplate"))}`)}</button>
            </div>
          </fieldset>
        </div>
      </div>
    </div>
    </div>
    <div id="mySkinDetailPanelDiagnostics" class="my-skin-detail-panel my-skin-diagnostics-panel" role="tabpanel" aria-labelledby="mySkinDetailTabDiagnostics"${activeDetailTab === "diagnostics" ? "" : " hidden"}>
      ${selectedOfficialExternal ? `
        <div class="my-skin-toolbar my-skin-toolbar-secondary">
          ${canRefreshOfficialManifest ? `<button id="refreshOfficialExternalProjectManifest" type="button" class="icon-text" data-tutorial-id="my-skins-official-refresh">${uiIcon("refresh")}Refresh official manifest</button>` : ""}
          ${canValidateOfficialExternal ? `<button id="preflightOfficialExternalDeployment" type="button" class="icon-text" data-tutorial-id="my-skins-official-preflight">${uiIcon("inspect")}Run validation</button>` : ""}
          ${canRelinkOfficialSavedCar ? `<button id="relinkOfficialSavedCar" type="button" class="icon-text" data-tutorial-id="my-skins-official-saved-car-link">${uiIcon("target")}Relink SavedCar</button>` : ""}
        </div>
        ${mySkinOfficialExternalHealthHtml(selected)}
        ${mySkinOfficialSavedCarLinkHtml(selected)}
        ${mySkinSavedCarTemplateStatusHtml(selected)}
        ${mySkinOfficialExternalDetailsHtml(selected)}
      ` : `
        <div class="my-skins-empty small">
          <strong>Diagnostics</strong>
          <span>No official external diagnostics are available for this livery.</span>
        </div>
      `}
    <details class="my-skin-technical-details">
      <summary>Technical Details</summary>
      <div class="my-skin-technical-body">
        <div class="my-skin-section" data-tutorial-id="my-skins-storage-mode">
          <div class="my-skin-section-title">${escapeHtml(t("storage_mode"))}</div>
          <div class="my-skin-info-grid">
            <div><span>${escapeHtml(t("local_library_source"))}</span><strong>${escapeHtml(selected.local_root || "-")}</strong></div>
            <div><span>${escapeHtml(t("game_root"))}</span><strong>${escapeHtml(selected.game_root || "-")}</strong></div>
            <div><span>${escapeHtml(t("active_in_game"))}</span><strong>${escapeHtml(selected.active === false ? t("inactive_in_game") : t("active_in_game"))}</strong></div>
          </div>
        </div>
        <div class="my-skin-section">
          <div class="my-skin-section-title">${uiIcon("plate")}${escapeHtml(t("numberplate_variant"))}</div>
          <div class="my-skin-info-grid">
            <div><span>Variant</span><strong>${escapeHtml(numberplateIndex)}</strong></div>
            <div><span>Selector</span><strong>${escapeHtml(numberplateSelector)}</strong></div>
            <div><span>Start number</span><strong>${escapeHtml(numberplateBake.start_number || numberplate.start_number || selected.start_number || "-")}</strong></div>
          </div>
        </div>
        <div class="my-skin-section">
          <div class="my-skin-section-title">Tyres</div>
          <div class="my-skin-info-grid">
            <div><span>Selection</span><strong>${escapeHtml(tyreCurrentLabel)}</strong></div>
            <div><span>Source</span><strong>${escapeHtml(tyreBrand.source || "-")}</strong></div>
            <div><span>Family</span><strong>${escapeHtml(tyreFamily || "-")}</strong></div>
            <div><span>Options</span><strong>${escapeHtml(tyreSupported ? String(tyreOptions.length) : "not supported")}</strong></div>
            <div><span>Default</span><strong>${escapeHtml(tyreBrand.defaultLabel || "-")}</strong></div>
          </div>
        </div>
        <div class="my-skin-section">
          <div class="my-skin-section-title">Driver</div>
          <div class="my-skin-info-grid">
            <div><span>Supported</span><strong>${driverSupported ? "yes" : "no"}</strong></div>
            <div><span>Car support</span><strong>${driverCarSupported ? "yes" : "no"}</strong></div>
            <div><span>Selection</span><strong>${escapeHtml(selectedDriverProfileId ? (selectedDriverProfile.name || selectedDriverProfileId) : driverDefaultLabel)}</strong></div>
            <div><span>Profile ID</span><strong>${escapeHtml(selectedDriverProfileId || "-")}</strong></div>
            <div><span>Slots</span><strong>${escapeHtml((driverMeta?.designSlots || []).join(", ") || "-")}</strong></div>
          </div>
        </div>
        <div class="my-skin-technical-columns">
          <div class="my-skin-section">
            <div class="my-skin-section-title">${escapeHtml(t("relevant_files"))}</div>
            <div class="my-skin-file-list">
              ${files.map((file) => `
                <div class="my-skin-file-row ${file.exists ? "is-found" : "is-missing"}">
                  <div class="kind">${escapeHtml(file.kind)}</div>
                  <div class="path">${escapeHtml(file.path)}</div>
                  <div class="state">${file.exists ? "found" : escapeHtml(t("missing")).toLowerCase()}</div>
                </div>
              `).join("")}
            </div>
          </div>
          <div class="my-skin-section">
            <div class="my-skin-section-title">${escapeHtml(t("localization"))}</div>
            <div class="my-skin-loc-list">
              ${locFiles.length ? locFiles.map((path) => `<div class="my-skin-loc-row">${escapeHtml(path)}</div>`).join("") : `<div class="my-skins-empty small">${escapeHtml(t("no_loc_registered"))}</div>`}
            </div>
          </div>
        </div>
      </div>
    </details>
    </div>
  `;
  document.querySelectorAll("[data-my-skin-detail-tab]").forEach((button) => {
    button.addEventListener("click", () => setMySkinDetailTab(button.dataset.mySkinDetailTab || "overview"));
  });
  if (selectedOfficialExternal && activeDetailTab === "diagnostics" && String(officialExternalInfo(selected).savedCarSource || "").trim() !== "liverylab_generated") {
    ensureOfficialSavedCarTemplatesLoaded(selected.car_model || selected.carModel || "");
  }
  $("saveMySkinProjectMetadata")?.addEventListener("click", async () => {
    if (!canEditProjectMetadata) {
      showMySkinReadOnlyBlocked(selected, "Project metadata editing");
      return;
    }
    const author = $("mySkinEditAuthor")?.value.trim() || "";
    const description = ($("mySkinEditDescription")?.value.trim() || "").slice(0, LIVERY_DESCRIPTION_MAX_LENGTH).trim();
    await withButtonWorking("saveMySkinProjectMetadata", "Saving...", async () => {
      const result = await call("update_my_skin", {
        skinKey: selected.key,
        author,
        description,
      });
      if (!result.ok) return;
      log(result.message || "Project metadata saved");
      await loadMySkins();
    });
  });
  $("saveMySkinCategory")?.addEventListener("click", () => assignSelectedLiveryCategory(selected));
  $("refreshMySkinAceThumbnail")?.addEventListener("click", async () => {
    const skinKey = String(selected.key || "").trim();
    await withButtonWorking("refreshMySkinAceThumbnail", "Checking...", async () => {
      const waitResult = await waitForMySkinAceThumbnail(selected);
      clearMySkinThumbnailCache();
      await loadMySkins();
      const refreshed = (state.mySkins || []).find((row) => String(row.key || "") === skinKey) || {};
      if (waitResult.ready && mySkinAceSnapshot(refreshed).available === true) {
        const message = "AC EVO thumbnail found. You can now use it as the local livery thumbnail.";
        log(message);
        setInspectOutputText(message);
      } else {
        const message = `${waitResult.reason || "No newer AC EVO thumbnail was found after checking for 8 seconds."} Open the garage car in AC EVO, change one Car Customization option, press Apply/Save, then try Refresh again.`;
        log(message);
        setInspectOutputText(message);
      }
    });
  });
  $("useMySkinAceThumbnail")?.addEventListener("click", async () => {
    if (!canEditPreviewImage) {
      showMySkinReadOnlyBlocked(selected, "Preview image editing");
      return;
    }
    const label = selected.display_name || selected.skin_id || "this skin";
    const active = selected.active !== false;
    const result = await showConfirmModal({
      title: "Use AC EVO thumbnail",
      message: `Replace the current preview image for "${label}" with the AC EVO snapshot?`,
      details: [
        selectedReadOnly
          ? "This stores a personal thumbnail override in LiveryLab-managed preview storage. The original package artwork and authoring assets remain unchanged."
          : "This overwrites the current LiveryProjects livery_thumbnail.png for this skin.",
        selectedOfficialExternal
          ? "Official livery game content is not modified; its preview remains in LiveryLab-managed storage and Saved Games."
          : active
          ? "Because this skin is active, LiveryLab will also update the AC EVO generated preview thumbnails used by the game."
          : "The game files are not updated because this skin is not active in game.",
      ].join("\n"),
      confirmLabel: "Use Thumbnail",
      loadingLabel: "Applying...",
      onConfirm: () => call("use_my_skin_ace_snapshot_thumbnail", { skinKey: selected.key }),
    });
    if (!result) return;
    if (!result.ok) return;
    clearMySkinThumbnailCache();
    await refreshOpenLiveryPreviewNumberplateAfterSkinUpdate(selected.key);
    await loadMySkins();
  });
  $("saveMySkinTyreBrand")?.addEventListener("click", async () => {
    if (!canEditTyres) {
      showMySkinReadOnlyBlocked(selected, "Tyre editing");
      return;
    }
    const brand = $("mySkinTyreBrand")?.value || "";
    if (!brand) {
      showStatusModal({
        type: "error",
        title: "Tyres",
        subtitle: "Choose a tyre option",
        error: "The current tyre override does not match the detected tyre family.",
        details: "Choose Default or one of the available tyre options before saving.",
      });
      return;
    }
    await withButtonWorking("saveMySkinTyreBrand", "Saving...", async () => {
      const result = await call("update_my_skin_tyre_brand", {
        skinKey: selected.key,
        brand,
      });
      if (!result.ok) return;
      const updated = result.data?.item || {};
      const label = updated.display_name || selected.display_name || updated.skin_id || selected.skin_id || "Skin";
      const actions = Array.isArray(result.data?.actions) ? result.data.actions : [];
      const needsGameUpdate = mySkinNeedsGameUpdate(updated);
      state.mySkinsDetailBusy = true;
      state.mySkinsDetailBusyButtonId = "saveMySkinTyreBrand";
      state.mySkinsDetailBusyLabel = "Updating...";
      renderMySkins();
      try {
        await loadMySkins();
        const summary = needsGameUpdate
          ? `Tyre brand settings for "${label}" were saved locally. Use Update in Game to make them visible in AC EVO.`
          : `Tyre brand settings for "${label}" were saved.`;
        const details = [
          summary,
          result.message || "",
          ...(actions.length ? ["", "Changes:", ...actions.map((action) => `- ${action}`)] : []),
        ].filter(Boolean).join("\n");
        log(summary);
        setInspectOutputText(details);
      } finally {
        state.mySkinsDetailBusy = false;
        state.mySkinsDetailBusyButtonId = "";
        state.mySkinsDetailBusyLabel = "";
        renderMySkins();
      }
    });
  });
  if (driverSupported && !driverProfilesLoaded && !driverProfilesLoading) {
    ensureDriverProfilesLoaded(selected.car_model).then(() => {
      if (state.mySkinSelectedKey === selected.key) renderMySkins();
    });
  }
  $("mySkinDriverProfile")?.addEventListener("focus", () => {
    if (!state.driverProfilesLoadedCars?.has(driverCarKey(selected.car_model))) {
      ensureDriverProfilesLoaded(selected.car_model).then(() => {
        if (state.mySkinSelectedKey === selected.key) renderMySkins();
      });
    }
  });
  $("saveMySkinDriverProfile")?.addEventListener("click", async () => {
    if (!canEditDriver) {
      showMySkinReadOnlyBlocked(selected, "Driver editing");
      return;
    }
    const profileId = $("mySkinDriverProfile")?.value || "";
    await withButtonWorking("saveMySkinDriverProfile", "Saving...", async () => {
      const result = await call("update_my_skin_driver_profile", {
        skinKey: selected.key,
        profileId,
        carModel: selected.car_model || "",
      });
      if (!result.ok) return;
      const updated = result.data?.item || {};
      const label = updated.display_name || selected.display_name || updated.skin_id || selected.skin_id || "Skin";
      const actions = Array.isArray(result.data?.actions) ? result.data.actions : [];
      const needsGameUpdate = mySkinNeedsGameUpdate(updated);
      state.mySkinsDetailBusy = true;
      state.mySkinsDetailBusyButtonId = "saveMySkinDriverProfile";
      state.mySkinsDetailBusyLabel = "Updating...";
      renderMySkins();
      try {
        await loadMySkins();
        const summary = needsGameUpdate
          ? `Driver settings for "${label}" were saved locally. Use Update in Game to make them visible in AC EVO.`
          : `Driver settings for "${label}" were saved.`;
        const details = [
          summary,
          result.message || "",
          ...(actions.length ? ["", "Changes:", ...actions.map((action) => `- ${action}`)] : []),
        ].filter(Boolean).join("\n");
        log(summary);
        setInspectOutputText(details);
      } finally {
        state.mySkinsDetailBusy = false;
        state.mySkinsDetailBusyButtonId = "";
        state.mySkinsDetailBusyLabel = "";
        renderMySkins();
      }
    });
  });
  if (NUMBERPLATE_SKIN_TEMPLATE_WORKFLOW_ENABLED) loadNumberplateTemplateOptions().then(() => {
    const currentTemplateId = numberplateTemplateId || numberplate.template_id || "";
    renderNumberplateStudioTemplateSelect("mySkinNumberplateTemplate", currentTemplateId, numberplate);
    const templateAvailableNow = !currentTemplateId || Boolean(numberplateStudioTemplateById(currentTemplateId));
    const lockedNow = Boolean(numberplateBake.surfaces && currentTemplateId && !templateAvailableNow);
    const templatePanel = $("mySkinNumberplateTemplatePanel");
    if (templatePanel) templatePanel.dataset.templateMissing = lockedNow ? "1" : "0";
    $("mySkinNumberplateTemplate")?.toggleAttribute("disabled", lockedNow);
    $("mySkinNumberplateStartNumber")?.toggleAttribute("disabled", lockedNow);
    updateMySkinNumberplateTemplateInfo($("mySkinNumberplateTemplate")?.value || "");
    if (lockedNow && $("mySkinNumberplateTemplateInfo")) {
      $("mySkinNumberplateTemplateInfo").textContent = `This skin uses baked numberplates from "${currentTemplateId}". The original template is not installed, so the start number cannot be changed.`;
    }
    updateMySkinNumberplateSourceUi();
  });
  const mySkinNumberplateSourceSelect = $("mySkinNumberplateSource");
  if (mySkinNumberplateSourceSelect) {
    mySkinNumberplateSourceSelect.dataset.previousSource = mySkinNumberplateSourceSelect.value || "acevo";
    mySkinNumberplateSourceSelect.addEventListener("change", () => {
      const previousSource = mySkinNumberplateSourceSelect.dataset.previousSource || "";
      const nextSource = mySkinNumberplateSourceSelect.value || "acevo";
      if (nextSource === "acevo" && previousSource !== "acevo") {
        clearMySkinAcevoNumberplateControls();
      }
      mySkinNumberplateSourceSelect.dataset.previousSource = nextSource;
      updateMySkinNumberplateSourceUi();
    });
  }
  $("mySkinNumberplateMode")?.addEventListener("change", updateMySkinNumberplateModeUi);
  $("mySkinNumberplateTemplate")?.addEventListener("change", (event) => {
    updateMySkinNumberplateTemplateInfo(event.target?.value || "");
  });
  $("mySkinNumberplateTemplate")?.addEventListener("focus", () => loadNumberplateTemplateOptions());
  $("mySkinNumberplateTemplate")?.addEventListener("pointerdown", () => loadNumberplateTemplateOptions());
  $("mySkinNumberplateNoPlateNumber")?.addEventListener("input", (event) => {
    event.target.value = String(event.target.value || "").replace(/\D/g, "").slice(0, 3);
  });
  $("saveMySkinNumberplate")?.addEventListener("click", async () => {
    if (!canEditNumberplate) {
      showMySkinReadOnlyBlocked(selected, "Numberplate editing");
      return;
    }
    const source = $("mySkinNumberplateSource")?.value || "acevo";
    const isRoadPlateSource = source === "road_default" || source === "road_custom";
    if (numberplatesBlockedForNormalMode(selected) && source !== "none" && !isRoadPlateSource) {
      showStatusModal({
        type: "warning",
        title: t("numberplate_variant"),
        subtitle: "Not supported",
        message: numberplateUnsupportedMessage,
      });
      return;
    }
    let mode = "same";
    let fontPath = "";
    let backgroundPath = "";
    let hoodBackgroundPath = "";
    let leftBackgroundPath = "";
    let rightBackgroundPath = "";
    let textColor = null;
    let templateId = "";
    let startNumber = "";
    if (isRoadPlateSource) {
      mode = source;
    } else if (!NUMBERPLATE_SKIN_TEMPLATE_WORKFLOW_ENABLED) {
      mode = source === "none" ? "none" : "";
      if (source === "none") {
        startNumber = String($("mySkinNumberplateNoPlateNumber")?.value || "").replace(/\D/g, "").slice(0, 3);
        if (!startNumber) {
          showStatusModal({
            type: "error",
            title: "Start number required",
            subtitle: "Numberplate setting not saved",
            message: "Enter a start number before saving the No Numberplate option.",
          });
          return;
        }
      }
    } else if (source === "none") {
      mode = "none";
      startNumber = String($("mySkinNumberplateNoPlateNumber")?.value || "").replace(/\D/g, "").slice(0, 3);
      if (!startNumber) {
        showStatusModal({
          type: "error",
          title: "Start number required",
          subtitle: "Numberplate setting not saved",
          message: "Enter a start number before saving the No Numberplate option.",
        });
        return;
      }
    } else if (source === "template") {
      if ($("mySkinNumberplateTemplatePanel")?.dataset.templateMissing === "1") {
        showStatusModal({
          type: "warning",
          title: "Baked numberplate locked",
          subtitle: "Template not installed",
          message: "This skin only has baked numberplate files. Install the original Numberplate Studio template before changing the start number.",
        });
        return;
      }
      const templatePayload = selectedNumberplateTemplatePayload("mySkinNumberplateTemplate");
      if (!templatePayload?.templateId) {
        log("Please choose a numberplate template first", true);
        return;
      }
      startNumber = String($("mySkinNumberplateStartNumber")?.value || "").replace(/\D/g, "").slice(0, 3);
      if (!startNumber) {
        log("Please enter a start number first", true);
        showStatusModal({
          type: "error",
          title: "Start number required",
          subtitle: "Numberplate template not saved",
          message: "Please enter a start number before saving a LiveryLab numberplate template.",
        });
        return;
      }
      mode = templatePayload.mode || "surface";
      fontPath = templatePayload.fontPath || "";
      backgroundPath = templatePayload.backgroundPath || "";
      hoodBackgroundPath = templatePayload.hoodBackgroundPath || "";
      leftBackgroundPath = templatePayload.leftBackgroundPath || "";
      rightBackgroundPath = templatePayload.rightBackgroundPath || "";
      textColor = templatePayload.textColor || null;
      templateId = templatePayload.templateId || "";
    } else {
      mode = $("mySkinNumberplateMode")?.value || "same";
      fontPath = $("mySkinNumberplateFont")?.value.trim() || "";
      if (mode === "surface") {
        hoodBackgroundPath = hasHoodNumberplate ? ($("mySkinNumberplateHoodBackground")?.value.trim() || "") : "";
        leftBackgroundPath = $("mySkinNumberplateLeftBackground")?.value.trim() || "";
        rightBackgroundPath = $("mySkinNumberplateRightBackground")?.value.trim() || "";
      } else {
        backgroundPath = $("mySkinNumberplateBackground")?.value.trim() || "";
      }
    }
    await withButtonWorking("saveMySkinNumberplate", "Saving...", async () => {
      const result = await call("update_my_skin_numberplate", {
        skinKey: selected.key,
        mode,
        fontPath,
        backgroundPath,
        hoodBackgroundPath,
        leftBackgroundPath,
        rightBackgroundPath,
        textColor,
        templateId,
        startNumber,
        sourceType: source,
      });
      if (!result.ok) return;
      const updated = result.data?.item || {};
      const label = updated.display_name || selected.display_name || updated.skin_id || selected.skin_id || "Skin";
      const actions = Array.isArray(result.data?.actions) ? result.data.actions : [];
      const needsGameUpdate = mySkinNeedsGameUpdate(updated);
      state.mySkinsDetailBusy = true;
      state.mySkinsDetailBusyButtonId = "saveMySkinNumberplate";
      state.mySkinsDetailBusyLabel = "Updating...";
      renderMySkins();
      try {
        await loadMySkins();
        const summary = needsGameUpdate
          ? `Numberplate settings for "${label}" were saved locally. Use Update in Game to make them visible in AC EVO.`
          : `Numberplate settings for "${label}" were saved.`;
        const details = [
          summary,
          result.message || "",
          ...(actions.length ? ["", "Changes:", ...actions.map((action) => `- ${action}`)] : []),
        ].filter(Boolean).join("\n");
        log(summary);
        setInspectOutputText(details);
      } finally {
        state.mySkinsDetailBusy = false;
        state.mySkinsDetailBusyButtonId = "";
        state.mySkinsDetailBusyLabel = "";
        renderMySkins();
      }
    });
  });
  hydrateMySkinsViewThumbnails(list, detail);
  hydrateMySkinAceThumbnailActionState(selected);
  startMySkinThumbnailWatcher(selected);
  startMySkinRoadPlateWatcher(selected);
  updateDebugUi();
  $("openMySkinExportPackage")?.addEventListener("click", async () => {
    if (!canExportPackage) {
      showMySkinReadOnlyBlocked(selected, "Package export");
      return;
    }
    await openMySkinExportPackageModal(selected, exportDependencyNotes);
  });
  $("duplicateMySkinProject")?.addEventListener("click", async () => {
    if (!canDuplicateProject) {
      showMySkinReadOnlyBlocked(selected, "Duplicating this livery project");
      return;
    }
    const sourceLabel = selected.display_name || selected.skin_id || "Livery";
    const result = await showInputModal({
      title: "Duplicate Livery",
      subtitle: sourceLabel,
      message: "Create a new editable copy of this project?",
      label: "New livery name",
      value: `${sourceLabel} Copy`.slice(0, LIVERY_DISPLAY_NAME_MAX_LENGTH),
      details: [
        "The duplicate receives a new technical identity and remains inactive.",
        isOfficialExternalSkin(selected)
          ? "A new Official car GUID and a new local SavedCar are created. Saved Games and AC EVO are not modified."
          : "The Advanced project files and local authoring data are copied. AC EVO is not modified.",
      ].join("\n"),
      confirmLabel: "Duplicate Livery",
      loadingLabel: "Duplicating...",
      onConfirm: async (name) => {
        const displayName = String(name || "").trim().slice(0, LIVERY_DISPLAY_NAME_MAX_LENGTH).trim();
        if (!displayName) throw new Error("Please enter a name for the duplicated livery.");
        return window.pywebview.api.duplicate_livery_project({ skinKey: selected.key, displayName });
      },
    });
    if (!result) return;
    if (!result.ok) {
      const duplicateError = displayErrorFromResult("duplicate_livery_project", result, "The duplicate could not be created.");
      showStatusModal({
        type: "error",
        title: "Livery duplicate failed",
        subtitle: sourceLabel,
        error: duplicateError.summary,
        details: duplicateError.details,
        technicalDetails: technicalDetailsFromResult(result),
        detailsOpen: Boolean(duplicateError.details || technicalDetailsFromResult(result)),
      });
      return;
    }
    const duplicate = result.data?.entry || {};
    clearMySkinThumbnailCache();
    if (duplicate.key) state.mySkinSelectedKey = duplicate.key;
    await loadMySkins();
    showStatusModal({
      type: "success",
      title: "Livery duplicated",
      subtitle: duplicate.display_name || duplicate.skin_id || "New project",
      message: "The editable copy was created locally and is inactive.",
      details: [
        `Project: ${result.data?.projectFolder || duplicate.local_root || "-"}`,
        isOfficialExternalSkin(selected) ? `New car GUID: ${duplicate.car_guid || "-"}` : `New skin ID: ${duplicate.skin_id || "-"}`,
        "No AC EVO or Saved Games files were modified.",
      ].join("\n"),
    });
    setInspectOutputText(result.message || "Livery project duplicated");
  });
  $("openMySkinLiveryTools")?.addEventListener("click", async () => {
    if (!canRemoveOrTrash) {
      showMySkinReadOnlyBlocked(selected, "Removing this livery");
      return;
    }
    await openMySkinLiveryTools(selected);
  });
  $("openMySkinProjectFolder")?.addEventListener("click", async () => {
    if (!canOpenAuthoringTools) {
      showMySkinReadOnlyBlocked(selected, "Opening the editable LiveryProjects folder");
      return;
    }
    const inspect = await hydrateLiveryProjectTrashOption(selected.key);
    const projectFolder = String(inspect?.projectFolder || "").trim();
    if (!inspect?.exists || !projectFolder) {
      showStatusModal({
        type: "warning",
        title: t("open_project_folder"),
        subtitle: "Project folder not found",
        message: "No LiveryProjects folder exists for this livery yet.",
        details: projectFolder ? `Expected folder:\n${projectFolder}` : "",
      });
      return;
    }
    const result = await window.pywebview.api.open_folder(projectFolder);
    showResult(result, "Folder opened", "open_folder");
  });
  $("registerOfficialExternalLivery")?.addEventListener("click", async () => {
    if (!canRegisterOfficialExternal) return;
    const label = selected.display_name || selected.skin_id || "this Official livery";
    const sourceId = String(
      officialExternalInfo(selected).sourceDescriptorId
      || selected.key
      || ""
    ).trim();
    const result = await showConfirmModal({
      title: "Register in LiveryLab",
      message: `Register "${label}" as a read-only Official livery?`,
      details: [
        "LiveryLab will validate external_livery.json and every referenced PNG.",
        "The matching Garage SavedCar will be copied into the local Official library.",
        "Saved Games files are only read during registration and are not modified.",
        "Close AC EVO before continuing so the SavedCar snapshot stays consistent.",
      ].join("\n"),
      confirmLabel: "Register",
      loadingLabel: "Registering...",
      onConfirm: () => window.pywebview.api.register_official_external_livery({
        sourceDescriptorId: sourceId,
        root: state.root || $("rootPath")?.value?.trim() || "",
      }),
    });
    if (!result?.ok) return;
    const registered = result.data?.item || {};
    state.mySkinSelectedKey = registered.key || selected.key;
    clearMySkinThumbnailCache();
    await loadMySkins();
    setInspectOutputText([
      result.message || "Official external livery registered",
      "",
      `Local library: ${result.data?.localRoot || registered.local_root || "-"}`,
      `Copied files: ${Number(result.data?.copy?.copiedCount || 0)}`,
      "Status: read-only and managed by LiveryLab",
    ].join("\n"));
  });
  $("refreshOfficialExternalProjectManifest")?.addEventListener("click", async () => {
    if (!canRefreshOfficialManifest) return;
    const result = await window.pywebview.api.refresh_official_external_project_manifest({
      skinKey: selected.key,
      carGuid: selected.car_guid || "",
    });
    showResult(result, "Official manifest refreshed", "refresh_official_external_project_manifest");
    if (!result?.ok) return;
    const data = result.data || {};
    const status = data.assetStatus && typeof data.assetStatus === "object" ? data.assetStatus : {};
    const slotLines = Array.isArray(status.slots)
      ? status.slots
        .filter((slot) => slot && typeof slot === "object" && (slot.exists || slot.referenced))
        .map((slot) => `- ${slot.label || slot.id || slot.filename}: ${slot.exists ? "present" : "missing"}${slot.referenced ? ", referenced" : ""}`)
      : [];
    setInspectOutputText([
      result.message || "Official external manifest refreshed",
      "",
      `Project: ${data.projectFolder || "-"}`,
      `external_livery.json: ${data.externalLiveryJson || "-"}`,
      `Referenced PNG slots: ${Number(data.referencedSlotCount || 0)}/${Number(data.slotCount || 0)}`,
      `Changed: ${data.changed ? "yes" : "no"}`,
      ...(slotLines.length ? ["", "Slots:", ...slotLines] : []),
    ].join("\n"));
    await loadMySkins();
  });
  $("preflightOfficialExternalDeployment")?.addEventListener("click", async () => {
    if (!canValidateOfficialExternal) return;
    await withButtonWorking("preflightOfficialExternalDeployment", "Checking...", async () => {
      if (typeof window.pywebview?.api?.validate_official_external_livery !== "function") {
        setInspectOutputText(officialExternalValidationInspectText(selected));
        showStatusModal({
          type: "info",
          title: "Official local validation",
          subtitle: "Using loaded validation data",
          message: "The current runtime does not expose the live validation endpoint yet, so LiveryLab is showing the validation data already loaded with My Liveries.",
        });
        return;
      }
      const result = await window.pywebview.api.validate_official_external_livery({
        skinKey: selected.key,
      });
      showResult(result, "Official local validation", "validate_official_external_livery");
      if (!result?.ok) return;
      setInspectOutputText(officialExternalValidationInspectText(result.data || {}));
      await loadMySkins();
    });
  });
  $("relinkOfficialSavedCar")?.addEventListener("click", async () => {
    if (!canRelinkOfficialSavedCar) return;
    await withButtonWorking("relinkOfficialSavedCar", "Scanning...", async () => {
      const scan = await window.pywebview.api.list_official_saved_cars({
        skinKey: selected.key,
        customOnly: true,
      });
      if (!scan?.ok) {
        showResult(scan, "Official SavedCars scan", "list_official_saved_cars");
        return;
      }
      const items = Array.isArray(scan.data?.items) ? scan.data.items : [];
      const details = items.length
        ? items.map((row, index) => {
          const marker = Array.isArray(row.markerSources) && row.markerSources.length ? ` [${row.markerSources.join(", ")}]` : "";
          return `${index + 1}. ${row.carModel || "car"} | ${row.guid || "-"}${marker}\n   ${row.path || ""}`;
        }).join("\n")
        : "No official/custom SavedCars were found. LiveryLab hides unrelated garage cars by default.";
      const linkedGuid = String(selected.car_guid || officialExternalInfo(selected).linkedSavedCarGuid || "").trim();
      const result = await showInputModal({
        type: items.length ? "info" : "warning",
        title: "Relink SavedCar",
        subtitle: "Official external livery",
        message: "Paste the GUID of an official/custom SavedCar to link it to this project.",
        label: "SavedCar GUID",
        value: linkedGuid,
        details,
        confirmLabel: "Link SavedCar",
        loadingLabel: "Linking...",
        onConfirm: (value) => window.pywebview.api.link_official_external_project_saved_car({
          skinKey: selected.key,
          carGuid: value,
        }),
      });
      if (!result) return;
      showResult(result, "SavedCar linked", "link_official_external_project_saved_car");
      if (!result?.ok) return;
      const validation = result.data?.validation?.preflight || {};
      const health = validation.health && typeof validation.health === "object" ? validation.health : {};
      setInspectOutputText([
        result.message || "Official project SavedCar linked",
        "",
        `car_guid: ${result.data?.carGuid || "-"}`,
        `SavedCar file: ${result.data?.savedCarFound ? "found" : "not found"}`,
        `Health: ${health.summary || "-"}`,
      ].join("\n"));
      await loadMySkins();
    });
  });
  $("openMySkinLayerStudio")?.addEventListener("click", async () => {
    if (!canOpenAuthoringTools) {
      showMySkinReadOnlyBlocked(selected, "Layer Studio");
      return;
    }
    if (!window.LiveryLabLayerStudio?.openForSkin) {
      showStatusModal({
        type: "error",
        title: "Layer Studio",
        subtitle: "Module not loaded",
        message: "Layer Studio could not be opened because its frontend module is not available.",
      });
      return;
    }
    showLayerStudioInfoModalWhenNeeded();
    await window.LiveryLabLayerStudio.openForSkin(selected);
  });
  $("openMySkin3dPreview")?.addEventListener("click", async () => {
    if (!canOpen3dPreview) {
      showMySkinReadOnlyBlocked(selected, "3D preview");
      return;
    }
    if (state.liveryPreviewOpening) return;
    state.liveryPreviewOpening = true;
    const button = $("openMySkin3dPreview");
    setButtonWorking(button, true, "Opening...");
    try {
      window.LiveryLabLayerStudio?.close?.();
      if (!selectedReadOnly) showLiveryPreviewInfoModalWhenNeeded();
      log(`${t("log_running")} build_livery_preview_model...`);
      const result = await window.pywebview.api.build_livery_preview_model({ skinKey: selected.key, root: state.root });
      showResult(result, "3D Livery Preview model prepared");
      if (!result.ok) return;
      const data = result.data || {};
      const readOnlyPreview = data.project?.readOnly === true || selectedReadOnly;
      applyPreview3dProject(data.project || {});
      if (!readOnlyPreview) {
        await detectDefaultLiveryPreviewBodyBaseColor();
        const saveResult = await window.pywebview.api.save_preview3d_project(payloadPreview3dProject());
        if (!saveResult.ok) {
          renderPreview3dStatus({ warnings: [saveResult.message || "Could not save 3D preview setup"] });
          return;
        }
      }
      const loaded = await openLiveryPreviewWorkspace(data);
      if (loaded) await reloadPreview3dTexture();
      const materialMaps = Array.isArray(data.project?.liveMaterialTextureMaps)
        ? data.project.liveMaterialTextureMaps.filter((item) => item?.stem && !["ext_skin_c", "ext_skin_d"].includes(String(item.stem).toLowerCase()))
        : [];
      const materialMapLine = materialMaps.length
        ? materialMaps.map((item) => item.name || item.label || item.stem).join(", ")
        : "-";
      setInspectOutputText([
        result.message || "3D Livery Preview model prepared",
        "",
        `Project folder: ${data.projectFolder || "-"}`,
        `Live texture: ${data.project?.liveTexturePath || data.liveTexture?.path || "-"}`,
        `AO texture: ${data.project?.liveAoTexturePath || data.aoTexture?.path || "-"}`,
        `Detail/Splat texture: ${data.project?.liveDetailTexturePath || data.liveDetailTexture?.path || "-"}`,
        `Additional material maps: ${materialMapLine}`,
        `Banner texture: ${data.project?.liveBannerTexturePath || data.liveBannerTexture?.path || "-"}`,
        `Exterior mesh: ${data.project?.exteriorMeshPath || data.exteriorMesh?.path || "-"}`,
        `GLB cache: ${data.glb?.glbPath || data.modelCache?.path || "-"}`,
        `Wheel test GLBs: ${data.wheelPreview?.auxiliaryModels?.length || 0}`,
        `Viewer loaded: ${loaded ? "yes" : "no"}`,
        "",
        readOnlyPreview ? "Read-only preview:" : "Next step:",
        readOnlyPreview
          ? "This installed livery is shown from the preview cache. Editing tools, texture import and LiveryProjects workflow are disabled."
          : "Export your Photoshop PNG as EXT_SKIN_C.png into the project folder.",
        ...(readOnlyPreview ? [] : [
          "Optional detail/splat inspection: export EXT_Skin_D.png into the same folder.",
          "Optional material masks are imported when this skin uses them.",
          "Optional window banner: export EXT_Banner_C.png into the same folder.",
        ]),
        ...(data.warnings?.length ? ["", "Warnings:", ...data.warnings.map((row) => `- ${row}`)] : []),
      ].join("\n"));
    } finally {
      state.liveryPreviewOpening = false;
      setButtonWorking(button, false);
    }
  });
  $("toggleMySkinActive")?.addEventListener("click", async () => {
    const needsGameUpdate = mySkinNeedsGameUpdate(selected);
    const activate = selected.active === false || needsGameUpdate;
    const label = selected.display_name || selected.skin_id || "this entry";
    const actionLabel = selected.active === false
      ? t("activate_in_game")
      : (needsGameUpdate ? t("update_in_game") : t("deactivate_in_game"));
    const officialExternal = isOfficialExternalSkin(selected);
    const runtimeGuard = mySkinRuntimeGuard(selected);
    if (!runtimeGuard.ready) {
      showStatusModal({
        type: "warning",
        title: "Runtime blocks game write",
        subtitle: runtimeGuard.label || (officialExternal ? "Official runtime required" : "Advanced runtime required"),
        message: runtimeGuard.message || (officialExternal
          ? "Official game writes require Official runtime."
          : "Advanced game writes require Advanced/extracted mode."),
        details: runtimeGuard.details.join("\n"),
      });
      return;
    }
    const confirmed = await confirmGameWriteAction({
      title: actionLabel,
      message: officialExternal
        ? activate
          ? `Deploy "${label}" to the official AC EVO ExternalLiveries folder?`
          : `Deactivate "${label}" in game by moving its ExternalLiveries deployment and matching garage SavedCar into LiveryLab disabled storage?`
        : needsGameUpdate
        ? `Update "${label}" in game by syncing the current local My Liveries files into the ACE game folder?`
        : activate
        ? `Activate "${label}" in game by syncing the local My Liveries files into the ACE game folder?`
        : `Deactivate "${label}" in game? Matching user garage SavedCars for this livery will be removed. The local My Liveries files stay available.`,
      writes: officialExternal ? (activate ? [
        "Copy external_livery.json and referenced PNG files to Saved Games/ACE/ExternalLiveries.",
        "Restore the last vaulted garage SavedCar, including its customization, when available.",
        "Otherwise install the prepared LiveryLab-generated SavedCar when it is not present yet.",
        "Never overwrite an existing incompatible SavedCar.",
        "The activated car will appear under Vehicles → My Cars, not in Vehicle Hub.",
        "AC EVO does not currently display the custom LiveryLab livery name in game.",
        "Clear matching generated .texture/.texturemips files in the deployment folder so AC EVO regenerates them.",
        "Do not write into AC EVO game files.",
      ] : [
        "Move the active Saved Games/ACE/ExternalLiveries folder into LiveryLab disabled storage.",
        "Move every valid matching garage SavedCar into the same LiveryLab-managed vault.",
        "Keep original profile paths, GUIDs and checksums in a local manifest for reactivation.",
        "Do not delete the deployment files.",
        "Do not write into AC EVO game files.",
      ]) : [
        activate ? "Copy/sync the local livery files into the ACE game folder." : "Keep synced livery files in place unless you use Remove from Game.",
        "Update the car actor references for this livery.",
        "Update the shared mechanical preset references when needed.",
        ...(activate ? [] : ["Remove matching AC EVO user garage SavedCars for this livery."]),
      ],
      backupNote: officialExternal
        ? activate
          ? "Official deployment writes only to Saved Games. A vaulted SavedCar is restored without overwriting another valid garage file; existing generated runtime texture cache files may be removed so EVO can regenerate them."
          : "Official deactivation moves the ExternalLivery deployment and matching SavedCar into a LiveryLab-managed disabled folder. A failed partial move is rolled back."
        : "Actor and mechanical preset files are backed up before overwrite when they change.",
      runtimeGuard: officialExternal ? runtimeGuard : null,
      confirmLabel: actionLabel,
    });
    if (!confirmed) return;
    state.mySkinsActionBusy = true;
    state.mySkinsActionBusyLabel = activate ? "Activating..." : "Deactivating...";
    renderMySkins();
    try {
      const apiName = officialExternal
        ? (activate ? "activate_official_external_project" : "deactivate_official_external_project")
        : "set_my_skin_active";
      log(`${t("log_running")} ${apiName}...`);
      const officialInfo = officialExternalInfo(selected);
      const officialCarGuid = String(selected.car_guid || officialInfo.carGuid || officialInfo.linkedSavedCarGuid || "").trim();
      const result = officialExternal
        ? activate
          ? await window.pywebview.api.activate_official_external_project({ skinKey: selected.key, carGuid: officialCarGuid, refreshManifest: true })
          : await window.pywebview.api.deactivate_official_external_project({ skinKey: selected.key })
        : await window.pywebview.api.set_my_skin_active({ skinKey: selected.key, active: activate, bypassLiveryLabSafety: state.liveryLabSafetyBypassed });
      showResult(result, apiName, apiName);
      if (!result.ok) return;
      if (activate && !officialExternal) {
        await syncSafetyBypassAfterProtectedWrite(result, selected.game_root || "");
      }
      setInspectOutputText(result.message || "");
      clearMySkinThumbnailCache();
      state.mySkinsActionBusyLabel = activate ? "Applying..." : "Deactivating...";
      renderMySkins();
      await loadMySkins();
    } finally {
      state.mySkinsActionBusy = false;
      state.mySkinsActionBusyLabel = "";
      renderMySkins();
    }
  });
  $("removeMySkin")?.addEventListener("click", async () => {
    const label = selected.display_name || selected.skin_id || "this entry";
    const result = await showConfirmModal({
      title: "Remove from library",
      message: `Remove "${label}" from the My Skins library only?`,
      details: "The actual files will remain on disk.",
      confirmLabel: "Remove",
      loadingLabel: "Removing...",
      onConfirm: () => {
        const payload = { skinKey: selected.key };
        if (!isOfficialExternalSkin(selected)) {
          payload.bypassLiveryLabSafety = state.liveryLabSafetyBypassed;
        }
        return call("remove_my_skin", payload);
      },
    });
    if (!result) return;
    if (!result.ok) return;
    setInspectOutputText(result.message || "");
    if (state.mySkinSelectedKey === selected.key) {
      state.mySkinSelectedKey = "";
    }
    clearMySkinThumbnailCache();
    await loadMySkins();
    if (isOfficialExternalSkin(selected)) {
      await refreshOfficialDeletedLiveries({ silent: true });
    }
  });
}

async function loadMySkins() {
  state.mySkinsLoading = true;
  renderMySkins();
  try {
    const scope = normalizeWorkflowScope(state.workflowScope);
    const result = await window.pywebview.api.get_my_skins({
      root: $("rootPath").value.trim(),
      includeHidden: false,
      scope,
    });
    if (!result.ok) return;
    state.mySkins = result.data.skins || [];
    state.mySkinsLoaded = true;
    state.mySkinsLoadedRoot = normalizeRootPath($("rootPath").value.trim());
    state.mySkinsLoadedScope = scope;
    await loadLiveryCategories();
    syncLiveryPreviewGameUpdateAction();
  } finally {
    state.mySkinsLoading = false;
    renderMySkins();
  }
}

window.LiveryLabAppRefresh = {
  async afterLibraryImport() {
    clearMySkinThumbnailCache();
    await loadMySkins();
  },
};

function dependencyDisplayName(row = {}, fallback = "-") {
  return String(row.label || row.name || row.displayName || row.id || fallback || "-").trim() || "-";
}

function mySkinExportDependencyNotes(selected = {}, context = {}) {
  const notes = [];
  const tyreBrand = context.tyreBrand && typeof context.tyreBrand === "object" ? context.tyreBrand : {};
  const tyreSelection = String(context.tyreSelection || tyreBrand.selection || "default").toLowerCase();
  const tyreRefs = Array.isArray(tyreBrand.refs) ? tyreBrand.refs.map((ref) => String(ref || "")) : [];
  const looksCustomTyre = tyreSelection === "custom"
    || /^racing_[a-z0-9_]+_1$/.test(tyreSelection)
    || tyreRefs.some((ref) => /common_assets[\\/]parts[\\/]tyres[\\/]textures[\\/]racing_/i.test(ref));
  if (tyreSelection !== "default" && looksCustomTyre) {
    notes.push(`Custom tyre brand "${context.tyreCurrentLabel || tyreBrand.label || tyreSelection}" will be embedded when it is a local LiveryLab brand.`);
  }

  const bake = context.numberplateBake && typeof context.numberplateBake === "object" ? context.numberplateBake : {};
  const bakedSurfaceCount = Array.isArray(bake.surfaces) ? bake.surfaces.length : 0;
  const templateId = String(context.numberplateTemplateId || bake.template_id || "").trim();
  if (bakedSurfaceCount) {
    notes.push(`Baked numberplates${templateId ? ` from "${templateId}"` : ""} will be included; the original template is only needed for later start-number editing.`);
  }

  const driverProfile = selected?.driver_profile && typeof selected.driver_profile === "object" ? selected.driver_profile : {};
  if (String(driverProfile.selection || "").toLowerCase() === "profile" && String(driverProfile.profileId || driverProfile.id || "").trim()) {
    const driverLabel = driverProfile.label || driverProfile.name || driverProfile.profileId || driverProfile.id;
    notes.push(`Driver profile "${driverLabel}" will be embedded and imported if missing on another setup.`);
  }

  if (!notes.length) {
    notes.push("No custom tyre brand, baked numberplate, or driver profile dependencies detected for this livery.");
  }
  return notes;
}

function packageDependencyDetailLines(info = {}) {
  const dependencyStatus = info.dependencyStatus || {};
  const tyreDeps = Array.isArray(dependencyStatus.tyreBrands) ? dependencyStatus.tyreBrands : [];
  const plateDeps = Array.isArray(dependencyStatus.numberplateTemplates) ? dependencyStatus.numberplateTemplates : [];
  const driverDeps = Array.isArray(dependencyStatus.driverProfiles) ? dependencyStatus.driverProfiles : [];
  const missing = Array.isArray(dependencyStatus.missing) ? dependencyStatus.missing : [];
  const lines = [];
  if (tyreDeps.length) {
    lines.push("", "Tyre brand dependencies:");
    tyreDeps.forEach((row) => {
      const status = row.installed
        ? "already installed, will not be overwritten"
        : row.embedded
          ? "embedded, will be imported if missing"
          : "missing";
      lines.push(`- ${dependencyDisplayName(row)}: ${status}`);
    });
  } else {
    lines.push("", "Tyre brand dependencies: none");
  }
  if (plateDeps.length) {
    lines.push("", "Numberplate dependencies:");
    plateDeps.forEach((row) => {
      const name = dependencyDisplayName(row);
      const bakedCount = Number(row.bakedFileCount || 0);
      const missingBaked = Array.isArray(row.missingBakedFiles) ? row.missingBakedFiles.length : 0;
      let status = row.installed
        ? "template installed, editing available"
        : bakedCount
          ? "baked files included, editing locked until template is installed"
          : "template missing";
      if (missingBaked) status = `${status}, ${missingBaked} baked files missing`;
      lines.push(`- ${name}: ${status}`);
    });
  } else {
    lines.push("", "Numberplate dependencies: none");
  }
  if (driverDeps.length) {
    lines.push("", "Driver profile dependencies:");
    driverDeps.forEach((row) => {
      const status = row.installed
        ? "profile installed"
        : row.embedded
          ? row.projectEmbedded
            ? "profile and project embedded, will be imported if missing"
            : "profile embedded, will be imported if missing"
          : "profile missing; install or recreate before activation";
      lines.push(`- ${dependencyDisplayName(row)}: ${status}`);
    });
  } else {
    lines.push("", "Driver profile dependencies: none");
  }
  if (missing.length) {
    lines.push("", "Missing dependencies:");
    missing.forEach((row) => {
      const name = dependencyDisplayName(row);
      lines.push(`- ${row.kind || "dependency"} ${name}: ${row.reason || "missing"}`);
    });
  }
  return lines;
}

function showMySkinPackageExportSummary(result = {}) {
  const data = result.data || {};
  const lines = [
    result.message || "Livery package exported.",
    "",
    `Mode: ${data.packageMode === "editable_project" ? "Editable Project" : "Read-only"}`,
    `Package: ${data.packagePath || data.packageRoot || "-"}`,
    `Entries: ${Number(data.copiedCount || 0)}`,
    ...(data.project?.included ? [`Project files: ${Number(data.project.fileCount || 0)}`] : []),
    `Missing package files: ${Number(data.missingCount || 0)}`,
    ...packageDependencyDetailLines(data),
  ];
  const hasMissing = Number(data.missingCount || 0) > 0 || Number(data.dependencyStatus?.missingCount || 0) > 0;
  showStatusModal({
    type: hasMissing ? "warning" : "success",
    title: "Livery Package Export",
    subtitle: hasMissing ? "Export completed with warnings" : "Package ready",
    message: hasMissing
      ? "The package was exported, but some files or dependencies need attention."
      : "The package was exported with its required portable dependencies.",
    details: lines.join("\n"),
    openFolderPath: data.packagePath || data.packageRoot || "",
  });
  setInspectOutputText(lines.join("\n"));
}

function renderMySkinPackageSummary() {
  const pre = $("mySkinPackageSummary");
  if (!pre) return;
  const info = state.mySkinPackageInfo;
  if (!info) {
    pre.textContent = "";
    return;
  }
  const skin = info.skin || {};
  const manifest = info.manifest || {};
  const dependencyStatus = info.dependencyStatus || {};
  const tyreDeps = Array.isArray(dependencyStatus.tyreBrands) ? dependencyStatus.tyreBrands : [];
  const plateDeps = Array.isArray(dependencyStatus.numberplateTemplates) ? dependencyStatus.numberplateTemplates : [];
  const driverDeps = Array.isArray(dependencyStatus.driverProfiles) ? dependencyStatus.driverProfiles : [];
  pre.textContent = [
    `Package: ${info.packagePath || info.packageRoot || "-"}`,
    `Skin: ${skin.displayName || skin.skinId || "-"}`,
    `Car: ${skin.carModel || "-"}`,
    `Project type: ${manifest.projectType || skin.projectType || "-"}`,
    `Package mode: ${manifest.packageMode || "read_only"}`,
    `Manifest: ${info.manifestPath || "-"}`,
    `Entries: ${Number(info.copiedCount || 0)}`,
    `Missing in package: ${Number(info.missingCount || 0)}`,
    `Tyre brand dependencies: ${tyreDeps.length || 0}`,
    `Numberplate dependencies: ${plateDeps.length || 0}`,
    `Driver profile dependencies: ${driverDeps.length || 0}`,
    ...(dependencyStatus.missingCount ? [`Missing dependencies: ${dependencyStatus.missingCount}`] : []),
    ...packageDependencyDetailLines(info),
    "Import target: My Liveries local library",
    "Game activation: not automatic",
  ].join("\n");
}

async function installMySkinPackage() {
  const dir = String(state.mySkinPackageDir || $("mySkinPackageDir")?.value || "").trim();
  if (!dir) {
    log("Please select a package ZIP first", true);
    return;
  }
  await withButtonWorking("installMySkinPackage", "Importing...", async () => {
    log(`${t("log_running")} import_livery_package_smart...`);
    let migrationInfo = null;
    if (normalizeWorkflowScope(state.workflowScope) === "official") {
      const inspected = await window.pywebview.api.inspect_advanced_package_official_migration({
        root: $("rootPath").value.trim(),
        packagePath: dir,
      });
      if (inspected.ok && ["advanced", "advanced_extracted_content", "legacy", "legacy_advanced"].includes(String(inspected.data?.projectType || "").toLowerCase())) {
        migrationInfo = inspected.data;
        if (!migrationInfo.ready) {
          showStatusModal({
            type: "error",
            title: "Official package migration blocked",
            message: "This Advanced package cannot be converted into an Official project.",
            details: [...(migrationInfo.blockers || []), ...(migrationInfo.warnings || [])].join("\n"),
          });
          return;
        }
        const duplicateStatus = migrationInfo.duplicateStatus || {};
        if (duplicateStatus.status === "exact") {
          const existing = duplicateStatus.existingEntry || {};
          await loadMySkins();
          if (existing.key) {
            state.mySkinSelectedKey = existing.key;
            renderMySkins();
          }
          showStatusModal({
            type: "info",
            title: "Already installed - no changes made",
            subtitle: existing.displayName || migrationInfo.displayName || "Existing Official project",
            message: "This exact Advanced livery package is already available in My Liveries.",
            details: [
              `Car: ${existing.carModel || migrationInfo.carModel || "-"}`,
              `Existing project: ${existing.projectFolder || "-"}`,
              "No duplicate project was created.",
            ].join("\n"),
          });
          setTab("library");
          return;
        }
        const variant = migrationInfo.officialResources?.defaultVehicleVariant || {};
        const relatedChanged = duplicateStatus.status === "related_changed";
        const confirmed = await showConfirmModal({
          title: relatedChanged ? "Install changed livery as copy" : "Import Advanced package as Official",
          message: relatedChanged
            ? `A related Official project already exists. Install the changed "${migrationInfo.displayName || migrationInfo.skin?.skinId || "Legacy livery"}" as a separate copy?`
            : `Create a new Official project from "${migrationInfo.displayName || migrationInfo.skin?.skinId || "Legacy livery"}"?`,
          details: [
            `Car: ${migrationInfo.carModel || "-"}`,
            `Official variant: ${variant.displayLabel || variant.label || "-"}`,
            `Texture slots: ${Number(migrationInfo.readySlotCount || 0)}`,
            `Layer Studio: ${migrationInfo.layerStudioAvailable ? "copy compatible surfaces" : "not included"}`,
            "",
            "Advanced-only data will be removed:",
            ...(migrationInfo.droppedFeatures || []).map((row) => `- ${row.field || "Advanced metadata"}`),
            ...((migrationInfo.dependencyCounts && Object.values(migrationInfo.dependencyCounts).some(Number)) ? ["- Bundled dependency packages"] : []),
            ...(migrationInfo.manualTextureReview ? ["", "Manual review required: numberplate graphics may already be baked into EXT_Skin_C."] : []),
            ...(relatedChanged ? ["", "The existing project will not be overwritten."] : []),
            "",
            "The source ZIP remains unchanged. Game activation is not automatic.",
          ].join("\n"),
          confirmLabel: relatedChanged ? "Install as Copy" : "Create Official Project",
        });
        if (!confirmed) return;
      }
    }
    const result = await window.pywebview.api.import_livery_package_smart({
      root: $("rootPath").value.trim(),
      packagePath: dir,
      workflowScope: normalizeWorkflowScope(state.workflowScope),
    });
    if (!result.ok) {
      showResult(result, "Livery package import failed", "import_livery_package_smart");
      return;
    }
    if (result.message) log(result.message);
    const data = result.data || {};
    const entry = data.mySkinEntry || data.entry || {};
    const targetScope = normalizeWorkflowScope(data.targetScope || state.workflowScope);
    if (targetScope !== normalizeWorkflowScope(state.workflowScope)) await setMySkinsWorkflowScope(targetScope);
    else await loadMySkins();
    if (entry.key) {
      state.mySkinSelectedKey = entry.key;
      renderMySkins();
    }
    const dependencies = data.dependencies || {};
    const migrated = data.route === "advanced_to_official";
    const convertedToAdvanced = data.route === "official_to_advanced";
    const alreadyInstalled = data.status === "already_installed";
    if (alreadyInstalled) {
      showStatusModal({
        type: "info",
        title: "Already installed - no changes made",
        subtitle: entry.display_name || entry.skin_id || "Existing Official project",
        message: "This exact livery package is already available in My Liveries.",
        details: [
          `Car: ${entry.car_model || "-"}`,
          `Existing project: ${entry.project_folder || entry.local_root || "-"}`,
          "No duplicate project was created.",
        ].join("\n"),
      });
      setInspectOutputText("Livery already installed; no changes made.");
      setTab("library");
      return;
    }
    showStatusModal({
      type: "success",
      title: migrated ? "Official Project Created" : (convertedToAdvanced ? "Advanced Project Created" : "Livery Package Imported"),
      subtitle: "Import complete",
      message: entry.display_name || entry.skin_id || (migrated ? "Official project imported" : (convertedToAdvanced ? "Official package converted to Advanced" : "Skin package imported to local library")),
      details: [
        `Package: ${data.packagePath || data.packageRoot || "-"}`,
        `Workflow: ${migrated ? "Advanced package to Official project" : (convertedToAdvanced ? "Official package to Advanced project" : (data.route || "standard import"))}`,
        `${data.projectFolder ? "Project folder" : "Local library folder"}: ${data.projectFolder || data.localRoot || "-"}`,
        ...(migrated ? [
          `Imported texture slots: ${(data.migration?.importedSlots || []).length}`,
          `Dropped Advanced features: ${(data.migration?.droppedFeatures || []).length}`,
          data.migration?.variantFallback?.used
            ? `Vehicle variant fallback: ${data.migration.variantFallback.requestedVariantId || "Advanced variant"} -> ${data.migration.variantFallback.selectedVariantId || "neutral Official variant"}`
            : "",
          ...(data.migration?.fidelityWarnings || []).map((warning) => `Material note: ${warning}`),
        ] : convertedToAdvanced ? [
          `Artwork: ${data.conversionPolicy?.artworkLocked ? "locked baked Official base" : "editable Advanced project"}`,
          `Imported texture slots: ${Number(data.conversion?.artwork?.copiedCount || 0)}`,
          `Layer Studio sources: ${data.conversion?.layerStudioSources?.copied ? `${Number(data.conversion.layerStudioSources.fileCount || 0)} files` : "not included"}`,
        ] : [
          `Imported files: ${Number(data.importedCount || 0)}`,
          `Missing package entries: ${Number(data.missingSourceCount || 0)}`,
          `Dependency imports: ${Number(dependencies.importedCount || 0)}`,
          `Dependencies already installed: ${Number(dependencies.alreadyInstalledCount || 0)}`,
          `Missing dependencies: ${Number(dependencies.missingCount || 0)}`,
        ]),
        "Game activation: not automatic",
      ].filter(Boolean).join("\n"),
    });
    setInspectOutputText(result.message || "");
    setTab("library");
  });
}

function openSingleLiveryImport() {
  setTab("my-skin-packages");
  window.setTimeout(() => {
    const card = $("singleLiveryImportCard");
    const pickButton = $("pickMySkinPackageDir");
    card?.classList.remove("livery-import-highlight");
    if (card) {
      void card.offsetWidth;
      card.classList.add("livery-import-highlight");
      card.scrollIntoView({ block: "center", behavior: "smooth" });
      window.setTimeout(() => card.classList.remove("livery-import-highlight"), 3200);
    }
    pickButton?.focus({ preventScroll: true });
    const message = "Choose a downloaded LiveryLab package ZIP, then click Import package.";
    log(message);
    setInspectOutputText(message);
  }, 80);
}

async function exportSelectedMySkinPackage(selected, { showSummary = true, packageMode = "read_only" } = {}) {
  const skin = selected || (state.mySkins || []).find((row) => row.key === state.mySkinSelectedKey);
  if (!skin) {
    log("Kein My-Skin-Eintrag ausgewaehlt", true);
    return null;
  }
  if (!mySkinCan(skin, "canExportPackage")) {
    showMySkinReadOnlyBlocked(skin, "Package export");
    return null;
  }
  const outDir = mySkinPackageExportDirForSkin(skin);
  if (!outDir) {
    log("Please select a target folder for the export package first", true);
    return null;
  }
  const result = await confirmFreeOutputIfInGameRoot({
    target: outDir,
    title: "Export package inside AC EVO installation folder",
    message: "The selected package export target is inside the AC EVO installation folder. Continue?",
    writes: ["Create a portable LiveryLab ZIP package."],
    backupNote: "Package export may replace an existing ZIP with the same name.",
    confirmLabel: "Export",
    loadingLabel: "Exporting...",
    onConfirm: () => call("export_my_skin_package", {
      root: $("rootPath").value.trim(),
      skinKey: skin.key,
      outDir,
      packageMode,
    }),
  });
  if (!result) return null;
  if (!result.ok) return result;
  if (showSummary) showMySkinPackageExportSummary(result);
  return result;
}

function mySkinPackageExportDirForSkin(skin = {}) {
  const value = isOfficialExternalSkin(skin)
    ? state.officialMySkinExportDir
    : state.mySkinExportDir;
  return String(value || "").trim();
}

function setMySkinPackageExportDirForSkin(skin = {}, value = "") {
  const cleanValue = String(value || "").trim();
  if (isOfficialExternalSkin(skin)) {
    state.officialMySkinExportDir = cleanValue;
  } else {
    state.mySkinExportDir = cleanValue;
  }
  return cleanValue;
}

async function openMySkinExportPackageModal(selected, dependencyNotes = []) {
  const details = [
    "Package contents:",
    ...(dependencyNotes.length ? dependencyNotes.map((line) => `- ${line}`) : ["- No custom dependencies detected."]),
  ].join("\n");
  const result = await showInputModal({
    title: t("export_package_title"),
    message: t("export_package_hint"),
    label: t("target_folder"),
    value: mySkinPackageExportDirForSkin(selected),
    details,
    confirmLabel: "Export Read-only Package",
    alternateLabel: "Export Editable Project Package",
    hideCancel: true,
    modalClass: "export-package",
    loadingLabel: "Exporting...",
    onConfirm: async (outDir) => {
      const targetDir = setMySkinPackageExportDirForSkin(selected, outDir);
      if (!targetDir) {
        throw new Error("Please select a target folder for the export package first.");
      }
      if (pathLooksInsideRoot(targetDir)) {
        throw new Error("Choose a package export folder outside the ACE game files.");
      }
      return exportSelectedMySkinPackage(selected, { showSummary: false, packageMode: "read_only" });
    },
    onAlternate: async (outDir) => {
      const targetDir = setMySkinPackageExportDirForSkin(selected, outDir);
      if (!targetDir) {
        throw new Error("Please select a target folder for the export package first.");
      }
      if (pathLooksInsideRoot(targetDir)) {
        throw new Error("Choose a package export folder outside the ACE game files.");
      }
      return exportSelectedMySkinPackage(selected, { showSummary: false, packageMode: "editable_project" });
    },
  });
  if (!result) return;
  if (!result.ok) {
    showResult(result, "Livery package export failed", "export_my_skin_package");
    return;
  }
  showMySkinPackageExportSummary(result);
}

async function trashSelectedMySkin(selected) {
  const label = selected.display_name || selected.skin_id || "this skin";
  if (isOfficialExternalSkin(selected)) {
    const result = await showConfirmModal({
      type: "danger",
      title: "Remove official livery",
      message: `Remove "${label}" from My Liveries?`,
      details: [
        "Remove action:",
        "- Deactivate the livery in Saved Games first.",
        "- Move its ExternalLiveries deployment and matching SavedCar into LiveryLab's local disabled vault.",
        "- Move the local Official project/library folder into the Official trash.",
        "- Abort removal if the Saved Games deactivation cannot be completed safely.",
      ].join("\n"),
      confirmLabel: "Remove",
      loadingLabel: "Removing...",
      onConfirm: () => window.pywebview.api.remove_my_skin({ skinKey: selected.key }),
    });
    if (!result) return;
    showResult(result, "remove_my_skin", "remove_my_skin");
    if (!result.ok) return;
    if (state.mySkinSelectedKey === selected.key) {
      state.mySkinSelectedKey = "";
    }
    clearMySkinThumbnailCache();
    await loadMySkins();
    await refreshOfficialDeletedLiveries({ silent: true });
    setInspectOutputText(result.message || "");
    return;
  }
  const project = await hydrateLiveryProjectTrashOption(selected.key);
  const policy = mySkinPolicy(selected);
  const canTrashLiveryProject = policy.editable === true && policy.projectState === "editable";
  const hasProject = Boolean(canTrashLiveryProject && project?.exists);
  const localMigrationTrash = isInactiveMigratedAdvancedCopy(selected);
  const runTrash = async () => {
    log(`${t("log_running")} trash_my_skin...`);
    return window.pywebview.api.trash_my_skin({
      skinKey: selected.key,
      includeLiveryProject: hasProject,
      bypassLiveryLabSafety: state.liveryLabSafetyBypassed,
    });
  };
  const choice = localMigrationTrash
    ? await showConfirmModal({
      type: "danger",
      title: "Remove migrated Advanced copy",
      message: `Remove the inactive migrated copy of "${label}" from this LiveryLab installation?`,
      details: [
        "Only files inside this LiveryLab installation are moved to the LiveryLab trash.",
        "AC EVO game files and garage SavedCars are not changed.",
        ...(hasProject ? ["The complete migrated LiveryProject is included and can be restored from Deleted Liveries."] : []),
      ].join("\n"),
      confirmLabel: "Move to Trash",
      loadingLabel: "Moving local copy...",
      onConfirm: runTrash,
    })
    : await confirmGameWriteAction({
      type: "danger",
      title: t("move_livery_to_trash"),
      message: `Remove "${label}" from My Liveries? The livery will disappear from LiveryLab, and matching game files and garage SavedCars will be cleaned up.`,
      writes: [
        "Remove this entry from the My Liveries library.",
        "Deactivate actor and mechanical references.",
        "Remove matching AC EVO user garage SavedCars for this livery.",
        "Remove related localization entries.",
        "Move skin-specific game files to the LiveryLab trash.",
        ...(hasProject ? ["Move the complete LiveryProject folder to the LiveryLab trash, including PSDs, source files and Layer Studio data."] : []),
      ],
      backupNote: hasProject
        ? "Project files are moved into the LiveryLab trash so Restore can bring the editable project back later. Shared game files like actor, mechanical preset and LOC are backed up before overwrite when they change."
        : "Shared files like actor, mechanical preset and LOC are backed up before overwrite when they change.",
      confirmLabel: "Remove from App",
      loadingLabel: "Moving to trash...",
      onConfirm: runTrash,
    });
  const result = choice;
  if (!result) return;
  showResult(result, "trash_my_skin", "trash_my_skin");
  if (!result.ok) return;
  if (state.mySkinSelectedKey === selected.key) {
    state.mySkinSelectedKey = "";
  }
  clearMySkinThumbnailCache();
  await loadMySkins();
  await refreshDeletedLiveries({ silent: true });
  if (result.data?.trashDir) {
    setInspectOutputText([
      result.message || "",
      "",
      `Trash: ${result.data.trashDir}`,
      "",
      "Moved:",
      ...((result.data.moved || []).map((row) => `- ${row}`)),
      ...(result.data.liveryProject?.moved ? ["", "LiveryProject:", `- ${result.data.liveryProject.relative || result.data.liveryProject.path}`] : []),
      ...(result.data.liveryProject?.warning ? ["", "LiveryProject warning:", `- ${result.data.liveryProject.warning}`] : []),
      ...((result.data.missing || []).length ? ["", "Missing:", ...result.data.missing.map((row) => `- ${row}`)] : []),
    ].join("\n"));
  }
}

async function openMySkinLiveryTools(selected) {
  await trashSelectedMySkin(selected);
}

function checkboxValue(id, fallback = false) {
  const input = $(id);
  return input ? Boolean(input.checked) : Boolean(fallback);
}

function skinIdFromLiveryName(value) {
  const slug = String(value || "")
    .normalize("NFKD")
    .replace(/[\u0300-\u036f]/g, "")
    .toLowerCase()
    .replace(/ß/g, "ss")
    .replace(/&/g, " and ")
    .replace(/[^a-z0-9]+/g, "_")
    .replace(/^_+|_+$/g, "")
    .replace(/_+/g, "_");
  return (slug.slice(0, LIVERY_TECHNICAL_ID_MAX_LENGTH).replace(/^_+|_+$/g, "")) || "new_skin";
}

function shortCarSlug(value) {
  let slug = skinIdFromLiveryName(value).replace(/^ks_/, "");
  if (slug.length <= LIVERY_CAR_SLUG_MAX_LENGTH) return slug;
  const parts = slug.split("_").filter(Boolean);
  for (const count of [4, 3, 2]) {
    const candidate = parts.slice(-count).join("_");
    if (candidate && candidate.length <= LIVERY_CAR_SLUG_MAX_LENGTH) return candidate;
  }
  return slug.slice(0, LIVERY_CAR_SLUG_MAX_LENGTH).replace(/^_+|_+$/g, "") || "car";
}

function officialExternalFolderPreview(carModel, skinId) {
  const prefix = `${shortCarSlug(carModel || "car")}__`;
  const room = Math.max(8, OFFICIAL_EXTERNAL_FOLDER_MAX_LENGTH - prefix.length);
  const skin = skinIdFromLiveryName(skinId).slice(0, room).replace(/^_+|_+$/g, "") || "skin";
  return `${prefix}${skin}`;
}

function currentSkinIdFromInputs() {
  const override = $("skinId")?.value.trim() || "";
  const name = $("skinName")?.value.trim() || "";
  return override ? skinIdFromLiveryName(override) : skinIdFromLiveryName(name);
}

function currentSkinProjectType() {
  const selected = document.querySelector('input[name="skinProjectType"]:checked');
  return String(selected?.value || "advanced_extracted_content").trim() || "advanced_extracted_content";
}

function skinCreatorUsesOfficialProject() {
  return currentSkinProjectType() === "official_external_livery";
}

function updateSkinIdPreview() {
  const preview = $("skinIdPreview");
  const skinId = currentSkinIdFromInputs();
  if (preview) {
    preview.textContent = skinCreatorUsesOfficialProject()
      ? `Skin ID: ${skinId} / External folder: ${officialExternalFolderPreview($("officialProjectCarModel")?.value.trim() || "", skinId)}`
      : `Skin ID: ${skinId}`;
  }
  return skinId;
}

function updateSkinProjectTypeMode() {
  const official = skinCreatorUsesOfficialProject();
  const projectChoice = document.querySelector(".skin-project-choice");
  const templateStart = document.querySelector(".skin-template-start");
  const advancedTemplate = $("skinTemplateAdvanced");
  const advancedFields = $("advancedCreatorFields");
  const officialFields = $("officialExternalProjectFields");
  const identityGrid = document.querySelector(".skin-identity-grid");
  const identityAnchor = $("skinIdentityAnchor");
  const advancedIdentityMount = $("advancedCreatorIdentityMount");
  const officialIdentityMount = $("officialCreatorIdentityMount");
  const actions = $("skinCreatorActions");
  const actionsAnchor = $("skinCreatorActionsAnchor");
  const advancedActionsMount = $("advancedCreatorActionsMount");
  const officialActionsMount = $("officialCreatorActionsMount");
  const texconvStatusRow = $("texconvStatusRow");
  const authorInput = $("skinAuthor");
  const descriptionInput = $("skinDescription");
  if (projectChoice) projectChoice.hidden = true;
  if (templateStart) templateStart.hidden = true;
  if (advancedTemplate && official) advancedTemplate.hidden = true;
  if (advancedFields) advancedFields.hidden = official;
  if (officialFields) officialFields.hidden = !official;
  if (texconvStatusRow) texconvStatusRow.hidden = true;
  if (authorInput) authorInput.placeholder = official ? "Optional official author credit" : "Optional";
  if (descriptionInput) descriptionInput.placeholder = official ? "Optional official project note" : "Optional local project note";
  if (identityGrid && identityAnchor && advancedIdentityMount && officialIdentityMount) {
    if (official && identityGrid.parentElement !== officialIdentityMount) {
      officialIdentityMount.appendChild(identityGrid);
    } else if (!official && identityGrid.parentElement !== advancedIdentityMount) {
      advancedIdentityMount.appendChild(identityGrid);
    }
    identityGrid.classList.toggle("official-creator-identity", official);
    identityGrid.classList.toggle("advanced-creator-identity", !official);
  }
  if (actions && actionsAnchor && advancedActionsMount && officialActionsMount) {
    if (official && actions.parentElement !== officialActionsMount) {
      officialActionsMount.appendChild(actions);
    } else if (!official && actions.parentElement !== advancedActionsMount) {
      advancedActionsMount.appendChild(actions);
    }
    actions.classList.toggle("official-creator-actions", official);
    actions.classList.toggle("advanced-creator-actions", !official);
  }
  if ($("toggleSkinTemplateAdvanced")) $("toggleSkinTemplateAdvanced").hidden = official || !debugToolEnabled("skinCreatorAdvanced");
  if ($("createPreviewDds")) $("createPreviewDds").closest("label")?.toggleAttribute("hidden", official);
  setIconButtonLabel($("createSkin"), official ? "Create official project" : t("create_skin"), "plus");
  if (official) renderOfficialCreatorCars(state.officialCarResourcesStatus);
  else renderAdvancedCreatorCars(state.carCompatibility);
  updateSkinIdPreview();
}

function payloadSkin({ overwrite = false } = {}) {
  const templatePayload = NUMBERPLATE_SKIN_TEMPLATE_WORKFLOW_ENABLED ? selectedNumberplateTemplatePayload("skinNumberplateTemplate") : null;
  const hasNumberplateTemplate = NUMBERPLATE_SKIN_TEMPLATE_WORKFLOW_ENABLED && Boolean(templatePayload?.templateId);
  const skinName = $("skinName").value.trim().slice(0, LIVERY_DISPLAY_NAME_MAX_LENGTH).trim();
  const skinId = currentSkinIdFromInputs();
  return {
    root: $("rootPath").value.trim(),
    cardesign: $("skinCardesign").value.trim(),
    design: $("skinDesign").value.trim(),
    actor: $("skinActor").value.trim(),
    visual: $("skinVisual").value.trim(),
    mechanical: $("skinMechanical").value.trim(),
    carfinalstate: $("skinCarFinalState").value.trim(),
    numberplateTemplateId: NUMBERPLATE_SKIN_TEMPLATE_WORKFLOW_ENABLED ? (templatePayload?.templateId || "") : "",
    numberplateMode: NUMBERPLATE_SKIN_TEMPLATE_WORKFLOW_ENABLED ? (templatePayload?.mode || "") : "",
    numberplateFont: NUMBERPLATE_SKIN_TEMPLATE_WORKFLOW_ENABLED ? (templatePayload?.fontPath || $("skinNumberplateFont").value.trim()) : "",
    numberplateBackground: NUMBERPLATE_SKIN_TEMPLATE_WORKFLOW_ENABLED ? (templatePayload?.backgroundPath || $("skinNumberplateBackground").value.trim()) : "",
    numberplateHoodBackground: NUMBERPLATE_SKIN_TEMPLATE_WORKFLOW_ENABLED ? (templatePayload?.hoodBackgroundPath || "") : "",
    numberplateLeftBackground: NUMBERPLATE_SKIN_TEMPLATE_WORKFLOW_ENABLED ? (templatePayload?.leftBackgroundPath || "") : "",
    numberplateRightBackground: NUMBERPLATE_SKIN_TEMPLATE_WORKFLOW_ENABLED ? (templatePayload?.rightBackgroundPath || "") : "",
    numberplateTextColor: NUMBERPLATE_SKIN_TEMPLATE_WORKFLOW_ENABLED ? (templatePayload?.textColor || null) : null,
    numberplateStartNumber: hasNumberplateTemplate ? "000" : "",
    skinId,
    skinName,
    author: $("skinAuthor")?.value.trim() || "",
    description: ($("skinDescription")?.value.trim() || "").slice(0, LIVERY_DESCRIPTION_MAX_LENGTH).trim(),
    liveryTemplate: selectedCreatorTemplatePayload("advanced"),
    previewImage: "",
    copySource: checkboxValue("copySource", true),
    patchActor: checkboxValue("patchActor", true),
    createVisual: checkboxValue("createVisual", true) || hasNumberplateTemplate,
    patchVisualActor: checkboxValue("patchVisualActor", true) || hasNumberplateTemplate,
    updateMechanical: checkboxValue("updateMechanical", true),
    createCarFinalState: checkboxValue("createCarFinalState", true),
    createNumberplateVariant: hasNumberplateTemplate,
    overwrite: Boolean(overwrite),
  };
}

function payloadOfficialExternalProject({ overwrite = false } = {}) {
  const skinName = $("skinName").value.trim().slice(0, LIVERY_DISPLAY_NAME_MAX_LENGTH).trim();
  const vehicleVariant = selectedOfficialVehicleVariant();
  const selectedCar = selectedOfficialCreatorCar();
  const thumbnailPath = selectedCar?.thumbnailExists ? String(selectedCar.thumbnailPath || "").trim() : "";
  return {
    root: $("rootPath").value.trim(),
    liveryName: skinName,
    carModel: $("officialProjectCarModel")?.value.trim() || "",
    externalFolderName: "",
    carGuid: "",
    author: $("skinAuthor")?.value.trim() || "",
    description: ($("skinDescription")?.value.trim() || "").slice(0, LIVERY_DESCRIPTION_MAX_LENGTH).trim(),
    liveryTemplate: { mode: "blank", startPointId: "blank", name: "Blank livery" },
    thumbnail: thumbnailPath,
    vehicleVariant: vehicleVariant ? { ...vehicleVariant, label: officialVehicleVariantLabel(vehicleVariant) } : null,
    savedCarPath: "",
    overwrite: Boolean(overwrite),
  };
}

function uiIcon(name) {
  const icons = {
    home: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 11.5 12 4l9 7.5"/><path d="M5.5 10.5V20h13v-9.5"/><path d="M9.5 20v-6h5v6"/></svg>',
    brush: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M14 4 20 10 9 21H3v-6L14 4Z"/><path d="m12 6 6 6"/></svg>',
    edit: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 20h4l11-11-4-4L4 16v4Z"/><path d="m13 7 4 4"/></svg>',
    library: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 4h14v16H5z"/><path d="M8 8h8"/><path d="M8 12h8"/><path d="M8 16h5"/></svg>',
    texture: '<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="4" y="4" width="16" height="16" rx="1"/><path d="M4 12h16"/><path d="M12 4v16"/><path d="M4 8h4"/><path d="M16 16h4"/></svg>',
    mesh: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 18 12 4l8 14H4Z"/><path d="M8 18 12 4l4 14"/><path d="M7 13h10"/></svg>',
    uv: '<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="4" y="4" width="16" height="16"/><path d="M4 12h16"/><path d="M12 4v16"/><path d="M16 20l4-4"/></svg>',
    material: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3a9 9 0 0 0 0 18h1.5a2 2 0 0 0 1.4-3.4 1.8 1.8 0 0 1 1.3-3.1H18a3 3 0 0 0 3-3A8.5 8.5 0 0 0 12 3Z"/><circle cx="8" cy="10" r="1"/><circle cx="11" cy="7" r="1"/><circle cx="15" cy="8" r="1"/></svg>',
    inspect: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="11" cy="11" r="6"/><path d="m16 16 4 4"/></svg>',
    settings: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12.2 2h-.4a2 2 0 0 0-2 1.7l-.1.8a2 2 0 0 1-3 1.5L6 5.6a2 2 0 0 0-2.7.7l-.2.4a2 2 0 0 0 .7 2.7l.7.4a2 2 0 0 1 0 3.4l-.7.4a2 2 0 0 0-.7 2.7l.2.4a2 2 0 0 0 2.7.7l.7-.4a2 2 0 0 1 3 1.5l.1.8a2 2 0 0 0 2 1.7h.4a2 2 0 0 0 2-1.7l.1-.8a2 2 0 0 1 3-1.5l.7.4a2 2 0 0 0 2.7-.7l.2-.4a2 2 0 0 0-.7-2.7l-.7-.4a2 2 0 0 1 0-3.4l.7-.4a2 2 0 0 0 .7-2.7l-.2-.4a2 2 0 0 0-2.7-.7l-.7.4a2 2 0 0 1-3-1.5l-.1-.8a2 2 0 0 0-2-1.7Z"/><circle cx="12" cy="12" r="3"/></svg>',
    info: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8"/><path d="M12 11v5"/><path d="M12 8h.01"/></svg>',
    refresh: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M20 6v5h-5"/><path d="M4 18v-5h5"/><path d="M18 9a6.5 6.5 0 0 0-10.8-2.4L4 10"/><path d="M6 15a6.5 6.5 0 0 0 10.8 2.4L20 14"/></svg>',
    target: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8"/><circle cx="12" cy="12" r="3"/><path d="M12 2v3"/><path d="M12 19v3"/><path d="M2 12h3"/><path d="M19 12h3"/></svg>',
    wheel: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="8"/><circle cx="12" cy="12" r="3"/><path d="M12 4v5"/><path d="M12 15v5"/><path d="M4 12h5"/><path d="M15 12h5"/><path d="m6.4 6.4 3.5 3.5"/><path d="m14.1 14.1 3.5 3.5"/><path d="m17.6 6.4-3.5 3.5"/><path d="m9.9 14.1-3.5 3.5"/></svg>',
    driver: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 13a7 7 0 0 1 14 0v2.5A4.5 4.5 0 0 1 14.5 20h-5A4.5 4.5 0 0 1 5 15.5V13Z"/><path d="M7 13h10"/><path d="M8 10h8"/><path d="M9 16h6"/><path d="M12 4v3"/></svg>',
    open: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 12h13"/><path d="m12 7 5 5-5 5"/><path d="M4 5v14"/></svg>',
    cube: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3 4.5 7.2v9.2L12 21l7.5-4.6V7.2L12 3Z"/><path d="m4.8 7.4 7.2 4.1 7.2-4.1"/><path d="M12 11.5v9.1"/></svg>',
    play: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M8 5v14l11-7-11-7Z"/></svg>',
    power: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3v8"/><path d="M7.1 6.8a7 7 0 1 0 9.8 0"/></svg>',
    plus: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14"/><path d="M5 12h14"/></svg>',
    copy: '<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="8" y="8" width="11" height="11"/><path d="M16 8V5H5v11h3"/></svg>',
    eye: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2.8 12s3.4-6 9.2-6 9.2 6 9.2 6-3.4 6-9.2 6-9.2-6-9.2-6Z"/><circle cx="12" cy="12" r="2.7"/></svg>',
    trash: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h16"/><path d="M9 7V4h6v3"/><path d="M7 7l1 13h8l1-13"/><path d="M10 11v5"/><path d="M14 11v5"/></svg>',
    file: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M6 3h8l4 4v14H6V3Z"/><path d="M14 3v5h5"/></svg>',
    keyboard: '<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="6" width="18" height="12" rx="1"/><path d="M7 10h.01"/><path d="M10 10h.01"/><path d="M13 10h.01"/><path d="M16 10h.01"/><path d="M7 14h10"/></svg>',
    save: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 4h12l2 2v14H5V4Z"/><path d="M8 4v6h8V4"/><path d="M8 20v-6h8v6"/></svg>',
    upload: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 16V4"/><path d="m7 9 5-5 5 5"/><path d="M5 14v5h14v-5"/></svg>',
    download: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 4v12"/><path d="m7 11 5 5 5-5"/><path d="M5 18v2h14v-2"/></svg>',
    import: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 4v10"/><path d="m7 9 5 5 5-5"/><path d="M5 16v4h14v-4"/></svg>',
    export: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 5h8v4"/><path d="M5 5v14h14v-8"/><path d="M12 12h8"/><path d="m16 8 4 4-4 4"/></svg>',
    folder: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M3 6h7l2 2h9v10.5A2.5 2.5 0 0 1 18.5 21h-13A2.5 2.5 0 0 1 3 18.5V6Z"/></svg>',
    package: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3 4 7v10l8 4 8-4V7l-8-4Z"/><path d="M4 7l8 4 8-4"/><path d="M12 11v10"/><path d="m8 5 8 4"/></svg>',
    plate: '<svg viewBox="0 0 24 24" aria-hidden="true"><rect x="3" y="7" width="18" height="10" rx="1"/><path d="M7 11h4"/><path d="M13 11h4"/><path d="M7 14h10"/></svg>',
    circle: '<svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="7"/></svg>',
    check: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m5 12 4 4 10-10"/></svg>',
    minus: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14"/></svg>',
    up: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m6 14 6-6 6 6"/><path d="M12 8v12"/></svg>',
    down: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m6 10 6 6 6-6"/><path d="M12 4v12"/></svg>',
    x: '<svg viewBox="0 0 24 24" aria-hidden="true"><path d="m6 6 12 12"/><path d="M18 6 6 18"/></svg>',
  };
  return `<span class="button-icon">${icons[name] || ""}</span>`;
}

function inferredButtonIcon(button) {
  if (!button || button.classList.contains("icon-button")) return "";
  const id = String(button.id || "").toLowerCase();
  if (button.dataset.icon) return button.dataset.icon;
  if (button.dataset.pick) return "file";
  if (button.dataset.folder) return "folder";
  if (button.dataset.save) return "target";
  if (button.dataset.openTab) return "open";
  if (id.includes("refresh") || String(button.dataset.i18n || "") === "refresh") return "refresh";
  if (id.includes("startgame")) return "play";
  if (id.includes("rename")) return "edit";
  if (id.includes("delete") || id.includes("trash") || id.includes("remove")) return "trash";
  if (id.includes("analyze") || id.includes("inspect")) return "inspect";
  if (id.includes("extract") || id.includes("package")) return "package";
  if (id.includes("save")) return "save";
  if (id.includes("export")) return "package";
  if (id.includes("load") || id.includes("open")) return "open";
  if (id.includes("useSelected".toLowerCase())) return "target";
  return "";
}

function decorateIconButtons(root = document) {
  root.querySelectorAll("button").forEach((button) => {
    const icon = inferredButtonIcon(button);
    if (!icon) return;
    button.classList.add("icon-text");
    button.querySelector(".button-icon")?.remove();
    button.insertAdjacentHTML("afterbegin", uiIcon(icon));
  });
  root.querySelectorAll(".nav-label[data-icon]").forEach((label) => {
    const icon = label.dataset.icon;
    if (!icon) return;
    label.classList.add("icon-text");
    label.querySelector(".button-icon")?.remove();
    label.insertAdjacentHTML("afterbegin", uiIcon(icon));
  });
  root.querySelectorAll(".about-feature-icon[data-icon]").forEach((icon) => {
    icon.innerHTML = uiIcon(icon.dataset.icon).replace('<span class="button-icon">', "").replace("</span>", "");
    icon.querySelector("svg")?.setAttribute("stroke", "#fff");
  });
}

window.uiIcon = uiIcon;
window.decorateIconButtons = decorateIconButtons;

function setIconButtonLabel(button, label, icon) {
  if (!button) return;
  button.dataset.icon = icon || "";
  button.classList.toggle("icon-text", Boolean(icon));
  button.innerHTML = `${icon ? uiIcon(icon) : ""}${escapeHtml(label)}`;
}

function carCompatibilityStatusText(status) {
  return t(status || "unknown");
}

function carCompatibilityPresentText(car) {
  const metrics = car?.metrics || {};
  const mesh = car?.mesh || {};
  const parts = [];
  if (mesh.name) parts.push(`${t("exterior_mesh")}: ${mesh.vertexCount || 0}v / ${mesh.uvCount || 0}uv`);
  if (metrics.skinBlocks?.length) parts.push(`${t("skin_blocks")}: ${metrics.skinBlocks.join(", ")}`);
  if (Number(metrics.skinUvBBoxArea || 0) > 0) parts.push(`${t("uv_area")}: ${metrics.skinUvBBoxArea}`);
  if (Number(metrics.designCount || 0) > 0) parts.push(`${t("designs")}: ${metrics.designCount}`);
  if (Number(metrics.skinTemplateCount || 0) > 0) {
    parts.push(`templates: ${Number(metrics.skinTemplateReadyCount || 0)}/${Number(metrics.skinTemplateCount || 0)} ready`);
  }
  if (Number(metrics.skinLocalRefCount || 0) > 0) parts.push(`${t("skin_local_template")}: ${metrics.skinLocalRefCount}`);
  if (Number(metrics.carLevelRefCount || 0) > 0) parts.push(`${t("car_level_paint")}: ${metrics.carLevelRefCount}`);
  return parts.join(" | ");
}

function carCompatibilityProgressHtml() {
  const progress = state.carCompatibilityProgress || {};
  const index = Number(progress.index || 0);
  const total = Number(progress.total || 0);
  const percent = total ? Math.max(0, Math.min(100, Math.round((index / total) * 100))) : 0;
  const counts = progress.counts || {};
  const templateCounts = progress.templateCounts || {};
  const phase = String(progress.phase || "");
  let current = progress.current ? `${t("about_car_compatibility_checking")}: ${progress.current}` : t("about_car_compatibility_loading");
  if (phase === "checking_templates") {
    current = progress.current ? `Checking skin templates: ${progress.current}` : "Checking skin templates";
  } else if (phase === "preparing_thumbnails") {
    current = progress.current || "Preparing Advanced creator thumbnails";
  } else if (phase === "saving") {
    current = progress.current || "Saving car analysis cache";
  } else if (phase === "done") {
    current = progress.current || "Car analysis complete";
  }
  const readyCount = Number(counts.ready || 0) + Number(counts.paint_only || 0);
  const templateTotal = Object.values(templateCounts).reduce((sum, value) => sum + Number(value || 0), 0);
  return `<div class="car-compatibility-progress">
    <div class="car-compatibility-progress-meta">
      <strong>${escapeHtml(current)}</strong>
      <span>${index}/${total || "?"} | ${percent}%</span>
    </div>
    <div class="car-compatibility-progress-track" aria-hidden="true">
      <span style="width: ${percent}%"></span>
    </div>
    <div class="car-compatibility-progress-counts">
      <span>${escapeHtml(t("count_ready"))}: ${readyCount}</span>
      <span>${escapeHtml(t("count_unsupported"))}: ${Number(counts.unsupported || 0)}</span>
      ${templateTotal ? `<span>Templates: ${Number(templateCounts.ready || 0)}/${templateTotal} ready</span>` : ""}
      ${Number(templateCounts.unsupported || 0) ? `<span>Blocked templates: ${Number(templateCounts.unsupported || 0)}</span>` : ""}
    </div>
  </div>`;
}

function setCarCompatibilityData(data = null, cacheStatus = null) {
  state.carCompatibility = data || null;
  state.carCompatibilityCacheStatus = cacheStatus || data?.cacheStatus || null;
}

function carCompatibilityCacheNoticeHtml() {
  const status = state.carCompatibilityCacheStatus;
  if (!status || !status.updateNeeded) return "";
  const newCars = Array.isArray(status.newCars) ? status.newCars : [];
  const missingCars = Array.isArray(status.missingCars) ? status.missingCars : [];
  const details = [];
  if (newCars.length) details.push(`New: ${newCars.slice(0, 4).join(", ")}${newCars.length > 4 ? ` +${newCars.length - 4}` : ""}`);
  if (missingCars.length) details.push(`Missing: ${missingCars.slice(0, 4).join(", ")}${missingCars.length > 4 ? ` +${missingCars.length - 4}` : ""}`);
  const reason = status.reason || "Car list changed. Run Analyze Cars again.";
  return `<div class="home-cache-notice">
    <strong>Update needed</strong>
    <span>${escapeHtml(reason)}${details.length ? ` ${escapeHtml(details.join(" | "))}` : ""}</span>
  </div>`;
}

function toggleCarAnalysisBypass(event = null) {
  if (event && !event.altKey) return;
  event?.preventDefault();
  event?.stopPropagation();
  state.carAnalysisBypassed = !state.carAnalysisBypassed;
  if (state.carAnalysisBypassed) {
    state.carCompatibilityLoading = false;
    state.carCompatibilityProgress = null;
  }
  renderCarCompatibility();
  applySetupGateStatus();
}

function renderCarCompatibility() {
  const summary = $("carCompatibilitySummary");
  const rowsRoot = $("carCompatibilityRows");
  const button = $("refreshCarCompatibility");
  const homeSummary = $("homeCarCompatibilitySummary");
  const homeButton = $("homeAnalyzeCars");
  const analyzeLabel = state.carCompatibility ? t("analyze_cars_again") : t("analyze_cars");
  const analyzeIcon = state.carCompatibility ? "refresh" : "inspect";
  if (button) {
    setIconButtonLabel(button, analyzeLabel, analyzeIcon);
    button.disabled = Boolean(state.carCompatibilityLoading);
  }
  if (homeButton) {
    setIconButtonLabel(homeButton, analyzeLabel, analyzeIcon);
    homeButton.disabled = Boolean(state.carCompatibilityLoading);
  }
  if (state.carAnalysisBypassed) {
    setHomeStepStatus("homeCarStepStatus", "Bypassed", "is-ok");
  } else if (state.carCompatibilityLoading) {
    setHomeStepStatus("homeCarStepStatus", "Analyzing", "is-warning");
  } else if (!state.carCompatibility) {
    setHomeStepStatus("homeCarStepStatus", "Not analyzed", "is-pending");
  } else {
    const updateNeeded = Boolean(state.carCompatibilityCacheStatus?.updateNeeded);
    setHomeStepStatus("homeCarStepStatus", updateNeeded ? "Update needed" : "Analyzed", updateNeeded ? "is-warning" : "is-ok");
  }
  const renderSummary = (target) => {
    if (!target) return;
    if (state.carAnalysisBypassed) {
      target.innerHTML = '<div class="compatibility-empty">Car Analysis bypassed for this session.</div>';
      return;
    }
    if (state.carCompatibilityLoading) {
      target.innerHTML = carCompatibilityProgressHtml();
      return;
    }
    const data = state.carCompatibility;
    if (!data) {
      target.innerHTML = `<span class="compatibility-pill is-loading">${escapeHtml(t("about_car_compatibility_pending"))}</span>`;
      return;
    }
    const counts = data.counts || {};
    const templateCounts = data.skinTemplateCounts || {};
    const readyCount = Number(counts.ready || 0) + Number(counts.paint_only || 0);
    target.innerHTML = carCompatibilityCacheNoticeHtml() + [
      ["ready", t("count_ready"), readyCount],
      ["unsupported", t("count_unsupported"), Number(counts.unsupported || 0)],
      ["unknown", t("unknown"), Number(counts.unknown || 0)],
    ].map(([key, label, count]) => `<span class="compatibility-pill status-${key}">${escapeHtml(label)}: ${Number(count || 0)}</span>`).join("")
      + `<span class="compatibility-pill status-unsupported">Blocked templates: ${Number(templateCounts.unsupported || 0)}</span>`;
  };
  renderSummary(homeSummary);
  renderAdvancedCreatorCars();
  renderWorkflowCompatibility();
  applySetupGateStatus();
  if (state.mySkins?.length && !state.carCompatibilityLoading) renderMySkins();
  if (!summary || !rowsRoot) return;

  if (state.carCompatibilityLoading) {
    renderSummary(summary);
    rowsRoot.innerHTML = `<tr><td colspan="4">${escapeHtml(t("about_car_compatibility_loading"))}</td></tr>`;
    return;
  }

  const data = state.carCompatibility;
  const cars = data?.cars || [];
  if (!data) {
    renderSummary(summary);
    rowsRoot.innerHTML = `<tr><td colspan="4">${escapeHtml(t("about_car_compatibility_pending"))}</td></tr>`;
    return;
  }
  if (!cars.length) {
    summary.innerHTML = "";
    rowsRoot.innerHTML = `<tr><td colspan="4">${escapeHtml(t("about_car_compatibility_empty"))}</td></tr>`;
    return;
  }

  renderSummary(summary);

  const order = { ready: 0, unsupported: 2, unknown: 3 };
  rowsRoot.innerHTML = [...cars]
    .sort((a, b) => (order[carCompatibilityDisplayStatus(a.status)] ?? 9) - (order[carCompatibilityDisplayStatus(b.status)] ?? 9) || String(a.carId).localeCompare(String(b.carId)))
    .map((car) => {
      const status = carCompatibilityDisplayStatus(car.status);
      const missing = car.missing?.length ? car.missing.join(" | ") : t("no_missing_signals");
      const templateWarningCount = Array.isArray(car.templateWarnings) ? car.templateWarnings.length : 0;
      const missingText = templateWarningCount
        ? `${missing} | blocked templates: ${templateWarningCount}`
        : missing;
      const present = carCompatibilityPresentText(car);
      return `<tr>
        <td><strong>${escapeHtml(car.carId || "-")}</strong></td>
        <td><span class="compatibility-status status-${escapeHtml(status)}">${escapeHtml(carCompatibilityStatusText(status))}</span></td>
        <td>${escapeHtml(present || "-")}</td>
        <td>${escapeHtml(missingText)}</td>
      </tr>`;
    })
    .join("");
}

async function loadCachedCarCompatibility() {
  if (!window.pywebview?.api?.load_car_livery_compatibility_cache) {
    renderCarCompatibility();
    return;
  }
  const root = $("rootPath")?.value.trim() || state.root || "";
  if (!root) {
    setCarCompatibilityData(null, null);
    renderCarCompatibility();
    return;
  }
  try {
    const result = await window.pywebview.api.load_car_livery_compatibility_cache(root);
    if (!result?.ok) {
      renderCarCompatibility();
      return;
    }
    const analysis = result.data?.analysis || null;
    setCarCompatibilityData(analysis, result.data?.cacheStatus || analysis?.cacheStatus || null);
    renderCarCompatibility();
  } catch (_error) {
    renderCarCompatibility();
  }
}

async function loadCarCompatibility(force = false) {
  if (!force && state.carCompatibility) {
    renderCarCompatibility();
    return;
  }
  if (state.carCompatibilityLoading) return;
  const root = $("rootPath")?.value.trim() || state.root || "";
  const refreshBrowserCompatibility = isCarsRootRel();
  state.carCompatibilityLoading = true;
  state.carCompatibilityProgress = { phase: "start", current: "", index: 0, total: 0, counts: {} };
  renderCarCompatibility();
  try {
    const result = await window.pywebview.api.analyze_car_livery_compatibility(root, Boolean(force));
    if (showResult(result, "Car livery compatibility analyzed")) {
      setCarCompatibilityData(result.data, result.data?.cacheStatus || null);
    }
  } catch (error) {
    log(error?.message || String(error), true);
  } finally {
    state.carCompatibilityLoading = false;
    state.carCompatibilityProgress = null;
    renderCarCompatibility();
    if (refreshBrowserCompatibility) await refreshBrowser(state.currentRel);
  }
}

function renderCreateSkinStepLines(steps) {
  const icons = {
    pending: uiIcon("circle"),
    running: uiIcon("refresh"),
    done: uiIcon("check"),
    skipped: uiIcon("minus"),
    error: uiIcon("x"),
  };
  return `<div class="status-step-list">${steps.map((step) => `
    <div class="status-step status-step-${step.status}">
      <span class="status-step-icon">${icons[step.status] || icons.pending}</span>
      <span class="status-step-label">${escapeHtml(step.label)}</span>
      ${step.note ? `<span class="status-step-note">${escapeHtml(step.note)}</span>` : ""}
    </div>
  `).join("")}</div>`;
}

function showCreateSkinProgress(steps, subtitle = "Preparing livery", details = "", error = "", technicalDetails = "") {
  const hasError = steps.some((step) => step.status === "error");
  const done = steps.every((step) => ["done", "skipped"].includes(step.status));
  const running = steps.some((step) => step.status === "running");
  showStatusModal({
    type: hasError ? "error" : done ? "success" : "info",
    title: "Livery Creator",
    subtitle,
    messageHtml: renderCreateSkinStepLines(steps),
    error,
    details,
    technicalDetails,
    blockClose: running,
  });
}

function liveryProjectReuseLines(project = {}) {
  if (!project?.exists) return [];
  const lines = [
    "Existing LiveryProjects workspace found:",
    project.path || "-",
    "",
    "Existing editable project files will be reused.",
    "No files in this project workspace will be deleted during skin creation.",
  ];
  const counts = [
    `${Number(project.fileCount || 0)} files`,
    `${Number(project.pngFileCount || 0)} PNG`,
    `${Number(project.workFileCount || 0)} work files`,
  ];
  lines.push(`Workspace content: ${counts.join(", ")}`);
  return lines;
}

function variantAvailabilityLines(availability = {}) {
  const message = String(availability?.message || "").trim();
  if (!message) return [];
  const targetNames = Array.isArray(availability.targetNames) ? availability.targetNames.filter(Boolean) : [];
  const totalCount = Number(availability.totalCount || 0);
  const targetCount = Number(availability.targetCount || targetNames.length || 0);
  const lines = ["Vehicle preset availability:", message];
  if (totalCount && targetCount && targetCount < totalCount) {
    lines.push(`Matching presets: ${targetNames.join(", ") || targetCount}`);
    lines.push(`This livery will not be added to ${totalCount - targetCount} incompatible vehicle preset${totalCount - targetCount === 1 ? "" : "s"}.`);
  }
  return lines;
}

async function validateNewSkinIdAvailability(skinPayload = {}) {
  if (skinPayload.overwrite) return { status: "ok", data: null };
  if (typeof window.pywebview?.api?.inspect_new_skin_id !== "function") return { status: "ok", data: null };
  if (!skinPayload.root || !skinPayload.cardesign || !skinPayload.design || !skinPayload.skinId) return { status: "ok", data: null };
  const result = await window.pywebview.api.inspect_new_skin_id(skinPayload);
  if (!result.ok) {
    showResult(result, "Skin ID check failed", "inspect_new_skin_id");
    return { status: "cancel", data: null };
  }
  const data = result.data || {};
  if (!data.conflict) return { status: "ok", data };
  const suggested = data.suggestedSkinId || "";
  if (suggested) {
    skinPayload.skinId = suggested;
    const skinIdInput = $("skinId");
    if (skinIdInput?.value.trim()) skinIdInput.value = suggested;
    updateSkinIdPreview();
    log(`Skin ID already exists. Using available ID: ${suggested}`);
    return { status: "ok", data: { ...data, skinId: suggested, autoSuggested: true } };
  }
  const registry = Array.isArray(data.registryMatches) ? data.registryMatches : [];
  const paths = Array.isArray(data.existingPaths) ? data.existingPaths : [];
  const projectLines = liveryProjectReuseLines(data.liveryProject || {});
  const variantLines = variantAvailabilityLines(data.variantAvailability || {});
  const details = [
    suggested ? `Suggested Skin ID: ${suggested}` : "Choose another Skin ID or overwrite the existing local livery intentionally.",
    "",
    registry.length ? "Existing My Liveries entries:" : "",
    ...registry.map((item) => `- ${item.key || item.display_name || data.skinId}${item.local_root ? `\n  ${item.local_root}` : ""}`),
    paths.length ? "Existing local targets:" : "",
    ...paths.map((item) => `- ${item.kind || "target"}: ${item.path || ""}`),
    ...(variantLines.length ? ["", ...variantLines] : []),
    ...(projectLines.length ? ["", ...projectLines] : []),
  ].filter((line) => line !== "").join("\n");
  const overwrite = await showConfirmModal({
    type: "danger",
    title: "Livery Creator",
    subtitle: "Skin ID already exists",
    message: `Skin ID "${data.skinId || skinPayload.skinId}" already exists for ${data.carModel || "this car"}.`,
    details,
    confirmLabel: "Overwrite Existing Skin",
    cancelLabel: "Cancel",
  });
  log(`Skin ID already exists. Suggested ID: ${suggested || "choose a different ID"}`, true);
  setInspectOutputText(details);
  return { status: overwrite ? "overwrite" : "cancel", data };
}

async function createOfficialExternalProjectFromCreator() {
  const payload = payloadOfficialExternalProject();
  const folderName = officialExternalFolderPreview(payload.carModel, skinIdFromLiveryName(payload.liveryName));
  if (!payload.liveryName) {
    log("Please enter a livery name first.", true);
    $("skinName")?.focus();
    updateSkinIdPreview();
    return;
  }
  if (!payload.carModel) {
    log("Please enter the target car id first.", true);
    $("officialProjectCarModel")?.focus();
    return;
  }
  const selectedCar = selectedOfficialCreatorCar();
  if (!selectedCar?.ready) {
    showStatusModal({
      type: "error",
      title: "Create official project",
      subtitle: "Preview Assets required",
      message: "Create needs prepared Official Preview Assets for the selected car.",
      details: [
        `Target car: ${payload.carModel || "-"}`,
        "Run the official setup/resource preparation, then refresh the Official car list.",
      ].join("\n"),
    });
    return;
  }
  if (!payload.vehicleVariant) {
    showStatusModal({
      type: "error",
      title: "Create official project",
      subtitle: "No compatible variant",
      message: "This car has no prepared variant that exposes a neutral EXT_SKIN material to Official External Liveries.",
      details: "Pre-authored design and presentation variants are excluded because AC EVO does not apply external livery textures to them.",
    });
    return;
  }
  const confirmed = await showConfirmModal({
    title: "Create official external project",
    message: "Create this official livery as a local OfficialLiveryProjects workspace?",
    details: [
      "- Create a local editable project under OfficialLiveryProjects.",
      "- Create external_livery.json and the initial EXT_Skin_C.png source texture.",
      "- Generate a matching garage SavedCar inside the local project.",
      "- Register the project in My Liveries.",
      "- Do not write to Saved Games or AC EVO game folders.",
      "",
      `Livery name: ${payload.liveryName}`,
      `Target car: ${payload.carModel}`,
      `Variant / trim: ${payload.vehicleVariant?.label || "Default"}`,
      "Start point: Blank livery",
      `ExternalLiveries folder: ${folderName}`,
      "Garage SavedCar: generated automatically from the selected variant",
      "Saved Games install: first Activate in Game",
    ].join("\n"),
    confirmLabel: "Create official project",
  });
  if (!confirmed) return;

  const steps = [
    { id: "project", label: "Create official project", status: "running" },
    { id: "library", label: "Refresh My Liveries library", status: "pending" },
  ];
  const setStep = (id, status, note = "") => {
    const step = steps.find((item) => item.id === id);
    if (!step) return;
    step.status = status;
    step.note = note;
    showCreateSkinProgress(steps, "Creating official project");
  };
  showCreateSkinProgress(steps, "Creating official project");
  try {
    const created = await window.pywebview.api.create_new_official_external_project(payload);
    if (!created.ok) {
      setStep("project", "error", created.message || "failed");
      showCreateSkinProgress(steps, "Official project creation failed", "", created.message || "Official project creation failed", technicalDetailsFromResult(created));
      log(created.message || "Official project creation failed", true);
      return;
    }
    setStep("project", "done", created.data?.externalFolderName || folderName);
    const entry = created.data?.entry || {};
    const skinKey = entry.key || "";
    if (skinKey) state.mySkinSelectedKey = skinKey;
    setStep("library", "running");
    clearMySkinThumbnailCache();
    await loadMySkins();
    if (skinKey) revealMySkinInCategory(skinKey);
    renderMySkins();
    await refreshRuntimeProfile({ silent: true, root: entry.game_root || payload.root || state.root || "" });
    setStep("library", "done");
    const creatorPreflight = created.data?.creatorPreflight && typeof created.data.creatorPreflight === "object" ? created.data.creatorPreflight : {};
    const creatorWarnings = Array.isArray(creatorPreflight.warnings) ? creatorPreflight.warnings : [];
    const creatorChecks = Array.isArray(creatorPreflight.checks) ? creatorPreflight.checks : [];
    const details = [
      created.message || "Official external project created",
      "",
      "Project folder:",
      created.data?.projectFolder || "-",
      "",
      `Prepared SavedCar GUID: ${created.data?.savedCar?.carGuid || entry.car_guid || "-"}`,
      `Prepared SavedCar: ${created.data?.savedCar?.path || "-"}`,
      ...(creatorWarnings.length ? ["", "Preflight warnings:", ...creatorWarnings.map((warning) => `- ${warning}`)] : []),
      ...(creatorChecks.length ? ["", "Preflight checks:", ...creatorChecks.map((check) => `- ${check.label || check.key || "Check"}: ${check.message || check.status || "-"}`)] : []),
      "",
      "Next step:",
      "Open the project or Layer Studio, edit EXT_Skin_C.png, then use Activate in Game to deploy to Saved Games.",
    ].join("\n");
    showCreateSkinProgress(steps, "Official project ready", details);
    setInspectOutputText(details);
    log(created.message || "Official external project created");
    setTab("library");
  } catch (error) {
    const running = steps.find((step) => step.status === "running");
    if (running) running.status = "error";
    showCreateSkinProgress(steps, "Official project creation failed", "", error?.message || String(error), devModeEnabled() ? (error?.stack || "") : "");
    log(error?.message || String(error), true);
  }
}

async function createSkinWithPreviewSetup() {
  if (skinCreatorUsesOfficialProject()) {
    await createOfficialExternalProjectFromCreator();
    return;
  }
  if ($("skinCardesign")?.value.trim()) {
    await autofillSkinTemplateFields(false);
  }
  const skinPayload = payloadSkin();
  skinPayload.bypassLiveryLabSafety = state.liveryLabSafetyBypassed;
  let skinId = skinPayload.skinId || "new_skin";
  if (!skinPayload.cardesign) {
    log("Please choose a ready car template first.", true);
    $("advancedCreatorCarSearch")?.focus();
    return;
  }
  const templateSupport = await inspectSkinTemplateSupportForPath(skinPayload.cardesign, skinPayload.design);
  if (templateSupport?.status === "unsupported") {
    showStatusModal({
      type: "error",
      title: "Livery Creator",
      subtitle: "Unsupported skin template",
      error: templateSupport.message || SKIN_TEMPLATE_UNSUPPORTED_MESSAGE,
      details: skinTemplateSupportReason(templateSupport),
    });
    log(skinTemplateUnsupportedText(templateSupport), true);
    return;
  }
  if (!skinPayload.skinName) {
    log("Please enter a livery name first.", true);
    $("skinName")?.focus();
    updateSkinIdPreview();
    return;
  }
  if (skinPayload.createNumberplateVariant && !skinPayload.numberplateTemplateId) {
    log("Please choose a numberplate template first", true);
    return;
  }
  const availability = await validateNewSkinIdAvailability(skinPayload);
  const availabilityStatus = availability?.status || availability || "ok";
  const preflightData = availability?.data || null;
  const projectReuseLines = liveryProjectReuseLines(preflightData?.liveryProject || {});
  const variantLines = variantAvailabilityLines(preflightData?.variantAvailability || {});
  if (availabilityStatus === "overwrite") {
    skinPayload.overwrite = true;
  } else if (availabilityStatus !== "ok") {
    return;
  }
  skinId = skinPayload.skinId || skinId;
  const writeRows = [
    `Create local skin files for "${skinId}" in LiveryLibrary/LocalSkins.`,
    "Write the new .cardesign and .design files.",
  ];
  if (skinPayload.copySource) writeRows.push("Copy and patch the source skin folder/material files.");
  if (skinPayload.patchActor || skinPayload.patchVisualActor || skinPayload.createNumberplateVariant) writeRows.push("Actor changes are deferred until Activate in Game.");
  if (skinPayload.createVisual) writeRows.push("Create a new visual preset.");
  if (skinPayload.updateMechanical) writeRows.push("Mechanical changes are deferred until Activate in Game.");
  if (skinPayload.createCarFinalState) writeRows.push("Create local car final state entries for matching vehicle presets.");
  if (skinPayload.previewImage) writeRows.push("Create/update local skin thumbnails.");
  const selectedStartPoint = skinPayload.liveryTemplate && typeof skinPayload.liveryTemplate === "object" ? skinPayload.liveryTemplate : {};
  const usesOriginalStartPoint = String(selectedStartPoint.mode || "").trim().toLowerCase() === "original_game_livery";
  if (usesOriginalStartPoint) writeRows.push("Use the original AC EVO livery textures as the project start point.");
  if (!(await showConfirmModal({
    title: "Create local livery",
    message: "Create this livery in LiveryLab's local library?",
    details: [
      ...writeRows.map((row) => `- ${row}`),
      ...(variantLines.length ? ["", ...variantLines] : []),
      ...(projectReuseLines.length ? ["", ...projectReuseLines] : []),
      "",
      `Skin id: ${skinId}`,
      `Display name: ${skinPayload.skinName || "-"}`,
      `Overwrite: ${skinPayload.overwrite ? "yes" : "no"}`,
    ].join("\n"),
    confirmLabel: "Create livery",
  }))) return;
  const createDds = Boolean($("createPreviewDds")?.checked);
  const steps = [
    { id: "skin", label: "Create local livery", status: "running" },
    { id: "project", label: "Prepare project files", status: "pending" },
    ...(usesOriginalStartPoint ? [{ id: "startPoint", label: "Apply livery start point", status: "pending" }] : []),
    { id: "textures", label: "Import preview PNG textures", status: "pending" },
    ...(createDds ? [{ id: "dds", label: "Create DDS source files", status: "pending" }] : []),
    { id: "wheelCache", label: "Prepare wheel preview cache", status: "pending" },
    { id: "library", label: "Refresh My Liveries library", status: "pending" },
  ];
  const setStep = (id, status, note = "") => {
    const step = steps.find((item) => item.id === id);
    if (!step) return;
    step.status = status;
    step.note = note;
    showCreateSkinProgress(steps);
  };
  const refreshCreatedSkinLibrary = async (skinKey) => {
    if (!skinKey) return;
    setStep("library", "running");
    clearMySkinThumbnailCache();
    await loadMySkins();
    revealMySkinInCategory(skinKey);
    renderMySkins();
    setStep("library", "done");
  };

  showCreateSkinProgress(steps);
  let details = "";
  try {
    const created = await window.pywebview.api.create_skin(skinPayload);
    if (!created.ok) {
      setStep("skin", "error", created.message || "failed");
      showCreateSkinProgress(steps, "Skin creation failed", "", created.message || "Skin creation failed", technicalDetailsFromResult(created));
      log(created.message || "Skin creation failed", true);
      setInspectOutputText(displayDetails(created.message || "Skin creation failed", technicalDetailsFromResult(created)));
      return;
    }
    setStep("skin", "done");
    details = created.data?.text || created.message || "";
    if (created.message) log(created.message);
    setInspectOutputText(details);
    const skinKey = created.data?.skinKey || created.data?.mySkin?.key || created.data?.result?.my_skin_entry?.key || "";
    if (skinKey) {
      state.mySkinSelectedKey = skinKey;
    }

    if (!skinKey) {
      setStep("project", "error", "skin key missing");
      showCreateSkinProgress(steps, "Skin created, preview setup incomplete", details);
      return;
    }

    if (!usesOriginalStartPoint && String(selectedStartPoint.mode || "").trim().toLowerCase() === "blank") {
      const recorded = await window.pywebview.api.apply_livery_start_point({
        skinKey,
        root: $("rootPath").value.trim(),
        startPoint: selectedStartPoint,
        overwrite: false,
        createDds: false,
      });
      if (!recorded.ok) {
        setStep("project", "error", recorded.message || "blank start point failed");
        await refreshCreatedSkinLibrary(skinKey);
        showCreateSkinProgress(steps, "Skin created, preview setup incomplete", details, recorded.message || "Blank livery setup failed", technicalDetailsFromResult(recorded));
        return;
      }
    }

    setStep("project", "running");
    const prepared = await window.pywebview.api.prepare_livery_preview_project({ skinKey, root: $("rootPath").value.trim() });
    if (!prepared.ok) {
      setStep("project", "error", prepared.message || "failed");
      await refreshCreatedSkinLibrary(skinKey);
      showCreateSkinProgress(steps, "Skin created, preview project failed", details, prepared.message || "Preview project failed", technicalDetailsFromResult(prepared));
      return;
    }
    const uvTemplate = prepared.data?.uvTemplate || {};
    const templateNotes = [];
    if (uvTemplate.path) templateNotes.push(uvTemplate.created ? "body UV generated" : "body UV ready");
    setStep("project", "done", templateNotes.join(", ") || (projectReuseLines.length ? "existing workspace reused" : ""));

    let startPointLines = [];
    if (usesOriginalStartPoint) {
      setStep("startPoint", "running");
      if (typeof window.pywebview?.api?.apply_livery_start_point !== "function") {
        setStep("startPoint", "error", "rebuild required");
        await refreshCreatedSkinLibrary(skinKey);
        const message = "This runtime does not expose the livery start point API yet. Rebuild required.";
        showCreateSkinProgress(steps, "Skin created, start point failed", details, message);
        log(message, true);
        return;
      }
      const applied = await window.pywebview.api.apply_livery_start_point({
        skinKey,
        root: $("rootPath").value.trim(),
        startPoint: selectedStartPoint,
        overwrite: false,
        createDds,
      });
      if (!applied.ok) {
        setStep("startPoint", "error", applied.message || "failed");
        await refreshCreatedSkinLibrary(skinKey);
        showCreateSkinProgress(steps, "Skin created, start point failed", details, applied.message || "Start point failed", technicalDetailsFromResult(applied));
        return;
      }
      const appliedData = applied.data || {};
      setStep("startPoint", "done", `${appliedData.imported || 0} imported, ${appliedData.skipped || 0} skipped, ${appliedData.missing || 0} missing`);
      startPointLines = [
        "",
        "Livery start point:",
        applied.message || "Original AC EVO livery applied",
        ...(appliedData.items || []).map((item) => `${item.label || item.stem || "Texture"}: ${item.status}${item.ddsStatus ? `, DDS ${item.ddsStatus}` : ""}`),
      ];
    }

    setStep("textures", "running");
    if (createDds) setStep("dds", "running");
    const imported = await window.pywebview.api.import_livery_preview_textures({
      skinKey,
      root: $("rootPath").value.trim(),
      overwrite: false,
      createDds,
    });
    if (!imported.ok) {
      setStep("textures", "error", imported.message || "failed");
      if (createDds) setStep("dds", "error", "not completed");
      await refreshCreatedSkinLibrary(skinKey);
      showCreateSkinProgress(steps, "Skin created, texture import failed", details, imported.message || "Texture import failed", technicalDetailsFromResult(imported));
      return;
    }
    const importedData = imported.data || {};
    setStep("textures", "done", `${importedData.imported || 0} imported, ${importedData.skipped || 0} skipped, ${importedData.missing || 0} missing`);
    if (createDds) {
      setStep("dds", "done", `${importedData.ddsImported || 0} imported`);
    }
    setStep("wheelCache", "running");
    let wheelCacheLines = ["Wheel preview cache: skipped"];
    try {
      const warmed = await window.pywebview.api.warm_livery_preview_wheel_cache({
        skinKey,
        root: $("rootPath").value.trim(),
        force: false,
      });
      if (warmed.ok) {
        const warmedData = warmed.data || {};
        const modelCount = Number(warmedData.auxiliaryModelCount || 0);
        const builtCount = Number(warmedData.auxiliaryModelsBuilt || 0);
        const reusedCount = Number(warmedData.auxiliaryModelsCached || 0);
        const warmedCount = Number(warmedData.warmedCount || 0);
        const skippedCount = Number(warmedData.skippedCount || 0);
        const warningCount = Array.isArray(warmedData.warnings) ? warmedData.warnings.length : 0;
        setStep("wheelCache", "done", `${modelCount} models, ${warmedCount} textures${skippedCount ? `, ${skippedCount} skipped` : ""}${warningCount ? `, ${warningCount} warnings` : ""}`);
        wheelCacheLines = [
          warmed.message || "Wheel preview cache prepared",
          `models: ${modelCount} (${builtCount} built, ${reusedCount} reused)`,
          `textures: ${warmedCount} cached, ${skippedCount} skipped`,
          ...(warningCount ? ["Warnings:", ...warmedData.warnings.map((row) => `- ${row}`)] : []),
        ];
      } else {
        setStep("wheelCache", "skipped", warmed.message || "not available");
        wheelCacheLines = ["Wheel preview cache skipped", warmed.message || ""].filter(Boolean);
      }
    } catch (error) {
      setStep("wheelCache", "skipped", error?.message || "not available");
      wheelCacheLines = ["Wheel preview cache skipped", error?.message || String(error || "")].filter(Boolean);
    }
    const textureLines = [
      imported.message || "Preview textures imported",
      ...(importedData.items || []).map((item) => `${item.label}: ${item.status}${item.ddsStatus ? `, DDS ${item.ddsStatus}` : ""}`),
    ].join("\n");
    const materialMaps = Array.isArray(importedData.project?.liveMaterialTextureMaps)
      ? importedData.project.liveMaterialTextureMaps.filter((item) => item?.stem && !["ext_skin_c", "ext_skin_d"].includes(String(item.stem).toLowerCase()))
      : [];
    const materialMapLines = materialMaps.length
      ? ["", "Additional material maps:", ...materialMaps.map((item) => `${item.name || item.label || item.stem}: ${item.path || "-"}`)]
      : [];
    details = [
      details,
      "",
      "Preview project:",
      prepared.data?.projectFolder || "-",
      ...(uvTemplate.path ? ["", `UV template: ${uvTemplate.path}`] : []),
      ...materialMapLines,
      ...(projectReuseLines.length ? ["", ...projectReuseLines] : []),
      "",
      "LiveryProjects is your live work folder. Export a finished package later from My Liveries into LiveryPackageExport.",
      ...startPointLines,
      "",
      textureLines,
      "",
      wheelCacheLines.join("\n"),
    ].join("\n");
    await refreshCreatedSkinLibrary(skinKey);
    showCreateSkinProgress(steps, "Skin and preview workspace ready", details);
    setInspectOutputText(details);
  } catch (error) {
    const running = steps.find((step) => step.status === "running");
    if (running) running.status = "error";
    showCreateSkinProgress(steps, "Skin creation failed", details, error?.message || String(error), devModeEnabled() ? (error?.stack || "") : "");
    log(error?.message || String(error), true);
  }
}

function deletedLiveryRoot() {
  return $("rootPath")?.value.trim() || $("homeRootPath")?.value.trim() || state.root || state.savedRoot || "";
}

function formatDeletedLiveryDate(value) {
  const text = String(value || "").trim();
  if (!text) return "Unknown";
  const parsed = new Date(text);
  if (Number.isNaN(parsed.getTime())) return text;
  return parsed.toLocaleString(undefined, {
    year: "numeric",
    month: "2-digit",
    day: "2-digit",
    hour: "2-digit",
    minute: "2-digit",
  });
}

function selectedDeletedLivery() {
  return (state.deletedLiveries || []).find((item) => String(item.id || "") === state.selectedDeletedLiveryId) || null;
}

function setDeletedLiveryActions() {
  const selected = selectedDeletedLivery();
  const items = Array.isArray(state.deletedLiveries) ? state.deletedLiveries : [];
  const openButton = $("openDeletedLiveryTrash");
  const restoreButton = $("restoreDeletedLivery");
  const removeButton = $("removeDeletedLivery");
  const removeAllButton = $("removeAllDeletedLiveries");
  if (openButton) openButton.disabled = !selected || !selected.path;
  if (restoreButton) restoreButton.disabled = !selected || !selected.canRestore;
  if (removeButton) removeButton.disabled = !selected;
  if (removeAllButton) removeAllButton.disabled = state.deletedLiveriesLoading || !items.length;
}

function renderDeletedLiveries() {
  const list = $("deletedLiveriesList");
  if (!list) return;
  list.innerHTML = "";
  list.className = "deleted-liveries-list";
  if (state.deletedLiveriesLoading) {
    const empty = document.createElement("div");
    empty.className = "deleted-liveries-empty";
    empty.textContent = "Loading deleted liveries...";
    list.appendChild(empty);
    setDeletedLiveryActions();
    return;
  }
  const items = Array.isArray(state.deletedLiveries) ? state.deletedLiveries : [];
  if (!items.length) {
    const empty = document.createElement("div");
    empty.className = "deleted-liveries-empty";
    empty.textContent = "No deleted liveries found.";
    list.appendChild(empty);
    state.selectedDeletedLiveryId = "";
    setDeletedLiveryActions();
    return;
  }
  if (!items.some((item) => String(item.id || "") === state.selectedDeletedLiveryId)) {
    state.selectedDeletedLiveryId = String(items[0]?.id || "");
  }
  items.forEach((item) => {
    const selected = String(item.id || "") === state.selectedDeletedLiveryId;
    const row = document.createElement("button");
    row.type = "button";
    row.className = `deleted-livery-row${selected ? " is-selected" : ""}${item.canRestore ? "" : " has-conflict"}`;
    row.dataset.trashId = item.id || "";
    row.innerHTML = `
      <span class="deleted-livery-main">
        <strong>${escapeHtml(item.displayName || item.skinId || item.id || "Deleted livery")}</strong>
        <span>${escapeHtml(item.carModel || "Unknown car")} / ${escapeHtml(item.skinId || "Unknown skin")}</span>
      </span>
      <span class="deleted-livery-meta">
        <span>${escapeHtml(formatDeletedLiveryDate(item.trashedAt))}</span>
        <span>${item.incomplete ? "Incomplete" : item.localSkinAvailable ? "Local files" : "No local files"}${item.liveryProjectAvailable ? " + Project" : ""}</span>
      </span>
    `;
    row.addEventListener("click", () => {
      state.selectedDeletedLiveryId = String(item.id || "");
      renderDeletedLiveries();
    });
    list.appendChild(row);
    const detail = document.createElement("div");
    detail.className = `deleted-livery-detail${selected ? " is-open" : ""}`;
    detail.hidden = !selected;
    const conflicts = Array.isArray(item.conflicts) ? item.conflicts.filter(Boolean) : [];
    detail.innerHTML = `
      <div><strong>Status</strong><span>${item.incomplete ? "Incomplete trash entry" : item.canRestore ? "Ready to restore locally" : "Restore blocked"}</span></div>
      <div><strong>Trash</strong><code>${escapeHtml(item.path || "")}</code></div>
      ${conflicts.length ? `<div><strong>Conflicts</strong><span>${conflicts.map(escapeHtml).join("<br>")}</span></div>` : ""}
    `;
    list.appendChild(detail);
  });
  setDeletedLiveryActions();
}

async function refreshDeletedLiveries({ silent = false } = {}) {
  if (!window.pywebview?.api?.list_trashed_my_skins) {
    const result = { ok: false, error: "Deleted Liveries runtime API is not available. Rebuild or resync the release runtime before testing restore actions." };
    if (!silent) showResult(result, "Deleted liveries unavailable", "list_trashed_my_skins");
    return result;
  }
  state.deletedLiveriesLoading = true;
  renderDeletedLiveries();
  try {
    const result = await window.pywebview.api.list_trashed_my_skins(deletedLiveryRoot());
    if (!result?.ok) {
      if (!silent) showResult(result, "Deleted liveries could not be loaded", "list_trashed_my_skins");
      state.deletedLiveries = [];
      return result;
    }
    state.deletedLiveries = Array.isArray(result.data?.items) ? result.data.items : [];
    if (!silent) showResult(result, "Deleted liveries loaded", "list_trashed_my_skins");
    return result;
  } catch (error) {
    const result = { ok: false, error: error?.message || String(error) };
    if (!silent) showResult(result, "Deleted liveries could not be loaded", "list_trashed_my_skins");
    state.deletedLiveries = [];
    return result;
  } finally {
    state.deletedLiveriesLoading = false;
    renderDeletedLiveries();
  }
}

async function restoreSelectedDeletedLivery() {
  const selected = selectedDeletedLivery();
  if (!selected) return;
  const confirmed = await showConfirmModal({
    type: "warning",
    title: "Restore deleted livery",
    subtitle: selected.displayName || selected.skinId || "Deleted livery",
    message: "This restores the livery to My Liveries as a local inactive livery. It does not write files into AC EVO.",
    details: [
      "Restore action:",
      "- Restore local LiveryLibrary skin files when present.",
      "- Restore the LiveryProject folder when present.",
      "- Add the livery back to My Liveries.",
      "- Keep the livery inactive in game.",
      "",
      "Use Activate in Game from My Liveries if you want to install it into AC EVO again.",
    ].join("\n"),
    confirmLabel: "Restore to My Liveries",
    loadingLabel: "Restoring...",
    onConfirm: () => window.pywebview.api.restore_trashed_my_skin({ trashId: selected.id, root: deletedLiveryRoot() }),
  });
  if (!confirmed) return;
  showResult(confirmed, "Deleted livery restored", "restore_trashed_my_skin");
  if (!confirmed.ok) return;
  await loadMySkins();
  await refreshDeletedLiveries({ silent: true });
}

async function removeSelectedDeletedLivery() {
  const selected = selectedDeletedLivery();
  if (!selected) return;
  const firstConfirmed = await showConfirmModal({
    type: "warning",
    title: "Remove deleted livery",
    subtitle: selected.displayName || selected.skinId || "Deleted livery",
    message: "This permanently removes this livery from the LiveryLab trash. It cannot be restored from this app afterwards.",
    details: [
      "The selected trash entry will be deleted from disk.",
      "This does not touch active AC EVO game files.",
      "Only continue if you no longer need this deleted livery.",
    ].join("\n"),
    confirmLabel: "I understand",
    cancelLabel: "Cancel",
  });
  if (!firstConfirmed) return;
  const finalConfirmed = await showConfirmModal({
    type: "danger",
    actionLayout: "danger-left",
    title: "Final confirmation",
    subtitle: selected.displayName || selected.skinId || "Deleted livery",
    message: "This is the final checkpoint. The selected trash folder will be permanently deleted.",
    details: [
      "Permanent remove action:",
      `- Trash entry: ${selected.id || "-"}`,
      "- Delete the stored local skin files in this trash entry.",
      "- Delete the stored project files in this trash entry.",
      "- Delete the trash manifest.",
    ].join("\n"),
    confirmLabel: "Remove permanently",
    cancelLabel: "Cancel",
    loadingLabel: "Removing...",
    onConfirm: () => window.pywebview.api.delete_trashed_my_skin({ trashId: selected.id, root: deletedLiveryRoot() }),
  });
  if (!finalConfirmed) return;
  showResult(finalConfirmed, "Deleted livery removed", "delete_trashed_my_skin");
  if (!finalConfirmed.ok) return;
  await refreshDeletedLiveries({ silent: true });
}

async function removeAllDeletedLiveries() {
  const items = Array.isArray(state.deletedLiveries) ? [...state.deletedLiveries] : [];
  if (!items.length || !window.pywebview?.api?.delete_trashed_my_skin) return;
  const confirmed = await showConfirmModal({
    type: "danger",
    actionLayout: "danger-left",
    title: "Remove all deleted liveries",
    subtitle: `${items.length} deleted ${items.length === 1 ? "entry" : "entries"}`,
    message: "This permanently removes every listed Advanced livery from the LiveryLab trash. These entries cannot be restored from this app afterwards.",
    details: [
      "Permanent remove action:",
      `- Delete ${items.length} listed Advanced trash ${items.length === 1 ? "entry" : "entries"}.`,
      "- Delete the stored local skin files, project files and trash manifests.",
      "- Do not touch active AC EVO game files.",
      "- Report every entry that cannot be removed.",
    ].join("\n"),
    confirmLabel: "Remove all permanently",
    cancelLabel: "Cancel",
    loadingLabel: "Removing all...",
    onConfirm: async () => {
      const deleted = [];
      const errors = [];
      let fileCount = 0;
      for (const item of items) {
        try {
          const result = await window.pywebview.api.delete_trashed_my_skin({
            trashId: item.id,
            root: deletedLiveryRoot(),
          });
          if (!result?.ok) {
            errors.push(`${item.displayName || item.skinId || item.id}: ${result?.error || result?.message || "Unknown error"}`);
            continue;
          }
          deleted.push(item.id);
          fileCount += Number(result.data?.fileCount || 0);
        } catch (error) {
          errors.push(`${item.displayName || item.skinId || item.id}: ${error?.message || String(error)}`);
        }
      }
      const message = [
        `Deleted Advanced trash entries: ${deleted.length}/${items.length}`,
        `Files deleted: ${fileCount}`,
        errors.length ? `Failed entries: ${errors.length}` : "Failed entries: 0",
      ].join("\n");
      return {
        ok: errors.length === 0,
        data: { deleted, deletedCount: deleted.length, requestedCount: items.length, fileCount, errors },
        message,
        error: errors.length ? `${message}\n${errors.join("\n")}` : "",
      };
    },
  });
  if (!confirmed) return;
  showResult(confirmed, "Deleted liveries removed", "delete_trashed_my_skin");
  if (confirmed.data?.deletedCount) state.selectedDeletedLiveryId = "";
  await refreshDeletedLiveries({ silent: true });
}

async function openSelectedDeletedLiveryTrash() {
  const selected = selectedDeletedLivery();
  if (!selected?.path || !window.pywebview?.api?.open_folder) return;
  const result = await window.pywebview.api.open_folder(selected.path);
  showResult(result, "Trash folder opened", "open_folder");
}

function selectedOfficialDeletedLivery() {
  return (state.officialDeletedLiveries || []).find((item) => String(item.id || "") === state.selectedOfficialDeletedLiveryId) || null;
}

function setOfficialDeletedLiveryActions() {
  const selected = selectedOfficialDeletedLivery();
  const items = Array.isArray(state.officialDeletedLiveries) ? state.officialDeletedLiveries : [];
  const openButton = $("openOfficialDeletedLiveryTrash");
  const restoreButton = $("restoreOfficialDeletedLivery");
  const removeButton = $("removeOfficialDeletedLivery");
  const removeAllButton = $("removeAllOfficialDeletedLiveries");
  if (openButton) openButton.disabled = !selected || !selected.path;
  if (restoreButton) restoreButton.disabled = !selected || !selected.canRestore;
  if (removeButton) removeButton.disabled = !selected;
  if (removeAllButton) removeAllButton.disabled = state.officialDeletedLiveriesLoading || !items.length;
}

function renderOfficialDeletedLiveries() {
  const list = $("officialDeletedLiveriesList");
  if (!list) return;
  list.innerHTML = "";
  list.className = "deleted-liveries-list";
  if (state.officialDeletedLiveriesLoading) {
    const empty = document.createElement("div");
    empty.className = "deleted-liveries-empty";
    empty.textContent = "Loading deleted official liveries...";
    list.appendChild(empty);
    setOfficialDeletedLiveryActions();
    return;
  }
  const items = Array.isArray(state.officialDeletedLiveries) ? state.officialDeletedLiveries : [];
  if (!items.length) {
    const empty = document.createElement("div");
    empty.className = "deleted-liveries-empty";
    empty.textContent = "No deleted official liveries found.";
    list.appendChild(empty);
    state.selectedOfficialDeletedLiveryId = "";
    setOfficialDeletedLiveryActions();
    return;
  }
  if (!items.some((item) => String(item.id || "") === state.selectedOfficialDeletedLiveryId)) {
    state.selectedOfficialDeletedLiveryId = String(items[0]?.id || "");
  }
  items.forEach((item) => {
    const selected = String(item.id || "") === state.selectedOfficialDeletedLiveryId;
    const row = document.createElement("button");
    row.type = "button";
    row.className = `deleted-livery-row${selected ? " is-selected" : ""}${item.canRestore ? "" : " has-conflict"}`;
    row.dataset.trashId = item.id || "";
    row.innerHTML = `
      <span class="deleted-livery-main">
        <strong>${escapeHtml(item.displayName || item.skinId || item.id || "Deleted official livery")}</strong>
        <span>${escapeHtml(item.carModel || "Unknown car")} / ${escapeHtml(item.skinId || "Unknown skin")}</span>
      </span>
      <span class="deleted-livery-meta">
        <span>${escapeHtml(formatDeletedLiveryDate(item.trashedAt))}</span>
        <span>${item.incomplete ? "Incomplete" : item.officialExternalRole === "project" ? "Project files" : "Local files"}</span>
      </span>
    `;
    row.addEventListener("click", () => {
      state.selectedOfficialDeletedLiveryId = String(item.id || "");
      renderOfficialDeletedLiveries();
    });
    list.appendChild(row);
    const detail = document.createElement("div");
    detail.className = `deleted-livery-detail${selected ? " is-open" : ""}`;
    detail.hidden = !selected;
    const conflicts = Array.isArray(item.conflicts) ? item.conflicts.filter(Boolean) : [];
    detail.innerHTML = `
      <div><strong>Status</strong><span>${item.incomplete ? "Incomplete trash entry" : item.canRestore ? "Ready to restore locally" : "Restore blocked"}</span></div>
      <div><strong>Trash</strong><code>${escapeHtml(item.path || "")}</code></div>
      ${conflicts.length ? `<div><strong>Conflicts</strong><span>${conflicts.map(escapeHtml).join("<br>")}</span></div>` : ""}
    `;
    list.appendChild(detail);
  });
  setOfficialDeletedLiveryActions();
}

async function refreshOfficialDeletedLiveries({ silent = false } = {}) {
  if (!window.pywebview?.api?.list_trashed_official_my_skins) {
    const result = { ok: false, error: "Deleted Official Liveries runtime API is not available. Rebuild or resync the release runtime before testing restore actions." };
    if (!silent) showResult(result, "Deleted official liveries unavailable", "list_trashed_official_my_skins");
    return result;
  }
  state.officialDeletedLiveriesLoading = true;
  renderOfficialDeletedLiveries();
  try {
    const result = await window.pywebview.api.list_trashed_official_my_skins();
    if (!result?.ok) {
      if (!silent) showResult(result, "Deleted official liveries could not be loaded", "list_trashed_official_my_skins");
      state.officialDeletedLiveries = [];
      return result;
    }
    state.officialDeletedLiveries = Array.isArray(result.data?.items) ? result.data.items : [];
    if (!silent) showResult(result, "Deleted official liveries loaded", "list_trashed_official_my_skins");
    return result;
  } catch (error) {
    const result = { ok: false, error: error?.message || String(error) };
    if (!silent) showResult(result, "Deleted official liveries could not be loaded", "list_trashed_official_my_skins");
    state.officialDeletedLiveries = [];
    return result;
  } finally {
    state.officialDeletedLiveriesLoading = false;
    renderOfficialDeletedLiveries();
  }
}

async function restoreSelectedOfficialDeletedLivery() {
  const selected = selectedOfficialDeletedLivery();
  if (!selected) return;
  const confirmed = await showConfirmModal({
    type: "warning",
    title: "Restore deleted official livery",
    subtitle: selected.displayName || selected.skinId || "Deleted official livery",
    message: "This restores the local Official livery to My Liveries as inactive. It does not write into AC EVO or Saved Games.",
    details: [
      "Restore action:",
      "- Restore the local Official library/project folder.",
      "- Add the livery back to My Liveries.",
      "- Keep the livery inactive in game.",
    ].join("\n"),
    confirmLabel: "Restore to My Liveries",
    loadingLabel: "Restoring...",
    onConfirm: () => window.pywebview.api.restore_trashed_official_my_skin({ trashId: selected.id }),
  });
  if (!confirmed) return;
  showResult(confirmed, "Deleted official livery restored", "restore_trashed_official_my_skin");
  if (!confirmed.ok) return;
  await loadMySkins();
  await refreshOfficialDeletedLiveries({ silent: true });
}

async function removeSelectedOfficialDeletedLivery() {
  const selected = selectedOfficialDeletedLivery();
  if (!selected) return;
  const confirmed = await showConfirmModal({
    type: "danger",
    actionLayout: "danger-left",
    title: "Remove deleted official livery",
    subtitle: selected.displayName || selected.skinId || "Deleted official livery",
    message: "This permanently removes this Official livery from the LiveryLab Official trash. It cannot be restored from this app afterwards.",
    details: [
      "Permanent remove action:",
      `- Trash entry: ${selected.id || "-"}`,
      "- Delete the stored local Official folder in this trash entry.",
      "- Delete the trash manifest.",
      "- Do not touch AC EVO or Saved Games files.",
    ].join("\n"),
    confirmLabel: "Remove permanently",
    cancelLabel: "Cancel",
    loadingLabel: "Removing...",
    onConfirm: () => window.pywebview.api.delete_trashed_official_my_skin({ trashId: selected.id }),
  });
  if (!confirmed) return;
  showResult(confirmed, "Deleted official livery removed", "delete_trashed_official_my_skin");
  if (!confirmed.ok) return;
  await refreshOfficialDeletedLiveries({ silent: true });
}

async function removeAllOfficialDeletedLiveries() {
  const items = Array.isArray(state.officialDeletedLiveries) ? [...state.officialDeletedLiveries] : [];
  if (!items.length || !window.pywebview?.api?.delete_trashed_official_my_skin) return;
  const confirmed = await showConfirmModal({
    type: "danger",
    actionLayout: "danger-left",
    title: "Remove all deleted official liveries",
    subtitle: `${items.length} deleted ${items.length === 1 ? "entry" : "entries"}`,
    message: "This permanently removes every listed Official livery from the LiveryLab Official trash. These entries cannot be restored from this app afterwards.",
    details: [
      "Permanent remove action:",
      `- Delete ${items.length} listed Official trash ${items.length === 1 ? "entry" : "entries"}.`,
      "- Delete the stored local Official folders and trash manifests.",
      "- Do not touch AC EVO ExternalLiveries or Saved Games files.",
      "- Report every entry that cannot be removed.",
    ].join("\n"),
    confirmLabel: "Remove all permanently",
    cancelLabel: "Cancel",
    loadingLabel: "Removing all...",
    onConfirm: async () => {
      const deleted = [];
      const errors = [];
      let fileCount = 0;
      for (const item of items) {
        try {
          const result = await window.pywebview.api.delete_trashed_official_my_skin({ trashId: item.id });
          if (!result?.ok) {
            errors.push(`${item.displayName || item.skinId || item.id}: ${result?.error || result?.message || "Unknown error"}`);
            continue;
          }
          deleted.push(item.id);
          fileCount += Number(result.data?.fileCount || 0);
        } catch (error) {
          errors.push(`${item.displayName || item.skinId || item.id}: ${error?.message || String(error)}`);
        }
      }
      const message = [
        `Deleted official trash entries: ${deleted.length}/${items.length}`,
        `Files deleted: ${fileCount}`,
        errors.length ? `Failed entries: ${errors.length}` : "Failed entries: 0",
      ].join("\n");
      return {
        ok: errors.length === 0,
        data: { deleted, deletedCount: deleted.length, requestedCount: items.length, fileCount, errors },
        message,
        error: errors.length ? `${message}\n${errors.join("\n")}` : "",
      };
    },
  });
  if (!confirmed) return;
  showResult(confirmed, "Deleted official liveries removed", "delete_trashed_official_my_skin");
  if (confirmed.data?.deletedCount) state.selectedOfficialDeletedLiveryId = "";
  await refreshOfficialDeletedLiveries({ silent: true });
}

async function openSelectedOfficialDeletedLiveryTrash() {
  const selected = selectedOfficialDeletedLivery();
  if (!selected?.path || !window.pywebview?.api?.open_folder) return;
  const result = await window.pywebview.api.open_folder(selected.path);
  showResult(result, "Official trash folder opened", "open_folder");
}

async function confirmRestoreAceDefaultState() {
  const root = $("rootPath")?.value.trim() || $("homeRootPath")?.value.trim() || "";
  if (!root) {
    showResult({ ok: false, error: "Select the AC EVO root folder before restoring the default state." }, "AC EVO restore unavailable", "restore_ace_default_state");
    return;
  }
  const firstConfirmed = await showConfirmModal({
    type: "warning",
    title: "Restore AC EVO to default",
    subtitle: "This affects game files and user garage SavedCars",
    message: "LiveryLab will restore AC EVO livery content back toward the default state. This includes permanently deleting LiveryLab-managed custom skin files from the game folder, deleting matching SavedCars from the AC EVO user garage and removing LiveryLab Custom Grid game hooks.",
    details: [
      "Planned restore workflow:",
      "- AC EVO must be closed before the action runs.",
      "- content.kspkg will be restored when LiveryLab previously disabled it.",
      "- Known LiveryLab-managed custom skin files will be deleted without backup.",
      "- Loose actor, mechanical preset and .loc references created by LiveryLab will be cleaned without backup.",
      "- Custom Grid Bridge hooks, exported grid files and AI name cleanup hooks will be removed without backup.",
      "- LiveryLab AI grid specs and custom tyre brand game files will be removed without backup.",
      "- User garage SavedCars that reference LiveryLab custom skins will be deleted without backup.",
      "- A manifest will be written so the restore action can be reviewed afterwards.",
      "",
      "SavedCars that do not reference LiveryLab-managed custom skins should not be touched.",
    ].join("\n"),
    confirmLabel: "I understand",
    cancelLabel: "Cancel",
  });
  if (!firstConfirmed) return;

  const finalConfirmed = await showConfirmModal({
    type: "danger",
    actionLayout: "danger-left",
    title: "Final confirmation",
    subtitle: "Restore AC EVO and remove matching user garage SavedCars",
    message: "This is the final checkpoint. Continue only if AC EVO is closed and you understand that matching LiveryLab game files and user garage SavedCars will be permanently deleted.",
    details: [
      "Final action:",
      "- Restore AC EVO package state.",
      "- Delete LiveryLab-managed custom skin files from the game folder without backup.",
      "- Remove LiveryLab Custom Grid Bridge, AI name cleanup and AI grid spec game files without backup.",
      "- Delete LiveryLab custom tyre brand game files without backup.",
      "- Delete matching AC EVO user garage SavedCars without backup.",
      "- Remove only LiveryLab-owned .loc entries.",
      "- Leave unrelated SavedCars untouched.",
      "",
      "Local LiveryLab library data is kept so skins can be activated again later.",
    ].join("\n"),
    confirmLabel: "Restore AC EVO to default state",
    cancelLabel: "Cancel",
    loadingLabel: "Restoring...",
    onConfirm: () => window.pywebview.api.restore_ace_default_state(root),
  });
  if (!finalConfirmed) return;
  showResult(finalConfirmed, "AC EVO restore completed", "restore_ace_default_state");
  if (!finalConfirmed.ok) return;
  await loadMySkins();
  await updateRootStatus(root);
  await updateContentPackageStatus(root);
  setInspectOutputText(finalConfirmed.message || "");
}

function escapeHtml(value) {
  return String(value ?? "")
    .replaceAll("&", "&amp;")
    .replaceAll("<", "&lt;")
    .replaceAll(">", "&gt;")
    .replaceAll('"', "&quot;");
}


function wireEvents() {
  document.addEventListener("keydown", stopPreviewCameraKeyboardLeak, true);
  document.addEventListener("keyup", stopPreviewCameraKeyboardLeak, true);
  document.addEventListener("keypress", stopPreviewCameraKeyboardLeak, true);
  document.addEventListener("wheel", stopPreviewCameraKeyboardLeak, true);
  document.querySelectorAll(".nav-button[data-tab]").forEach((button) => {
    button.addEventListener("click", () => setTab(button.dataset.tab));
  });
  document.querySelectorAll("[data-open-tab]").forEach((button) => {
    button.addEventListener("click", () => setTab(button.dataset.openTab));
  });
  document.querySelectorAll("[data-focus-target]").forEach((button) => {
    button.addEventListener("click", () => {
      const target = $(button.dataset.focusTarget);
      if (!target) return;
      target.focus();
      target.scrollIntoView({ behavior: "smooth", block: "center" });
    });
  });
  document.querySelectorAll("[data-pick]").forEach((button) => {
    button.addEventListener("click", () => choose("file", button.dataset.pick, button.dataset.filter));
  });
  document.querySelectorAll("[data-save]").forEach((button) => {
    button.addEventListener("click", () => choose("save", button.dataset.save, button.dataset.filter, button.dataset.name));
  });
  document.querySelectorAll("[data-folder]").forEach((button) => {
    button.addEventListener("click", () => choose("folder", button.dataset.folder));
  });
  setupPathFileNames();
  setupConverterPathDefaults();
  document.querySelectorAll(".uv-control-tab").forEach((button) => {
    button.addEventListener("click", () => setUvControlTab(button.dataset.uvTab || "setup"));
  });

  $("pickRoot").addEventListener("click", () => choose("folder", "rootPath"));
  addRimCardesignPathRow();
  $("addRimCardesignPath").addEventListener("click", () => addRimCardesignPathRow());
  $("pickHomeRoot").addEventListener("click", async () => {
    const result = await window.pywebview.api.choose_path("folder", ["All files (*.*)"], "");
    if (!showResult(result, t("log_path_selected")) || !result.data.path) return;
    await saveDefaultRoot(result.data.path);
    await refreshBrowser("");
  });
  $("refreshCarCompatibility")?.addEventListener("click", () => loadCarCompatibility(true));
  $("createUpdateBackup")?.addEventListener("click", () => createUpdateBackup());
  $("openLogsFolder")?.addEventListener("click", async () => {
    const result = await call("open_logs_folder");
    if (result?.ok) await refreshLogStatus();
  });
  $("importUpdateBackup")?.addEventListener("click", () => importUpdateBackup());
  $("restoreAceDefaultState")?.addEventListener("click", () => confirmRestoreAceDefaultState());
  $("refreshDeletedLiveries")?.addEventListener("click", () => refreshDeletedLiveries());
  $("openDeletedLiveryTrash")?.addEventListener("click", () => openSelectedDeletedLiveryTrash());
  $("restoreDeletedLivery")?.addEventListener("click", () => restoreSelectedDeletedLivery());
  $("removeDeletedLivery")?.addEventListener("click", () => removeSelectedDeletedLivery());
  $("removeAllDeletedLiveries")?.addEventListener("click", () => removeAllDeletedLiveries());
  $("refreshOfficialDeletedLiveries")?.addEventListener("click", () => refreshOfficialDeletedLiveries());
  $("openOfficialDeletedLiveryTrash")?.addEventListener("click", () => openSelectedOfficialDeletedLiveryTrash());
  $("restoreOfficialDeletedLivery")?.addEventListener("click", () => restoreSelectedOfficialDeletedLivery());
  $("removeOfficialDeletedLivery")?.addEventListener("click", () => removeSelectedOfficialDeletedLivery());
  $("removeAllOfficialDeletedLiveries")?.addEventListener("click", () => removeAllOfficialDeletedLiveries());
  $("homeAnalyzeCars")?.addEventListener("click", () => loadCarCompatibility(true));
  $("runHomeStorageHealth")?.addEventListener("click", () => runStorageHealthCheck());
  $("refreshRuntimeProfile")?.addEventListener("click", () => refreshRuntimeProfile({ silent: false }));
  $("refreshOfficialLiveries")?.addEventListener("click", () => refreshOfficialExternalLiveries({ silent: false }));
  $("prepareOfficialRuntime")?.addEventListener("click", async () => {
    const result = await prepareWorkflowRuntime("official");
    if (!result?.ready) return;
    const root = normalizeRootPath($("homeRootPath")?.value.trim() || $("rootPath")?.value.trim() || state.root || "");
    if (root) await updateRootStatus(root);
    renderOfficialRuntimeStatus();
    renderOfficialCarResourcesStatus(state.officialCarResourcesStatus);
    applySetupGateStatus();
  });
  document.querySelectorAll("[data-workflow-choice]").forEach((button) => {
    button.addEventListener("click", () => chooseWorkflow(button.dataset.workflowChoice || "advanced"));
  });
  document.querySelectorAll("[data-my-skins-scope]").forEach((button) => {
    button.addEventListener("click", () => setMySkinsWorkflowScope(button.dataset.mySkinsScope || "advanced"));
  });
  $("quitApp").addEventListener("click", () => window.pywebview.api.close_app());
  $("statusModalClose").addEventListener("click", () => closeStatusModal());
  $("statusModalCloseX").addEventListener("click", () => closeStatusModal());
  $("statusModalBackdrop").addEventListener("click", () => closeStatusModal());
  document.addEventListener("click", (event) => {
    const control = event.target?.closest?.("[data-long-paths-action]");
    if (!control) return;
    event.preventDefault();
    handleLongPathsSetupAction(control.dataset.longPathsAction || "");
  });
  $("packageExtractModalCancel")?.addEventListener("click", () => cancelPackageExtraction());
  $("statusModalOpenFolder")?.addEventListener("click", async () => {
    const target = state.statusModalOpenFolderPath;
    if (!target) return;
    const result = await window.pywebview.api.open_folder(target);
    showResult(result, "Folder opened", "open_folder");
  });
  $("confirmModalConfirm").addEventListener("click", () => closeConfirmModal(true));
  $("confirmModalCancel").addEventListener("click", () => closeConfirmModal(false));
  $("confirmModalAlternate")?.addEventListener("click", () => closeConfirmModal("alternate"));
  $("confirmModalCloseX").addEventListener("click", () => closeConfirmModal(false));
  $("confirmModalBackdrop").addEventListener("click", () => closeConfirmModal(false));
  $("numberplateGeneratorInfoConfirm")?.addEventListener("click", () => closeNumberplateGeneratorInfoModal());
  $("layerStudioInfoConfirm")?.addEventListener("click", () => closeLayerStudioInfoModal());
  $("liveryPreviewInfoConfirm")?.addEventListener("click", () => closeLiveryPreviewInfoModal());
  $("driverProfilesInfoConfirm")?.addEventListener("click", () => closeDriverProfilesInfoModal());
  $("customGridBridgeGuideConfirm")?.addEventListener("click", () => {
    window.LiveryLabCustomGrids?.closeBridgeGuideModal?.();
  });
  $("confirmModalInput").addEventListener("keydown", (event) => {
    if (event.key === "Enter") {
      event.preventDefault();
      closeConfirmModal(true);
    }
  });
  document.addEventListener("keydown", (event) => {
    if (event.key === "Escape" && state.confirmModalResolver) {
      event.preventDefault();
      closeConfirmModal(false);
      return;
    }
    if (event.key === "Escape" && state.statusModalOpen) {
      event.preventDefault();
      closeStatusModal();
    }
  });
  window.addEventListener("focus", () => ensureBrowserLoadedIfEmpty());
  document.addEventListener("visibilitychange", () => {
    if (!document.hidden) ensureBrowserLoadedIfEmpty();
  });
  $("saveRoot").addEventListener("click", async () => {
    const root = normalizeRootPath($("rootPath").value);
    await saveDefaultRoot(root);
  });
  $("saveHomeRoot").addEventListener("click", async () => {
    const root = normalizeRootPath($("homeRootPath").value);
    await saveDefaultRoot(root);
  });
  $("homeRootPath").addEventListener("input", () => {
    updateHomeRootSavePulse();
  });
  $("homeRootPath").addEventListener("change", () => {
    const root = normalizeRootPath($("homeRootPath").value);
    syncRootFields(root);
  });
  $("homePackagePath")?.addEventListener("input", syncHomePackageStatus);
  $("homePackagePath")?.addEventListener("change", syncHomePackageStatus);
  $("homePackageOutDir")?.addEventListener("input", syncHomePackageStatus);
  $("homePackageOutDir")?.addEventListener("change", syncHomePackageStatus);
  $("rootPath").addEventListener("change", () => {
    const root = normalizeRootPath($("rootPath").value);
    syncRootFields(root);
  });
  $("refreshBrowser").addEventListener("click", () => refreshBrowser());
  $("goBrowserFavorite").addEventListener("click", () => openSelectedBrowserFavorite());
  $("toggleUvBrowser").addEventListener("click", () => toggleUvBrowser());
  $("useSelected").addEventListener("click", useSelected);
  $("inspectSearch").addEventListener("input", (event) => {
    state.inspectSearch = event.target.value.trim();
    state.inspectMatchIndex = 0;
    renderInspectOutput();
  });
  $("inspectSearch").addEventListener("keydown", (event) => {
    if (event.key === "Enter") {
      event.preventDefault();
      stepInspectSearch(event.shiftKey ? -1 : 1);
    }
  });
  $("useSelectedForExport").addEventListener("click", () => applySelectionForExport());
  $("useSelectedForImport").addEventListener("click", () => applySelectionForImport());
  document.querySelectorAll("[data-use-as]").forEach((button) => {
    button.addEventListener("click", async () => {
      if (!state.selected || state.selected.isDir) return;
      if (browserItemBlockedForCurrentMode(state.selected)) {
        showBrowserLockedNotice();
        return;
      }
      setPath(button.dataset.useAs, state.selected.path);
      await applyPathDefaults(button.dataset.useAs, state.selected.path);
    });
  });

  $("exportDds").addEventListener("click", async () => {
    const texture = $("texturePath").value.trim();
    const mips = $("mipsPath").value.trim();
    const out = $("ddsOut").value.trim();
    if (!ensureTextureConverterPathsAllowed([
      { label: "Texture descriptor", path: texture },
      { label: "Texturemips input", path: mips },
      { label: "DDS output", path: out },
    ])) return;
    await confirmFreeOutputIfInGameRoot({
      target: out,
      title: "Write texture output inside AC EVO installation folder",
      message: "The selected DDS output target is inside the AC EVO installation folder. Continue?",
      writes: ["Write the DDS output file."],
      confirmLabel: "Export",
      loadingLabel: "Exporting...",
      onConfirm: () => call("convert_to_dds", texture, mips, out, $("ddsFormat").value, $("rootPath").value.trim()),
    });
  });
  $("exportPng").addEventListener("click", async () => {
    const texture = $("texturePath").value.trim();
    const mips = $("mipsPath").value.trim();
    const out = $("pngOut").value.trim();
    if (!ensureTextureConverterPathsAllowed([
      { label: "Texture descriptor", path: texture },
      { label: "Texturemips input", path: mips },
      { label: "PNG output", path: out },
    ])) return;
    await confirmFreeOutputIfInGameRoot({
      target: out,
      title: "Write texture output inside AC EVO installation folder",
      message: "The selected PNG output target is inside the AC EVO installation folder. Continue?",
      writes: ["Write the PNG output file."],
      confirmLabel: "Export",
      loadingLabel: "Exporting...",
      onConfirm: () => call("convert_to_png", texture, mips, out, $("rootPath").value.trim()),
    });
  });
  $("importMips").addEventListener("click", async () => {
    const out = $("mipsOut").value.trim();
    const dds = $("ddsIn").value.trim();
    const png = $("pngIn")?.value.trim() || "";
    const root = $("rootPath").value.trim();
    if (!ensureTextureConverterPathsAllowed([
      { label: "Back texture descriptor", path: $("backTexture").value.trim() },
      { label: "DDS input", path: dds },
      { label: "PNG input", path: png },
      { label: "Texturemips output", path: out },
    ])) return;
    await confirmFreeOutputIfInGameRoot({
      target: out,
      title: "Write texturemips inside AC EVO installation folder",
      message: "The selected .texturemips output target is inside the AC EVO installation folder. Continue?",
      writes: ["Write the .texturemips output file."],
      confirmLabel: "Write mips",
      loadingLabel: "Writing mips...",
      onConfirm: () => call("convert_to_texturemips", $("backTexture").value.trim(), dds, out, root, png),
    });
  });
  $("useSelectedForPackage").addEventListener("click", () => applySelectionForPackage());
  $("analyzePackage").addEventListener("click", async () => {
    const path = $("packagePath").value.trim();
    if (!path) {
      log("Please select a .kspkg first", true);
      return;
    }
    const result = await call("inspect_pack_file", path);
    if (!result.ok) return;
    renderPackageSummary(result.data);
    setInspectOutputText(result.data.summary || "");
    setFilePreviewText(result.data.summary || "");
    setTab("inspect");
  });
  $("writePackageList").addEventListener("click", async () => {
    const packPath = $("packagePath").value.trim();
    const outPath = $("packageListOut").value.trim();
    if (!packPath || !outPath) {
      log("Please set both the .kspkg and the file list target", true);
      return;
    }
    const result = await confirmFreeOutputIfInGameRoot({
      target: outPath,
      title: "Write package list inside AC EVO installation folder",
      message: "The selected package-list target is inside the AC EVO installation folder. Continue?",
      writes: ["Write a text listing for the selected .kspkg."],
      confirmLabel: "Write list",
      loadingLabel: "Writing list...",
      onConfirm: () => call("write_pack_file_listing", packPath, outPath, $("rootPath").value.trim()),
    });
    if (!result) return;
    if (!result.ok) return;
    setInspectOutputText(result.message || "");
  });
  $("extractSelectedPackage").addEventListener("click", async () => {
    const packPath = $("packagePath").value.trim();
    const outDir = $("packageOutDir").value.trim();
    const selection = $("packageSelection").value;
    if (!packPath || !outDir || !selection.trim()) {
      log("Please set the .kspkg, output folder and selection paths", true);
      return;
    }
    const confirmed = await confirmFreeOutputIfInGameRoot({
      target: outDir,
      title: "Extract package to AC EVO installation folder",
      message: "Extract selected package entries into the AC EVO installation folder so the game can use them?",
      writes: ["Create folders and files from the selected .kspkg entries."],
      backupNote: "Package extraction writes ACE files directly and may overwrite matching extracted files.",
      confirmLabel: "Extract",
      allowPackageExtractMissingContent: true,
    });
    if (!confirmed) return;
    resetPackageProgress();
    const result = await runPackageExtraction({ packPath, outDir, selection, root: $("rootPath").value.trim(), selectedOnly: true });
    if (!result.ok) return;
    renderPackageSummary(result.data);
    setInspectOutputText(result.message || "");
    resetPackageProgress();
  });
  $("extractPackage").addEventListener("click", async () => {
    const packPath = $("packagePath").value.trim();
    const outDir = $("packageOutDir").value.trim();
    if (!packPath || !outDir) {
      log("Please set the .kspkg and output folder", true);
      return;
    }
    const confirmed = await confirmFreeOutputIfInGameRoot({
      target: outDir,
      title: "Extract package to AC EVO installation folder",
      message: "Extract this package into the AC EVO installation folder so the game can use it?",
      writes: ["Create folders and files from the .kspkg archive."],
      backupNote: "Package extraction writes ACE files directly and may overwrite matching extracted files.",
      confirmLabel: "Extract",
      allowPackageExtractMissingContent: true,
    });
    if (!confirmed) return;
    resetPackageProgress();
    const result = await runPackageExtraction({ packPath, outDir, root: $("rootPath").value.trim() });
    if (!result.ok) return;
    renderPackageSummary(result.data);
    setInspectOutputText(result.message || "");
    resetPackageProgress();
  });
  $("extractHomePackage").addEventListener("click", () => runHomePackageExtraction());
  $("officialExtractCarResources")?.addEventListener("click", () => runOfficialCarResourceExtract());
  $("disableHomeContentPackage")?.addEventListener("click", () => disableContentPackageFlow());
  $("homeLiveryLabSafetyBypass")?.addEventListener("click", (event) => toggleLiveryLabSafetyFlow(event));
  $("homeCarAnalysisBypass")?.addEventListener("click", (event) => toggleCarAnalysisBypass(event));
  $("useSelectedForMesh").addEventListener("click", () => applySelectionForMesh(true));
  $("analyzeMesh").addEventListener("click", async () => {
    const path = $("meshPath").value.trim();
    if (!path) {
      log("Please select a .mesh file first", true);
      return;
    }
    const result = await call("inspect_file", path, $("rootPath").value.trim());
    if (!result.ok) return;
    renderMeshSummary(result.data);
    setInspectOutputText(result.data.summary || "");
    setFilePreviewText(result.data.summary || "");
    setTab("inspect");
  });
  $("exportMeshObj").addEventListener("click", async () => {
    const mesh = $("meshPath").value.trim();
    const out = $("meshObjOut").value.trim();
    if (!mesh || !out) {
      log("Please set the .mesh and OBJ target first", true);
      return;
    }
    const result = await confirmFreeOutputIfInGameRoot({
      target: out,
      title: "Write mesh output inside AC EVO installation folder",
      message: "The selected OBJ/MTL output target is inside the AC EVO installation folder. Continue?",
      writes: ["Write OBJ and MTL export files."],
      confirmLabel: "Export",
      loadingLabel: "Exporting...",
      onConfirm: () => call("export_mesh_obj", mesh, out, $("rootPath").value.trim()),
    });
    if (!result) return;
    if (!result.ok) return;
    setInspectOutputText(result.message || "");
  });
  $("useSelectedForPreview3d")?.addEventListener("click", () => applySelectionForTab("preview3d"));
  $("savePreview3dProject")?.addEventListener("click", () => savePreview3dProject());
  $("loadPreview3dViewer")?.addEventListener("click", () => loadPreview3dViewer());
  $("inspectPreview3dProject")?.addEventListener("click", () => inspectPreview3dProject(true));
  $("reloadPreview3dTexture")?.addEventListener("click", () => reloadPreview3dTexture());
  initLiveryPreviewMenuController();
  $("liveryPreviewBack")?.addEventListener("click", () => closeLiveryPreviewWorkspace());
  $("liveryPreviewOpenProjectFolder")?.addEventListener("click", async () => {
    const folder = state.liveryPreviewProjectFolder || "";
    if (!folder) {
      setLiveryPreviewWatchBadge("No project folder", "error");
      $("liveryPreviewStatus").textContent = "No livery project folder is available for this preview.";
      return;
    }
    const result = await window.pywebview.api.open_folder(folder);
    showResult(result, "Project folder opened");
    if (!result.ok) {
      setLiveryPreviewWatchBadge("Open folder failed", "error");
      $("liveryPreviewStatus").textContent = result.message || "Could not open project folder.";
    }
  });
  $("liveryPreviewReloadTexture")?.addEventListener("click", () => reloadLiveryPreviewTextureFromMenu());
  $("liveryPreviewReloadTextureView")?.addEventListener("click", () => reloadLiveryPreviewTextureFromMenu());
  $("liveryPreviewInspectMaterials")?.addEventListener("click", () => toggleLiveryPreviewMaterialInspector());
  $("liveryPreviewMaterialOverlay")?.addEventListener("click", () => setLiveryPreviewMaterialOverlay(!state.liveryPreviewMaterialOverlayActive));
  $("liveryPreviewApproxMaterials")?.addEventListener("click", () => setLiveryPreviewApproxMaterials(!state.liveryPreviewApproxMaterialsActive));
  $("liveryPreviewBodyBaseColor")?.addEventListener("input", (event) => setLiveryPreviewBodyBaseColor(event.target?.value || "#ffffff", { quiet: true }));
  $("liveryPreviewBodyBaseColor")?.addEventListener("change", (event) => setLiveryPreviewBodyBaseColor(event.target?.value || "#ffffff"));
  $("liveryPreviewDetectBodyBaseColor")?.addEventListener("click", () => detectLiveryPreviewBodyBaseColor());
  $("liveryPreviewSaveBodyBaseColor")?.addEventListener("click", () => saveLiveryPreviewBodyBaseColor());
  $("liveryPreviewToggleMaterialFinish")?.addEventListener("click", () => openLiveryPreviewMaterialFinishPanel());
  $("liveryPreviewMaterialFinishClose")?.addEventListener("click", () => setLiveryPreviewMaterialFinishPanelVisible(false));
  $("liveryPreviewStatusToggle")?.addEventListener("click", () => toggleLiveryPreviewStatusOutput());
  syncLiveryPreviewStatusCollapse();
  $("liveryPreviewMaterialFinishSave")?.addEventListener("click", async () => {
    await confirmAndSaveLiveryPreviewMaterialFinish({
      buttonId: "liveryPreviewMaterialFinishSave",
      source: "liveryPreviewSave",
      statusId: "liveryPreviewStatus",
    });
  });
  document.querySelectorAll("[data-material-finish-field]").forEach((input) => {
    input.addEventListener("input", (event) => {
      const field = event.target?.dataset?.materialFinishField || "";
      if (!field) return;
      setLiveryPreviewMaterialFinish({ [field]: event.target.value }, { quiet: true });
    });
    input.addEventListener("change", (event) => {
      const field = event.target?.dataset?.materialFinishField || "";
      if (!field) return;
      setLiveryPreviewMaterialFinish({ [field]: event.target.value });
    });
  });
  document.querySelectorAll("[data-material-finish-preset]").forEach((button) => {
    button.addEventListener("click", () => {
      const preset = LIVERY_MATERIAL_FINISH_PRESETS[button.dataset.materialFinishPreset || ""];
      if (preset) setLiveryPreviewMaterialFinish(preset);
    });
  });
  $("liveryPreviewMaterialSearch")?.addEventListener("input", (event) => {
    state.liveryPreviewMaterialFilter.query = event.target?.value || "";
    if (state.liveryPreviewMaterialInspection) renderPreviewMaterialPanel(state.liveryPreviewMaterialInspection);
  });
  $("liveryPreviewMaterialMappedOnly")?.addEventListener("change", (event) => {
    state.liveryPreviewMaterialFilter.mappedOnly = Boolean(event.target?.checked);
    if (state.liveryPreviewMaterialInspection) renderPreviewMaterialPanel(state.liveryPreviewMaterialInspection);
  });
  $("liveryPreviewExportWheelTextures")?.addEventListener("click", () => exportLiveryPreviewWheelTextures());
  document.querySelectorAll("[data-material-filter]").forEach((button) => {
    button.addEventListener("click", () => {
      state.liveryPreviewMaterialFilter.category = button.dataset.materialFilter || "all";
      if (state.liveryPreviewMaterialInspection) renderPreviewMaterialPanel(state.liveryPreviewMaterialInspection);
      else syncPreviewMaterialFilterControls();
    });
  });
  $("liveryPreviewTextureSlotList")?.addEventListener("click", (event) => {
    const button = event.target?.closest?.("[data-livery-preview-texture-key]");
    if (!button) return;
    setLiveryPreviewTextureKey(button.dataset.liveryPreviewTextureKey || "ext_skin_c");
  });
  document.querySelectorAll("[data-livery-preview-mode]").forEach((button) => {
    button.addEventListener("click", () => setLiveryPreviewPreviewMode(button.dataset.liveryPreviewMode || "materialView"));
  });
  $("liveryPreviewUseAoTexture")?.addEventListener("change", (event) => {
    setLiveryPreviewAoEnabled(Boolean(event.target?.checked));
  });
  $("liveryPreviewGenerateAoTexture")?.addEventListener("click", () => generateLiveryPreviewAoTexture());
  $("liveryPreviewImportTextures")?.addEventListener("click", async () => {
    if (state.liveryPreviewReadOnly) {
      setLiveryPreviewWatchBadge("Read-only preview", "watching");
      $("liveryPreviewStatus").textContent = "Texture import is disabled for read-only installed liveries. The preview cache is prepared automatically from the installed skin.";
      return;
    }
    const skinKey = state.liveryPreviewSkinKey || "";
    if (!skinKey) {
      setLiveryPreviewWatchBadge("No livery selected", "error");
      $("liveryPreviewStatus").textContent = "No livery is attached to this preview workspace.";
      return;
    }
    setLiveryPreviewWatchBadge("Importing textures", "reloading");
    $("liveryPreviewStatus").textContent = "Importing preview textures from installed skin...";
    const result = await window.pywebview.api.import_livery_preview_textures({
      skinKey,
      root: state.root,
      overwrite: Boolean($("liveryPreviewImportOverwrite")?.checked),
    });
    if (!result.ok) {
      setLiveryPreviewWatchBadge("Import failed", "error");
      $("liveryPreviewStatus").textContent = `Preview texture import failed:\n${result.message || ""}`;
      return;
    }
    applyPreview3dProject(result.data?.project || {});
    renderPreviewTextureImportStatus(result.data || {});
    await reloadPreview3dTexture({ reloadViewer: false });
    if (window.LiveryLabPreview3d?.reloadTexture) {
      try {
        await reloadLiveryPreviewViewerTextureWithMaterialMode();
      } catch (error) {
        log(error?.message || String(error), true);
      }
    }
    setLiveryPreviewWatchBadge("Watching livery PNGs", "watching");
    scheduleLiveryPreviewWatcherPoll();
  });
  $("liveryPreviewToggleNumberplate")?.addEventListener("click", () => {
    if (liveryPreviewNumberplatesBlocked()) {
      $("liveryPreviewStatus").textContent = normalizeNumberplateSupport(state.liveryPreviewNumberplateSupport || state.liveryPreviewProject || {}).message
        || "This car does not support numberplates yet";
      return;
    }
    if (liveryPreviewNumberplatesHidden()) {
      $("liveryPreviewStatus").textContent = "Numberplates are disabled for this skin.";
      return;
    }
    const panel = $("liveryPreviewNumberplatePanel");
    if (panel && !panel.hidden) {
      setLiveryPreviewNumberplatePanelVisible(false);
      return;
    }
    prepareNumberplateOverlayPanel({ openPanel: true, loadSelected: false, removeOverlay: false }).then(async () => {
      const overlayState = window.LiveryLabPreview3d?.hasNumberplateOverlay?.() || {};
      if (overlayState.mode !== "mapped-group" && $("liveryPreviewShowNumberplates")?.checked !== false) {
        await showMappedNumberplates();
      }
    });
  });
  $("liveryPreviewToggleNumberplateDev")?.addEventListener("click", () => {
    if (!debugToolEnabled("numberplateCalibration")) return;
    const panel = $("liveryPreviewNumberplateDevPanel");
    if (panel && !panel.hidden) {
      setLiveryPreviewNumberplateDevPanelVisible(false);
      return;
    }
    prepareNumberplateOverlayPanel({ openPanel: false, loadSelected: false, removeOverlay: false }).then(async () => {
      setLiveryPreviewNumberplateDevPanelVisible(true);
      const overlayState = window.LiveryLabPreview3d?.hasNumberplateOverlay?.() || {};
      if (overlayState.mode !== "mapped-group" && $("liveryPreviewShowNumberplates")?.checked !== false) {
        await showMappedNumberplates();
      }
    });
  });
  $("liveryPreviewToggleSceneSettings")?.addEventListener("click", () => {
    if (!debugToolEnabled("sceneSettings")) return;
    const panel = $("liveryPreviewSceneSettingsPanel");
    setLiveryPreviewSceneSettingsPanelVisible(panel?.hidden !== false);
  });
  $("liveryPreviewNumberplateDevClose")?.addEventListener("click", () => setLiveryPreviewNumberplateDevPanelVisible(false));
  $("liveryPreviewSceneSettingsClose")?.addEventListener("click", () => setLiveryPreviewSceneSettingsPanelVisible(false));
  $("liveryPreviewSceneSettingsSave")?.addEventListener("click", () => saveLiveryPreviewSceneSettings());
  $("liveryPreviewSceneSettingsReset")?.addEventListener("click", () => resetLiveryPreviewSceneSettingsScene());
  $("liveryPreviewSceneSettingsScene")?.addEventListener("change", (event) => {
    const scene = previewSceneName(event.target?.value || "light");
    setLiveryPreviewScene(scene, { quiet: true });
    renderLiveryPreviewSceneSettingsPanel();
  });
  [
    "liveryPreviewSceneBackground",
    "liveryPreviewSceneFloorColor",
    "liveryPreviewSceneFloorVisible",
    "liveryPreviewSceneExposure",
    "liveryPreviewSceneHemiIntensity",
    "liveryPreviewSceneKeyIntensity",
    "liveryPreviewSceneFillIntensity",
    "liveryPreviewSceneRimIntensity",
    "liveryPreviewSceneSoftShadowVisible",
    "liveryPreviewSceneContactShadow",
    "liveryPreviewSceneRealShadowEnabled",
    "liveryPreviewSceneRealShadowOpacity",
    "liveryPreviewSceneRealShadowRadius",
    "liveryPreviewSceneRealShadowBias",
    "liveryPreviewSceneRealShadowNormalBias",
    "liveryPreviewSceneRealShadowMapSize",
    "liveryPreviewSceneRealShadowCameraSize",
    "liveryPreviewSceneEnvironmentEnabled",
    "liveryPreviewSceneEnvironmentIntensity",
    "liveryPreviewSceneKeyX",
    "liveryPreviewSceneKeyY",
    "liveryPreviewSceneKeyZ",
    "liveryPreviewSceneFillX",
    "liveryPreviewSceneFillY",
    "liveryPreviewSceneFillZ",
    "liveryPreviewSceneRimX",
    "liveryPreviewSceneRimY",
    "liveryPreviewSceneRimZ",
    "liveryPreviewSceneEvoApprox",
    "liveryPreviewSceneMaskThreshold",
    "liveryPreviewScenePaintBrightness",
    "liveryPreviewScenePaintSpecular",
    "liveryPreviewScenePaintLightInfluence",
    "liveryPreviewScenePaintRimStrength",
    "liveryPreviewScenePaintHighlightStrength",
    "liveryPreviewScenePaintReflectionStrength",
    "liveryPreviewScenePaintStripStrength",
    "liveryPreviewScenePaintStripSharpness",
    "liveryPreviewScenePaintToneMapped",
    "liveryPreviewSceneCarbonScale",
    "liveryPreviewSceneCarbonBrightness",
    "liveryPreviewSceneMatteColor",
    "liveryPreviewSceneGlassOpacity",
    "liveryPreviewSceneLightEmissive",
    "liveryPreviewSceneRimMetalness",
    "liveryPreviewSceneTyreRoughness",
  ].forEach((id) => {
    $(id)?.addEventListener("input", () => applyLiveryPreviewSceneSettingsFromInputs());
    $(id)?.addEventListener("change", () => applyLiveryPreviewSceneSettingsFromInputs());
  });
  $("liveryPreviewNumberplateClose")?.addEventListener("click", () => {
    setLiveryPreviewNumberplatePanelVisible(false);
  });
  $("liveryPreviewHideNumberplate")?.addEventListener("click", () => {
    setLiveryPreviewNumberplatesVisible(false);
    state.liveryPreviewNumberplateGizmoEnabled = false;
    updateNumberplateGizmoUnsavedIndicator(window.LiveryLabPreview3d?.setNumberplateTunerEnabled?.(false) || { dirty: false });
    window.LiveryLabPreview3d?.removeNumberplateOverlay?.();
    $("liveryPreviewNumberplateInfo").textContent = "Overlay hidden.";
  });
  $("liveryPreviewShowNumberplates")?.addEventListener("change", async (event) => {
    try {
      if (liveryPreviewNumberplatesBlocked()) {
        event.target.checked = false;
        $("liveryPreviewStatus").textContent = normalizeNumberplateSupport(state.liveryPreviewNumberplateSupport || state.liveryPreviewProject || {}).message
          || "This car does not support numberplates yet";
        return;
      }
      if (liveryPreviewNumberplatesHidden()) {
        event.target.checked = false;
        $("liveryPreviewStatus").textContent = "Numberplates are disabled for this skin.";
        return;
      }
      await toggleMappedNumberplates(Boolean(event.target?.checked));
    } catch (error) {
      setLiveryPreviewNumberplatesVisible(false);
      $("liveryPreviewStatus").textContent = `Registered numberplates failed:\n${error?.message || error}`;
    }
  });
  $("liveryPreviewDiagRawPlacements")?.addEventListener("click", () => runNumberplatePlacementDiagnostics("raw"));
  $("liveryPreviewDiagMappedPlacements")?.addEventListener("click", () => runNumberplatePlacementDiagnostics("mapped"));
  $("liveryPreviewDiagComparePlacements")?.addEventListener("click", () => runNumberplatePlacementDiagnostics("compare"));
  $("liveryPreviewDiagRoundtrip")?.addEventListener("click", () => runNumberplateRoundtripDiagnostics());
  $("liveryPreviewCopyNumberplateCalibration")?.addEventListener("click", () => copySelectedNumberplateCalibrationRow());
  $("liveryPreviewShowMappedNumberplates")?.addEventListener("click", () => showMappedNumberplates());
  $("liveryPreviewShowActorGhost")?.addEventListener("click", () => showNumberplateActorGhostOverlay());
  $("liveryPreviewActorProjectionOffsetApply")?.addEventListener("click", () => showNumberplateActorGhostOverlay());
  $("liveryPreviewActorProjectionOffsetReset")?.addEventListener("click", () => resetNumberplateActorProjectionOffset());
  ["liveryPreviewActorProjectionOffsetX", "liveryPreviewActorProjectionOffsetY", "liveryPreviewActorProjectionOffsetZ"].forEach((id) => {
    $(id)?.addEventListener("change", () => readNumberplateActorProjectionOffsetInputs());
  });
  $("liveryPreviewActorGhostMinus")?.addEventListener("click", () => nudgeSelectedNumberplateActorGhost(-1));
  $("liveryPreviewActorGhostPlus")?.addEventListener("click", () => nudgeSelectedNumberplateActorGhost(1));
  $("liveryPreviewActorGhostResetSelected")?.addEventListener("click", () => resetSelectedNumberplateActorGhost());
  $("liveryPreviewActorGhostResetAll")?.addEventListener("click", () => resetAllNumberplateActorGhostOffsets());
  $("liveryPreviewActorGhostField")?.addEventListener("change", () => renderNumberplateActorGhostOffsetSummary());
  $("liveryPreviewNumberplateFitApply")?.addEventListener("click", () => refreshNumberplateFitPreview());
  $("liveryPreviewNumberplateFitReset")?.addEventListener("click", () => resetNumberplateFitControls());
  $("liveryPreviewNumberplateFitCopy")?.addEventListener("click", () => copyNumberplateFitValues());
  $("liveryPreviewNumberplateDebugLabels")?.addEventListener("change", (event) => {
    state.liveryPreviewNumberplateDebugLabels = event.target?.checked !== false;
    refreshNumberplateFitPreview();
  });
  [
    "liveryPreviewNumberplateFitBounds",
    "liveryPreviewNumberplateFitWidth",
    "liveryPreviewNumberplateFitLength",
    "liveryPreviewNumberplateFitHeight",
    "liveryPreviewNumberplateFitPlateScale",
    "liveryPreviewNumberplateFitFlipLength",
    "liveryPreviewNumberplateFitX",
    "liveryPreviewNumberplateFitY",
    "liveryPreviewNumberplateFitZ",
    "liveryPreviewNumberplateFitHoodY",
    "liveryPreviewNumberplateFitHoodZ",
    "liveryPreviewNumberplateFitHoodScale",
    "liveryPreviewNumberplateFitHoodPitch",
    "liveryPreviewNumberplateFitHoodYaw",
    "liveryPreviewNumberplateFitHoodRoll",
    "liveryPreviewNumberplateFitDoorPitch",
    "liveryPreviewNumberplateFitDoorYaw",
    "liveryPreviewNumberplateFitDoorRoll",
    "liveryPreviewNumberplateFitDoorMode",
  ].forEach((id) => {
    $(id)?.addEventListener("input", () => scheduleNumberplateFitPreviewRefresh());
    $(id)?.addEventListener("change", () => refreshNumberplateFitPreview());
  });
  $("liveryPreviewSaveNumberplatePlacement")?.addEventListener("click", () => saveSelectedNumberplatePlacement(false));
  $("liveryPreviewUndoNumberplate")?.addEventListener("click", () => undoSelectedNumberplateMove());
  $("liveryPreviewDeselectNumberplate")?.addEventListener("click", () => deselectNumberplateEditor({ fromUser: true }));
  document.querySelectorAll("[data-numberplate-tool]").forEach((button) => {
    button.addEventListener("click", () => setNumberplateToolMode(button.dataset.numberplateTool || "translate"));
  });
  updateNumberplateToolFields();
  ($("liveryPreviewExportPlacement") || $("liveryPreviewExportDoorPlacement"))?.addEventListener("click", () => exportSelectedNumberplatePlacementToActor());
  $("liveryPreviewActorNudgeMinus")?.addEventListener("click", () => nudgeSelectedNumberplateActor(-1));
  $("liveryPreviewActorNudgePlus")?.addEventListener("click", () => nudgeSelectedNumberplateActor(1));
  $("liveryPreviewActorRestoreBase")?.addEventListener("click", () => restoreSelectedNumberplateActorBase());
  $("liveryPreviewActorResetAllBase")?.addEventListener("click", () => resetAllNumberplateActorBase());
  $("liveryPreviewCopyNumberplateValues")?.addEventListener("click", () => copyNumberplateTunerValues());
  $("liveryPreviewResetNumberplate")?.addEventListener("click", () => {
    pushNumberplateUndoSnapshot();
    loadSelectedNumberplateTuner(true, true);
  });
  $("liveryPreviewApplyNumberplate")?.addEventListener("click", () => {
    storeCurrentNumberplateTunerState();
    applyNumberplateOverlay(true);
  });
  $("liveryPreviewNumberplatePlacement")?.addEventListener("change", (event) => {
    selectNumberplateByIndex(Number(event.target?.value || 0), { openPanel: true, showOverlay: true });
  });
  [
    "liveryPreviewPlateX",
    "liveryPreviewPlateY",
    "liveryPreviewPlateZ",
    "liveryPreviewPlateRotX",
    "liveryPreviewPlateRotY",
    "liveryPreviewPlateRotZ",
    "liveryPreviewPlateScale",
    "liveryPreviewPlateOpacity",
  ].forEach((id) => {
    $(id)?.addEventListener("input", () => {
      state.liveryPreviewNumberplateActiveInputId = id;
      if (!numberplateEditInputsReady()) return;
      if (state.liveryPreviewNumberplateInputUndoArmed) {
        pushNumberplateUndoSnapshot();
        state.liveryPreviewNumberplateInputUndoArmed = false;
      }
      storeCurrentNumberplateTunerState();
      state.liveryPreviewNumberplateUnsavedChanges = true;
      updateNumberplateGizmoUnsavedIndicator({ dirty: true });
      if (mappedNumberplatesAreEditable()) {
        selectedNumberplateBuiltinCalibration().then((calibration) => {
          window.LiveryLabPreview3d?.updateSelectedNumberplateTransform?.(previewTransformForNumberplate(
            selectedNumberplatePlacement().placement,
            currentNumberplateTransform(),
            calibration || currentNumberplateTransform(),
          ));
        });
      } else {
        applyNumberplateOverlay(false);
      }
    });
    $(id)?.addEventListener("focus", () => {
      state.liveryPreviewNumberplateActiveInputId = id;
      state.liveryPreviewNumberplateInputUndoArmed = true;
    });
    $(id)?.addEventListener("blur", () => {
      commitNumberplateEditInput(id);
      if (state.liveryPreviewNumberplateActiveInputId === id) {
        state.liveryPreviewNumberplateActiveInputId = "";
      }
      if (!numberplateEditInputsReady()) return;
      storeCurrentNumberplateTunerState();
      if (mappedNumberplatesAreEditable()) {
        selectedNumberplateBuiltinCalibration().then((calibration) => {
          window.LiveryLabPreview3d?.updateSelectedNumberplateTransform?.(previewTransformForNumberplate(
            selectedNumberplatePlacement().placement,
            currentNumberplateTransform(),
            calibration || currentNumberplateTransform(),
          ));
        });
      } else {
        applyNumberplateOverlay(false);
      }
    });
  });
  window.addEventListener("liverylab-numberplate-tuner-change", (event) => {
    syncNumberplateInputsFromGizmo(event.detail || {});
  });
  window.addEventListener("liverylab-numberplate-transform-start", (event) => {
    handleNumberplateTransformStart(event.detail || {});
  });
  window.addEventListener("liverylab-numberplate-selected", (event) => {
    selectNumberplateFromViewer(event.detail || {});
  });
  window.addEventListener("liverylab-numberplate-deselected", () => {
    deselectNumberplateEditor({ fromUser: true });
  });
  window.addEventListener("liverylab-numberplate-templates-updated", () => {
    invalidateNumberplateStudioTemplateCache();
  });
  $("liveryPreviewShowExterior")?.addEventListener("change", (event) => {
    window.LiveryLabPreview3d?.setModelVisible?.(event.target.checked);
  });
  $("liveryPreviewShowRims")?.addEventListener("change", (event) => {
    state.liveryPreviewRimsVisible = Boolean(event.target?.checked);
    const result = window.LiveryLabPreview3d?.setAuxiliaryVisible?.(state.liveryPreviewRimsVisible);
    $("liveryPreviewStatus").textContent = `Rims/tyres ${state.liveryPreviewRimsVisible ? "shown" : "hidden"}.\nAux models: ${result?.count ?? "-"}`;
  });
  $("liveryPreviewShowFloor")?.addEventListener("change", (event) => {
    const visible = Boolean(event.target?.checked);
    window.LiveryLabPreview3d?.setFloorVisible?.(visible);
    $("liveryPreviewStatus").textContent = `Floor ${visible ? "shown" : "hidden"}.`;
  });
  $("liveryPreviewWheelDiagnostics")?.addEventListener("click", () => {
    setLiveryPreviewWheelPanelVisible($("liveryPreviewWheelPanel")?.hidden !== false);
  });
  $("liveryPreviewWheelClose")?.addEventListener("click", () => setLiveryPreviewWheelPanelVisible(false));
  $("liveryPreviewWheelCopyDiagnostics")?.addEventListener("click", () => copyLiveryPreviewWheelDiagnostics());
  $("liveryPreviewWheelTune")?.addEventListener("click", () => setLiveryPreviewWheelTuner(!state.liveryPreviewWheelTunerEnabled));
  $("liveryPreviewWheelTuneTarget")?.addEventListener("change", () => updateLiveryPreviewWheelTunerTarget());
  $("liveryPreviewWheelSaveCalibration")?.addEventListener("click", () => saveLiveryPreviewWheelCalibration());
  window.addEventListener("liverylab-wheel-tuner-change", (event) => {
    updateLiveryPreviewWheelUnsavedIndicator(event.detail || {});
    renderLiveryPreviewWheelDiagnostics();
  });
  $("liveryPreviewUseWheelPlacementOverrides")?.addEventListener("change", async (event) => {
    state.liveryPreviewWheelPlacementOverrides = Boolean(event.target?.checked);
    $("liveryPreviewStatus").textContent = state.liveryPreviewWheelPlacementOverrides
      ? "Reloading wheel placement with per-car overrides..."
      : "Reloading wheel placement without per-car overrides...";
    await loadPreview3dViewer({
      canvasId: "liveryPreviewCanvas",
      emptyId: "liveryPreviewEmpty",
      statusId: "liveryPreviewStatus",
      cameraPreset: state.liveryPreviewCameraPreset || "free",
      forceWheelBuild: true,
    });
  });
  document.querySelectorAll("[data-camera-preset]").forEach((button) => {
    button.addEventListener("click", () => setLiveryPreviewCameraPreset(button.dataset.cameraPreset || "free"));
  });
  document.querySelectorAll("[data-livery-preview-scene]").forEach((button) => {
    button.addEventListener("click", () => setLiveryPreviewScene(button.dataset.liveryPreviewScene || "light"));
  });
  ["preview3dModelPath", "preview3dTexturePath", "preview3dAoTexturePath", "preview3dMaterialName", "preview3dWatchInterval"].forEach((id) => {
    $(id)?.addEventListener("input", () => {
      renderWorkflowCompatibility();
      schedulePreview3dWatcher();
    });
  });
  $("preview3dAutoRefresh")?.addEventListener("change", () => schedulePreview3dWatcher());
  $("useSelectedForRimSource").addEventListener("click", () => applySelectionForRim(false, "rimSourcePath"));
  $("inspectRimSource").addEventListener("click", () => inspectRimMesh("source"));
  $("inspectRimTarget")?.addEventListener("click", () => inspectRimMesh("target"));
  $("createRimSwap")?.addEventListener("click", async () => {
    const source = $("rimSourcePath").value.trim();
    const target = $("rimTargetPath").value.trim();
    const out = $("rimOutPath").value.trim();
    if (!source || !target || !out) {
      log("Please set source rimmesh, target rimmesh and output rimmesh first", true);
      return;
    }
    const result = await call("create_rimmesh_swap", source, target, out, $("rootPath").value.trim());
    if (!result.ok) return;
    renderRimSummary("rimSourceSummary", result.data?.source);
    renderRimSummary("rimTargetSummary", result.data?.target);
    renderRimCompatibility(result.data?.source, result.data?.target);
    setInspectOutputText(result.message || "");
  });
  $("patchRimCompat")?.addEventListener("click", async () => {
    const compat = $("rimCompatPath").value.trim();
    const target = $("rimTargetPath").value.trim();
    const test = $("rimOutPath").value.trim();
    if (!compat || !target || !test) {
      log("Please set compatiblerim, target rimmesh and output/test rimmesh first", true);
      return;
    }
    const result = await confirmGameWriteAction({
      type: "warning",
      title: "Patch compatiblerim for test",
      message: "Patch this compatiblerim to point to the test rimmesh?",
      writes: [
        "Overwrite the selected .compatiblerim reference.",
      ],
      backupNote: "The current compatiblerim is backed up before writing.",
      extraDetails: `Target:\n${compat}\n\nOld rimmesh:\n${target}\n\nTest rimmesh:\n${test}`,
      confirmLabel: "Patch",
      loadingLabel: "Patching...",
      onConfirm: () => call("patch_compatiblerim_for_test", compat, target, test, $("rootPath").value.trim()),
    });
    if (!result) return;
    if (!result.ok) return;
    if (result.data?.backup) setPath("rimCompatBackupPath", result.data.backup);
    setInspectOutputText(result.message || "");
  });
  $("createAdditionalRimOption").addEventListener("click", async () => {
    const payload = {
      sourceRimmesh: $("rimSourcePath").value.trim(),
      targetCompatiblerim: $("rimBaseCompatPath").value.trim(),
      actor: $("rimActorPath").value.trim(),
      cardesigns: rimCardesignPathValues(),
      slotId: $("rimOptionId").value.trim(),
      displayName: $("rimDisplayName").value.trim(),
      root: $("rootPath").value.trim(),
    };
    if (!payload.sourceRimmesh || !payload.targetCompatiblerim || !payload.actor || !payload.cardesigns.length || !payload.slotId) {
      log("Please set source rimmesh, target compatiblerim, actor, cardesign files and option id first", true);
      return;
    }
    const result = await confirmGameWriteAction({
      type: "warning",
      title: "Create rim option",
      message: "Create and register an additional rim option?",
      writes: [
        "Create a custom .compatiblerim file.",
        "Create one or more custom .rimmesh files.",
        "Patch the selected actor and cardesign files to register the rim option.",
        "Write a LiveryLab marker file for later removal.",
      ],
      backupNote: "Actor, cardesign, compatiblerim and rimmesh targets are backed up before overwrite when they change.",
      extraDetails: `Option id:\n${payload.slotId}`,
      confirmLabel: "Create option",
      loadingLabel: "Creating option...",
      onConfirm: () => call("create_additional_rim_option", payload),
    });
    if (!result) return;
    if (!result.ok) return;
    if (result.data?.new_rimmesh) setPath("rimOutPath", result.data.new_rimmesh);
    if (result.data?.new_compatiblerim) setPath("rimCompatPath", result.data.new_compatiblerim);
    if (result.data?.marker) setPath("rimRemoveMarkerPath", result.data.marker);
    renderRimSummary("rimSourceSummary", result.data?.rimmesh?.source);
    renderRimSummary("rimTargetSummary", result.data?.rimmesh?.target);
    renderRimCompatibility(result.data?.rimmesh?.source, result.data?.rimmesh?.target);
    setInspectOutputText(result.message || "");
  });
  $("removeCustomRimOption").addEventListener("click", async () => {
    const payload = {
      marker: $("rimRemoveMarkerPath").value.trim(),
      actor: $("rimActorPath").value.trim(),
      cardesigns: rimCardesignPathValues(),
      root: $("rootPath").value.trim(),
    };
    if (!payload.marker || !payload.actor || !payload.cardesigns.length) {
      log("Please set marker, actor and cardesign files first", true);
      return;
    }
    const result = await confirmGameWriteAction({
      type: "danger",
      title: "Remove custom rim option",
      message: "Remove this LiveryLab custom rim option?",
      writes: [
        "Patch the selected actor and cardesign files to unregister the rim option.",
        "Move the custom compatiblerim, rimmesh and marker files into LiveryLab quarantine.",
      ],
      backupNote: "Actor and cardesign files are backed up before overwrite when they change.",
      extraDetails: `Marker:\n${payload.marker}`,
      confirmLabel: "Remove",
      loadingLabel: "Removing...",
      onConfirm: () => call("remove_custom_rim_option", payload),
    });
    if (!result) return;
    if (!result.ok) return;
    setInspectOutputText(result.message || "");
  });
  $("restoreRimCompat")?.addEventListener("click", async () => {
    const compat = $("rimCompatPath").value.trim();
    const backup = $("rimCompatBackupPath").value.trim();
    if (!compat || !backup) {
      log("Please set compatiblerim and backup path first", true);
      return;
    }
    const result = await confirmGameWriteAction({
      type: "warning",
      title: "Restore compatiblerim backup",
      message: "Restore this compatiblerim backup?",
      writes: [
        "Overwrite the selected .compatiblerim with the chosen backup.",
      ],
      backupNote: "The current compatiblerim is backed up before the restore copy is written.",
      extraDetails: `Target:\n${compat}\n\nBackup:\n${backup}`,
      confirmLabel: "Restore",
      loadingLabel: "Restoring...",
      onConfirm: () => call("restore_compatiblerim_backup", compat, backup, $("rootPath").value.trim()),
    });
    if (!result) return;
    if (!result.ok) return;
    setInspectOutputText(result.message || "");
  });
  $("useSelectedForUv").addEventListener("click", () => applySelectionForUv());
  $("uvMeshPath").addEventListener("input", () => renderWorkflowCompatibility());
  $("analyzeUvMesh").addEventListener("click", () => analyzeUvMesh());
  $("loadUvTexture").addEventListener("click", () => loadUvTexture());
  $("useUvSlot").addEventListener("click", () => useUvSlot());
  $("loadUvPreset").addEventListener("click", () => loadSelectedUvPreset());
  $("saveUvPreset").addEventListener("click", () => saveNamedUvPreset());
  $("deleteUvPreset").addEventListener("click", () => deleteSelectedUvPreset());
  $("uvGroupsSkin").addEventListener("click", () => selectUvSkinGroups());
  $("uvGroupsGlass")?.addEventListener("click", () => selectUvGlassGroups());
  $("uvGroupsAll").addEventListener("click", () => selectAllUvGroups());
  $("uvGroupsNone").addEventListener("click", () => clearUvGroups());
  $("uvGroupsLargest").addEventListener("click", () => selectLargestUvGroup());
  document.querySelectorAll("[data-uv-material-filter]").forEach((button) => {
    button.addEventListener("click", () => setUvMaterialFilter(button.dataset.uvMaterialFilter || "all"));
  });
  $("uvMajorOnly").addEventListener("change", () => {
    state.uvMajorOnly = !!$("uvMajorOnly").checked;
    if ($("uvGroupMinTriangles")) state.uvGroupMinTriangles = uvGroupFilterThreshold();
    const visible = new Set(visibleUvGroups().map((group) => group.id));
    state.uvSelectedGroups = new Set([...state.uvSelectedGroups].filter((groupId) => !state.uvGroups.length || visible.has(groupId)));
    if (!state.uvSelectedGroups.size) state.uvSelectedGroups = visible;
    renderUvSummary(state.uvMeshData);
    renderUvGroupList();
    renderUvCanvas();
    scheduleSaveUvState();
  });
  $("uvGroupMinTriangles").addEventListener("change", () => {
    state.uvGroupMinTriangles = uvGroupFilterThreshold();
    const visible = new Set(visibleUvGroups().map((group) => group.id));
    state.uvSelectedGroups = new Set([...state.uvSelectedGroups].filter((groupId) => visible.has(groupId)));
    if (!state.uvSelectedGroups.size) state.uvSelectedGroups = visible;
    renderUvSummary(state.uvMeshData);
    renderUvGroupList();
    renderUvCanvas();
    scheduleSaveUvState();
  });
  $("uvShowTexture").addEventListener("change", () => {
    renderUvCanvas();
    scheduleSaveUvState();
  });
  $("uvOutlineOnly").addEventListener("change", () => {
    renderUvCanvas();
    scheduleSaveUvState();
  });
  $("uvFlipU").addEventListener("change", () => {
    renderUvCanvas();
    scheduleSaveUvState();
  });
  $("uvFlipV").addEventListener("change", () => {
    renderUvCanvas();
    scheduleSaveUvState();
  });
  $("uvExportOverlay").addEventListener("change", () => {
    scheduleSaveUvState();
  });
  $("exportUvTemplate").addEventListener("click", () => exportUvTemplate());
  $("uvCanvas").addEventListener("mousemove", (event) => {
    setUvHoveredGroup(uvGroupIdAtPointer(event));
  });
  $("uvCanvas").addEventListener("mouseleave", () => setUvHoveredGroup(null));
  $("uvCanvas").addEventListener("click", (event) => {
    const groupId = uvGroupIdAtPointer(event);
    if (groupId) setUvFocusedGroup(groupId);
  });

  $("inspectMaterial").addEventListener("click", async () => {
    const materialPath = $("materialPath").value.trim();
    await setMaterialOutputDefaults(materialPath, false);
    const result = await call("inspect_material", materialPath, $("rootPath").value.trim());
    if (result.ok) renderMaterial(result);
  });
  $("saveMaterialReports").addEventListener("click", async () => {
    await setMaterialOutputDefaults($("materialPath").value.trim(), false);
    const out = $("materialReportDir").value.trim();
    const result = await confirmFreeOutputIfInGameRoot({
      target: out,
      title: "Write material reports inside AC EVO installation folder",
      message: "The selected material report target is inside the AC EVO installation folder. Continue?",
      writes: ["Write JSON, CSV and Markdown material report files."],
      confirmLabel: "Write reports",
      loadingLabel: "Writing reports...",
      onConfirm: () => call("write_material_reports", out, $("rootPath").value.trim()),
    });
    if (!result) return;
    if (result.ok) setInspectOutputText(result.message || "");
  });
  $("exportMaterialTextures").addEventListener("click", async () => {
    await setMaterialOutputDefaults($("materialPath").value.trim(), false);
    const out = $("materialExportDir").value.trim();
    const result = await confirmFreeOutputIfInGameRoot({
      target: out,
      title: "Write material textures inside AC EVO installation folder",
      message: "The selected material texture export target is inside the AC EVO installation folder. Continue?",
      writes: ["Write DDS files for material texture slots."],
      confirmLabel: "Export",
      loadingLabel: "Exporting...",
      onConfirm: () => call("export_material_textures", out, $("rootPath").value.trim()),
    });
    if (!result) return;
    if (result.ok) setInspectOutputText(result.message || "");
  });

  $("analyzeSkin")?.addEventListener("click", async () => {
    await autofillSkinTemplateFields(true);
  });
  $("toggleSkinTemplateAdvanced").addEventListener("click", () => {
    const panel = $("skinTemplateAdvanced");
    setSkinTemplateAdvancedVisible(panel?.hidden);
  });
  $("skinCardesign").addEventListener("input", () => {
    state.skinTemplateAutofillRequestId += 1;
    clearSkinTemplateDependentFields();
  });
  $("skinCardesign").addEventListener("change", () => autofillSkinTemplateFields(false));
  $("skinPaintVariant")?.addEventListener("change", (event) => {
    const index = Number(event.target?.value || 0);
    applySkinPaintVariantOption(state.skinPaintVariantOptions[index] || null);
  });
  $("skinDesign").addEventListener("input", () => renderWorkflowCompatibility());
  document.querySelectorAll('input[name="skinProjectType"]').forEach((input) => {
    input.addEventListener("change", () => updateSkinProjectTypeMode());
  });
  $("skinName")?.addEventListener("input", () => updateSkinIdPreview());
  $("skinId")?.addEventListener("input", () => updateSkinIdPreview());
  $("refreshAdvancedCreatorCars")?.addEventListener("click", () => refreshAdvancedCreatorCars({ force: true }));
  $("advancedCreatorCarSearch")?.addEventListener("input", (event) => {
    const cursor = Number(event.target.selectionStart ?? event.target.value.length);
    state.advancedCreatorSearch = event.target.value || "";
    renderAdvancedCreatorCars(state.carCompatibility);
    window.requestAnimationFrame(() => {
      const search = $("advancedCreatorCarSearch");
      if (!search) return;
      search.focus();
      search.setSelectionRange(cursor, cursor);
    });
  });
  document.querySelectorAll("[data-advanced-creator-sort]").forEach((button) => {
    button.addEventListener("click", () => {
      const mode = String(button.dataset.advancedCreatorSort || "alphabetical");
      state.advancedCreatorSortMode = ["carType", "carBrand"].includes(mode) ? mode : "alphabetical";
      renderAdvancedCreatorCars(state.carCompatibility);
    });
  });
  $("advancedCreatorTemplateOptions")?.addEventListener("change", async (event) => {
    const input = event.target?.closest?.('input[name="advancedCreatorTemplate"]');
    if (!input) return;
    state.advancedCreatorSelectedTemplateId = String(input.value || "").trim();
    const selected = selectedAdvancedCreatorTemplate();
    renderAdvancedCreatorTemplateOptions(selectedAdvancedCreatorCar());
    syncAdvancedCreatorTemplateFields(selected);
    updateCreatorTemplateLibrarySelection("advanced", selectedAdvancedCreatorCar());
    if ($("skinCardesign")?.value.trim()) await autofillSkinTemplateFields(false);
  });
  $("advancedCreatorTemplateLibraryOptions")?.addEventListener("change", (event) => {
    const input = event.target?.closest?.('input[name="advancedCreatorTemplateLibrary"]');
    if (!input) return;
    state.liveryTemplateSelectedIds.advanced = String(input.value || "blank").trim() || "blank";
    renderCreatorTemplateLibrary("advanced", state.liveryTemplateCatalogs[state.liveryTemplateCatalogSelectedKeys.advanced] || null, selectedAdvancedCreatorCar());
  });
  $("advancedCreatorCarGrid")?.addEventListener("click", (event) => {
    const favorite = event.target?.closest?.("[data-advanced-creator-favorite-car-id]");
    if (favorite) {
      event.preventDefault();
      event.stopPropagation();
      const carId = favorite.dataset.advancedCreatorFavoriteCarId || "";
      const car = advancedCreatorCars().find((row) => String(row.carId || "") === carId) || { carId };
      setAdvancedCreatorCarFavorite(carId, !advancedCreatorCarIsFavorite(car));
      return;
    }
    const card = event.target?.closest?.("[data-advanced-creator-car-id]");
    if (!card || card.classList.contains("is-disabled")) return;
    selectAdvancedCreatorCar(card.dataset.advancedCreatorCarId || "");
  });
  $("advancedCreatorCarGrid")?.addEventListener("keydown", (event) => {
    if (event.key !== "Enter" && event.key !== " ") return;
    const favorite = event.target?.closest?.("[data-advanced-creator-favorite-car-id]");
    if (favorite) {
      event.preventDefault();
      event.stopPropagation();
      const carId = favorite.dataset.advancedCreatorFavoriteCarId || "";
      const car = advancedCreatorCars().find((row) => String(row.carId || "") === carId) || { carId };
      setAdvancedCreatorCarFavorite(carId, !advancedCreatorCarIsFavorite(car));
      return;
    }
    const card = event.target?.closest?.("[data-advanced-creator-car-id]");
    if (!card || card.classList.contains("is-disabled")) return;
    event.preventDefault();
    selectAdvancedCreatorCar(card.dataset.advancedCreatorCarId || "");
  });
  $("refreshOfficialCreatorCars")?.addEventListener("click", () => refreshOfficialCreatorCars({ force: true }));
  $("officialCreatorCarSearch")?.addEventListener("input", (event) => {
    const cursor = Number(event.target.selectionStart ?? event.target.value.length);
    state.officialCreatorSearch = event.target.value || "";
    renderOfficialCreatorCars(state.officialCarResourcesStatus);
    window.requestAnimationFrame(() => {
      const search = $("officialCreatorCarSearch");
      if (!search) return;
      search.focus();
      search.setSelectionRange(cursor, cursor);
    });
  });
  document.querySelectorAll("[data-official-creator-sort]").forEach((button) => {
    button.addEventListener("click", () => {
      const mode = String(button.dataset.officialCreatorSort || "alphabetical");
      state.officialCreatorSortMode = ["carType", "carBrand"].includes(mode) ? mode : "alphabetical";
      renderOfficialCreatorCars(state.officialCarResourcesStatus);
    });
  });
  $("officialVehicleVariantOptions")?.addEventListener("change", (event) => {
    const input = event.target?.closest?.('input[name="officialVehicleVariant"]');
    if (!input) return;
    state.officialCreatorSelectedVariantId = String(input.value || "").trim();
    const car = selectedOfficialCreatorCar();
    renderOfficialVehicleVariantOptions(car);
  });
  $("officialCreatorCarGrid")?.addEventListener("click", (event) => {
    const favorite = event.target?.closest?.("[data-official-creator-favorite-car-id]");
    if (favorite) {
      event.preventDefault();
      event.stopPropagation();
      const carId = favorite.dataset.officialCreatorFavoriteCarId || "";
      const car = officialCreatorCars().find((row) => String(row.carId || "") === carId) || { carId };
      setOfficialCreatorCarFavorite(carId, !officialCreatorCarIsFavorite(car));
      return;
    }
    const card = event.target?.closest?.("[data-official-creator-car-id]");
    if (!card || card.classList.contains("is-disabled")) return;
    selectOfficialCreatorCar(card.dataset.officialCreatorCarId || "");
  });
  $("officialCreatorCarGrid")?.addEventListener("keydown", (event) => {
    if (event.key !== "Enter" && event.key !== " ") return;
    const favorite = event.target?.closest?.("[data-official-creator-favorite-car-id]");
    if (favorite) {
      event.preventDefault();
      event.stopPropagation();
      const carId = favorite.dataset.officialCreatorFavoriteCarId || "";
      const car = officialCreatorCars().find((row) => String(row.carId || "") === carId) || { carId };
      setOfficialCreatorCarFavorite(carId, !officialCreatorCarIsFavorite(car));
      return;
    }
    const card = event.target?.closest?.("[data-official-creator-car-id]");
    if (!card || card.classList.contains("is-disabled")) return;
    event.preventDefault();
    selectOfficialCreatorCar(card.dataset.officialCreatorCarId || "");
  });
  updateSkinIdPreview();
  updateSkinProjectTypeMode();
  $("skinNumberplateTemplate")?.addEventListener("focus", () => loadNumberplateTemplateOptions());
  $("skinNumberplateTemplate")?.addEventListener("pointerdown", () => loadNumberplateTemplateOptions());
  $("skinNumberplateTemplate")?.addEventListener("change", (event) => {
    applyNumberplateTemplateToSkinCreatorInputs(event.target?.value || "");
  });
  $("skinNumberplateFont").addEventListener("focus", () => loadNumberplateTemplateOptions());
  $("skinNumberplateBackground").addEventListener("focus", () => loadNumberplateTemplateOptions());
  $("skinNumberplateFont").addEventListener("pointerdown", () => loadNumberplateTemplateOptions());
  $("skinNumberplateBackground").addEventListener("pointerdown", () => loadNumberplateTemplateOptions());
  $("numberplateCreatorBase")?.addEventListener("focus", () => loadNumberplateTemplateOptions());
  $("numberplateCreatorBase")?.addEventListener("pointerdown", () => loadNumberplateTemplateOptions());
  $("numberplateFontCreatorBase")?.addEventListener("focus", () => loadNumberplateTemplateOptions());
  $("numberplateFontCreatorBase")?.addEventListener("pointerdown", () => loadNumberplateTemplateOptions());
  $("refreshNumberplateCreator")?.addEventListener("click", () => {
    window.LiveryLabNumberplateStudio?.refresh?.();
  });
  window.LiveryLabCustomGrids?.init?.();
  $("refreshTyreBrands")?.addEventListener("click", () => loadCustomTyreBrands());
  $("refreshDriverProfiles")?.addEventListener("click", () => loadDriverProfiles());
  $("newDriverProfile")?.addEventListener("click", () => {
    $("driverSourceSkin")?.focus();
    $("driverProfileName")?.select?.();
  });
  $("importDriverProfile")?.addEventListener("click", () => importDriverProfilePackage());
  $("driverSourceSkin")?.addEventListener("change", (event) => {
    state.selectedDriverSourceSkinKey = event.target?.value || "";
    renderDriverSourceSkins();
  });
  $("createDriverProfileFromSkin")?.addEventListener("click", () => createDriverProfileFromSelectedSkin());
  $("driverPreviewClose")?.addEventListener("click", () => closeDriverPreviewModal());
  $("driverPreviewBackdrop")?.addEventListener("click", () => closeDriverPreviewModal());
  $("driverPreviewSaveAppearance")?.addEventListener("click", () => saveDriverPreviewAppearance());
  $("newTyreBrandTemplate")?.addEventListener("click", () => newTyreBrandTemplate());
  $("deleteTyreBrandTemplate")?.addEventListener("click", () => deleteTyreBrandTemplate());
  $("importTyreBrandPackage")?.addEventListener("click", () => importTyreBrandPackage());
  $("exportTyreBrandPackage")?.addEventListener("click", () => exportSelectedTyreBrandPackage());
  $("downloadTyreBrandTemplate")?.addEventListener("click", () => downloadTyreBrandTemplate());
  $("pickTyreBrandSlickPng")?.addEventListener("click", () => pickTyreBrandPng("tyreBrandSlickPng"));
  $("pickTyreBrandBlurPng")?.addEventListener("click", () => pickTyreBrandPng("tyreBrandBlurPng"));
  $("pickTyreBrandMarkPng")?.addEventListener("click", () => pickTyreBrandPng("tyreBrandMarkPng"));
  [
    ["tyreBrandSlickPng", "PNG (*.png)"],
    ["tyreBrandBlurPng", "PNG (*.png)"],
    ["tyreBrandMarkPng", "PNG (*.png)"],
  ].forEach(([target, filter]) => setupPathInputDrop($(target), { target, filter }));
  $("saveTyreBrandTemplate")?.addEventListener("click", () => saveTyreBrandTemplate());
  ["tyreBrandName", "tyreBrandTemplate", "tyreBrandSlickPng", "tyreBrandBlurPng", "tyreBrandMarkPng"].forEach((id) => {
    $(id)?.addEventListener("input", updateTyreBrandDraftFromInputs);
    $(id)?.addEventListener("change", updateTyreBrandDraftFromInputs);
  });
  $("createTyreBrand")?.addEventListener("click", () => createRacingTyreBrand());
  $("createNumberplateBackground")?.addEventListener("click", () => createNumberplateBackground());
  $("createNumberplateFont")?.addEventListener("click", () => createNumberplateFont());
  $("createSkin").addEventListener("click", async () => {
    await withButtonWorking("createSkin", skinCreatorUsesOfficialProject() ? "Preparing project..." : "Preparing skin...", async () => {
      await createSkinWithPreviewSetup();
    });
  });
  $("refreshMySkins").addEventListener("click", () => loadMySkins());
  $("openLiveryImport")?.addEventListener("click", () => openSingleLiveryImport());
  $("startGame")?.addEventListener("click", () => startGameFromButton("startGame"));
  $("startGameCustomGrids")?.addEventListener("click", () => startGameFromButton("startGameCustomGrids"));
  $("mySkinPackageDir").addEventListener("input", (event) => {
    state.mySkinPackageDir = event.target.value;
    state.mySkinPackageInfo = null;
  });
  $("pickMySkinPackageDir").addEventListener("click", async () => {
    const result = await window.pywebview.api.choose_path("file", ["LiveryLab Package (*.zip)", "ZIP (*.zip)"], "");
    if (showResult(result, t("log_path_selected")) && result.data.path) {
      state.mySkinPackageDir = result.data.path;
      $("mySkinPackageDir").value = state.mySkinPackageDir;
      state.mySkinPackageInfo = null;
      renderMySkinPackageSummary();
    }
  });
  $("installMySkinPackage").addEventListener("click", () => installMySkinPackage());
  $("importMySkins").addEventListener("click", async () => {
    await withButtonWorking("importMySkins", "Importing...", async () => {
      const result = await call("import_existing_my_skins", $("rootPath").value.trim());
      if (!result.ok) return;
      await loadMySkins();
      setInspectOutputText(result.message || "");
    });
  });
}

async function boot() {
  window.LiveryLabStartupLog?.checkpoint?.("boot_started", "app.js boot entered");
  try {
    setSplashProgress("Waiting for app bridge...", 8);
    await apiReady();
    setSplashProgress("Preparing interface...", 18);
    wireEvents();
    enablePathInputDrops();
    applyLanguage("en");
    setSkinTemplateAdvancedVisible(false);
    setUvControlTab(state.uvControlTab);
    updateLayoutForTab(activeTab());
    applySetupGateStatus();
    setSplashProgress("Loading settings...", 46);
    const settings = await window.pywebview.api.get_settings();
    window.LiveryLabStartupLog?.checkpoint?.("settings_loaded", `ok=${Boolean(settings?.ok)}`);
    window.LiveryLabTutorialMode?.init?.(settings?.data || {});
    if (settings.ok) {
      setSplashProgress("Applying saved paths...", 74);
      applyDebugSettings(settings.data || {});
      state.savedRoot = normalizeRootPath(settings.data.defaultRoot || "");
      syncRootFields(settings.data.defaultRoot || "");
      state.texconvPath = settings.data.texconvPath || "";
      state.officialStorageRoots = settings.data.officialStorageRoots && typeof settings.data.officialStorageRoots === "object"
        ? settings.data.officialStorageRoots
        : null;
      state.officialMySkinExportDir = settings.data.officialLiveryPackageExportRoot
        || state.officialStorageRoots?.officialLiveryPackageExportRoot
        || "";
      state.appStorageRoots = [
        settings.data.programRoot,
        settings.data.liveryProjectsRoot,
        settings.data.liveryLibraryRoot,
        settings.data.officialLiveryProjectsRoot,
        settings.data.officialLiveryLibraryRoot,
        settings.data.officialLiveryPackageExportRoot,
        settings.data.officialPreviewAssetsRoot,
        settings.data.officialLocalSkinsRoot,
        settings.data.onlineLeagueProjectsRoot,
        settings.data.onlineLeagueLibraryRoot,
        settings.data.onlineLeaguePackageExportRoot,
        settings.data.localSkinsRoot,
        settings.data.gridPresetsRoot,
        settings.data.convertedRoot,
        settings.data.convertedTexturesRoot,
        settings.data.convertedMeshesRoot,
        settings.data.convertedUvRoot,
        settings.data.liveryPackageExportRoot,
        settings.data.trashRoot,
      ].filter(Boolean);
      state.mySkinExportDir = state.mySkinExportDir || settings.data.liveryPackageExportRoot || settings.data.liveryPackagesRoot || settings.data.customLiveriesRoot || "";
      updateTexconvUi();
      state.browserFavorites = settings.data.browserFavorites || [];
      state.advancedCreatorFavoriteCarIds = Array.isArray(settings.data.advancedCreatorCarFavorites) ? settings.data.advancedCreatorCarFavorites : [];
      state.officialCreatorFavoriteCarIds = Array.isArray(settings.data.officialCreatorCarFavorites) ? settings.data.officialCreatorCarFavorites : [];
      state.workflowScope = normalizeWorkflowScope(settings.data.workflowScope || state.workflowScope || "advanced");
      state.workflowScopeConfigured = Boolean(settings.data.workflowScopeConfigured);
      state.numberplateGeneratorNoticeDismissedNotice = settings.data.numberplateGeneratorNoticeDismissedNotice || "";
      state.customGridBridgeGuideDismissedNotice = settings.data.customGridBridgeGuideDismissedNotice || "";
      state.layerStudioInfoDismissedNotice = settings.data.layerStudioInfoDismissedNotice || "";
      state.liveryPreviewInfoDismissedNotice = settings.data.liveryPreviewInfoDismissedNotice || "";
      state.driverProfilesInfoDismissedNotice = settings.data.driverProfilesInfoDismissedNotice || "";
      state.liveryLabSafetyBypassed = Boolean(settings.data.liveryLabSafetyBypassed);
      renderBrowserFavorites();
      const appVersion = $("appVersion");
      if (appVersion) appVersion.textContent = settings.data.appVersion || "v0.18.0";
    }
    applySetupGateStatus();
    await refreshLogStatus();
    if (state.savedRoot) {
      window.LiveryLabEvoUpdateStartupRoot = state.savedRoot;
      if (typeof window.LiveryLabEvoUpdateWorkflow?.checkAtStartup === "function") {
        await window.LiveryLabEvoUpdateWorkflow.checkAtStartup(state.savedRoot);
      }
    }
    setSplashProgress("Choose workspace.", 100);
    log(t("log_ready"));
    window.LiveryLabStartupLog?.checkpoint?.("boot_complete", "interface ready");
  } catch (error) {
    console.error(error);
    window.LiveryLabStartupLog?.reportError?.(error, { stage: "startup", operation: "boot" });
    window.LiveryLabStartupLog?.checkpoint?.("boot_failed", error?.message || String(error));
    setSplashProgress("Startup issue - opening app...", 100);
    try {
      log(error?.message || String(error), true);
    } catch (_logError) {}
  } finally {
    hideSplashWhenReady();
  }
}

boot();
