<!DOCTYPE html>
<html lang="{{HTML_LANG}}">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <script>window.GG_I18N={{GG_I18N_JSON}};</script>
  <script>window.__GG_ICON_BASE__='{{ICON_BASE_URL}}';</script>
  <script>window.GG_TOOL_PROFILES={{GCM_TOOL_PROFILES}};</script>
  {{BOOTSTRAP_CSS}}
  <style>{{DIALOG_CSS}}</style>
</head>
<body>

<div id="app" class="app-layout">

  <!-- ══════════════════════════════════════════════════════════════
       LEFT PANEL — tabs: Toolpaths | Layers
  ══════════════════════════════════════════════════════════════════ -->
  <div class="panel-left">

    <!-- Tab nav -->
    <div class="left-tab-nav">
      <button id="ltab-toolpaths" class="left-tab active"
              onclick="App.switchLeftTab('toolpaths')">{{GCM_TAB_TOOLPATHS}}</button>
      <button id="ltab-layers" class="left-tab"
              onclick="App.switchLeftTab('layers')">{{GCM_TAB_LAYERS}}</button>
      <button id="ltab-tools" class="left-tab"
              onclick="App.switchLeftTab('tools')">{{GCM_TAB_TOOLS}}</button>
    </div>

    <!-- ── Toolpaths pane ──────────────────────────────────────── -->
    <div id="pane-toolpaths" class="left-pane">
      <div class="panel-header panel-header-tp">
        <span class="fw-semibold small">{{GCM_HEADER_TOOLPATHS}}</span>
        <div class="tp-add-icons" role="group" aria-label="{{GCM_ARIA_ADD_TP_GROUP}}">
          <button type="button" class="btn btn-outline-primary tp-icon-btn" title="{{GCM_TP_PROFILE_TITLE}}"
                  aria-label="{{GCM_ARIA_ADD_PROFILE}}" onclick="App.addToolpath('profile')">
            <img src="{{ICON_BASE_URL}}GCM_TP_PROFILE.svg" width="18" height="18" alt="">
          </button>
          <button type="button" class="btn btn-outline-success tp-icon-btn" title="{{GCM_TP_POCKET_TITLE}}"
                  aria-label="{{GCM_ARIA_ADD_POCKET}}" onclick="App.addToolpath('pocket')">
            <img src="{{ICON_BASE_URL}}GCM_TP_POCKET.svg" width="18" height="18" alt="">
          </button>
          <button type="button" class="btn btn-outline-warning tp-icon-btn" title="{{GCM_TP_DRILL_TITLE}}"
                  aria-label="{{GCM_ARIA_ADD_DRILL}}" onclick="App.addToolpath('drill')">
            <img src="{{ICON_BASE_URL}}GCM_TP_DRILL.svg" width="18" height="18" alt="">
          </button>
          <button type="button" class="btn btn-outline-info tp-icon-btn" title="{{GCM_TP_FLUTING_TITLE}}"
                  aria-label="{{GCM_ARIA_ADD_FLUTING}}" onclick="App.addToolpath('fluting')">
            <img src="{{ICON_BASE_URL}}GCM_TP_FLUTING.svg" width="18" height="18" alt="">
          </button>
        </div>
      </div>
      <div id="tp-list" class="tp-list"></div>
      <div class="tp-list-footer">
        <div class="d-flex gap-1 align-items-center flex-wrap tp-footer-main min-w-0">
          <div class="d-flex gap-1 align-items-center flex-wrap tp-footer-icon-actions">
            <button type="button" class="btn btn-xs btn-outline-secondary tp-icon-btn tp-aspire-import-btn"
                    title="{{GCM_IMPORT_ASPIRE_TP_TITLE}}"
                    aria-label="{{GCM_ARIA_IMPORT_ASPIRE_TP}}" onclick="App.importAspireToolpaths()">
              <img src="{{ICON_BASE_URL}}GCM_TP_IMPORT.svg" width="18" height="18" alt="">
              {{GCM_ASPIRE_BTN_LABEL}}
            </button>
            <button type="button" class="btn btn-xs btn-outline-secondary tp-icon-btn" title="{{GCM_IMPORT_TP_TITLE}}"
                    aria-label="{{GCM_ARIA_IMPORT_TP}}" onclick="App.importToolpaths()">
              <img src="{{ICON_BASE_URL}}GCM_TP_IMPORT.svg" width="18" height="18" alt="">
            </button>
            <button type="button" class="btn btn-xs btn-outline-secondary tp-icon-btn" title="{{GCM_EXPORT_TP_TITLE}}"
                    aria-label="{{GCM_ARIA_EXPORT_TP}}" onclick="App.exportToolpaths()">
              <img src="{{ICON_BASE_URL}}GCM_TP_EXPORT.svg" width="18" height="18" alt="">
            </button>
            <button type="button" class="btn btn-xs btn-outline-info tp-icon-btn" title="{{GCM_SAMPLE_TP_TITLE}}"
                    style="{{SAMPLE_TP_DISPLAY}}" aria-label="{{GCM_ARIA_SAMPLE_TP}}" onclick="App.addDefaultToolpaths()">
              <img src="{{ICON_BASE_URL}}GCM_TP_SAMPLE.svg" width="18" height="18" alt="">
            </button>
          </div>
          <div class="d-flex gap-1 align-items-center tp-footer-save-clear flex-shrink-0">
            <button type="button" class="btn btn-xs btn-outline-danger tp-icon-btn" title="{{GCM_CLEAR_TP_TITLE}}"
                    aria-label="{{GCM_ARIA_CLEAR_TP}}" onclick="App.confirmClearAllToolpaths()">
              <img src="{{ICON_BASE_URL}}DELETE.svg" width="18" height="18" alt="">
            </button>
            <button type="button" class="btn btn-xs btn-primary" id="btn-tp-save-list" disabled
                    title="{{GCM_TP_SAVE_TITLE}}" aria-label="{{GCM_ARIA_SAVE_TP}}"
                    onclick="event.stopPropagation(); App.saveToolpathListConfiguration()">{{GCM_SAVE_BTN}}</button>
          </div>
        </div>
      </div>
    </div>

    <!-- ── Layers pane ─────────────────────────────────────────── -->
    <div id="pane-layers" class="left-pane d-none">
      <div class="panel-header">
        <span class="fw-semibold small">{{GCM_HEADER_LAYERS_PANE}}</span>
        <button class="btn btn-xs btn-outline-secondary"
                onclick="App.resetLayerColors()">{{GCM_RESET_LAYERS}}</button>
      </div>
      <div id="layer-list" class="layer-list"></div>
    </div>

    <!-- ── Tools pane ─────────────────────────────────────────── -->
    <div id="pane-tools" class="left-pane d-none">
      <div class="panel-header">
        <span class="fw-semibold small">{{GCM_HEADER_TOOL_LIBRARY}}</span>
        <div class="d-flex gap-1 align-items-center">
          <button type="button" class="btn btn-outline-primary tp-icon-btn" id="btn-tool-save-list" disabled
                  title="{{GCM_TOOL_SAVE_TITLE}}" aria-label="{{GCM_ARIA_SAVE_TOOL}}"
                  onclick="event.stopPropagation(); App.saveToolsConfiguration()">
            <img src="{{ICON_BASE_URL}}GCM_TP_SAVE.svg" width="18" height="18" alt="">
          </button>
          <button class="btn btn-xs btn-outline-primary" onclick="App.addTool()">{{GCM_ADD_TOOL_BTN}}</button>
        </div>
      </div>
      <div id="tool-list" class="tool-list"></div>
    </div>

    <!-- Footer — always visible -->
    <div class="panel-footer d-flex flex-column gap-1">
      <div class="output-folder-block">
        <div class="d-flex align-items-baseline flex-wrap gap-2 min-w-0 mb-1">
          <label class="label-xs label-inline mb-0 flex-shrink-0">{{GCM_OUTPUT_FOLDER_LABEL}}</label>
          <span id="post-name-inline" class="post-name-inline label-xs text-muted d-none" title=""></span>
        </div>
        <div class="input-group input-group-sm">
          <input id="output-folder" type="text" class="form-control form-control-xs"
                 placeholder="{{GCM_OUTPUT_FOLDER_PH}}" readonly>
          <button class="btn btn-outline-secondary btn-xs" onclick="App.browseFolder()">···</button>
        </div>
      </div>
      <div id="status-bar" class="status-bar d-none"></div>
      <button type="button" class="btn btn-primary btn-sm w-100" onclick="App.generate()">
        {{GCM_GENERATE_BTN}}
      </button>
    </div>
  </div>

  <!-- ══════════════════════════════════════════════════════════════
       RIGHT PANEL — tabs: All Sheets | Selected Sheet
  ══════════════════════════════════════════════════════════════════ -->
  <div class="panel-right">

    <!-- Preview toolbar -->
    <div class="preview-toolbar">
      <div class="right-tab-nav">
        <button id="rtab-all"      class="right-tab active"
                onclick="App.switchRightTab('all')">{{GCM_RIGHT_TAB_ALL}}</button>
        <button id="rtab-selected" class="right-tab"
                onclick="App.switchRightTab('selected')">{{GCM_RIGHT_TAB_SELECTED}}</button>
      </div>
      <span id="sheet-dims" class="text-muted label-xs"></span>
      <div class="d-flex gap-1 ms-auto flex-wrap align-items-center">
        <button type="button" class="btn btn-xs btn-outline-secondary"
                onclick="App.openMaterialSetup()">{{GCM_MATERIAL_SETUP_BTN}}</button>
        <button type="button" class="btn btn-xs btn-outline-secondary"
                onclick="App.openPostModal()">{{GCM_IMPORT_POST_BTN}}</button>
        <button id="btn-load-nesting" class="btn btn-xs btn-success"
                onclick="App.refreshNesting()" title="{{GCM_LOAD_NESTING_TITLE}}">
          {{GCM_LOAD_NESTING_BTN}}
        </button>
        <button id="btn-preview2d" class="btn btn-xs btn-outline-info"
                onclick="Preview2dView.open()" title="{{GCM_PREVIEW2D_TITLE}}">
          {{GCM_PREVIEW2D_BTN}}
        </button>
        <button id="btn-preview3d" class="btn btn-xs btn-outline-info"
                onclick="Preview3dView.open()" title="{{GCM_PREVIEW3D_TITLE}}">
          {{GCM_PREVIEW3D_BTN}}
        </button>
      </div>
    </div>

    <!-- All Sheets pane -->
    <div id="pane-all-sheets" class="preview-wrap right-pane">
      <div id="preview-hint" class="preview-hint">
        {{GCM_PREVIEW_HINT_NONE}}
      </div>
    </div>

    <!-- Selected Sheet pane -->
    <div id="pane-selected-sheet" class="preview-wrap right-pane d-none" style="justify-content:center">
      <div id="selected-hint" class="preview-hint">
        {{GCM_SELECTED_HINT_NONE}}
      </div>
    </div>

    <!-- Toolpath editor — popup dialog (opened when a toolpath is selected) -->
    <div id="tp-editor-backdrop" class="tp-editor-backdrop d-none" onclick="App.closeTpEditorWithoutSave()"></div>
    <div id="tp-editor" class="tp-editor d-none" role="dialog" aria-modal="true">
      <div id="ed-preview-tool-warning" class="px-2 py-1 text-danger label-xs d-none">
        {{GCM_PREVIEW_TOOL_WARN}}
      </div>
      <div class="editor-header">
        <div class="editor-header-title d-flex align-items-center gap-2 min-w-0 flex-grow-1">
          <span id="ed-type-icon" class="ed-type-icon tp-type-icon" aria-hidden="true"></span>
          <span id="ed-type-label" class="ed-type-label label-xs text-muted flex-shrink-0"></span>
          <span id="ed-title" class="editor-title fw-semibold label-sm text-truncate"></span>
        </div>
        <div class="d-flex gap-1 align-items-center flex-shrink-0">
          <button type="button" class="btn btn-xs btn-primary" id="btn-tp-save-editor" disabled
                  title="{{GCM_TP_SAVE_EDITOR_TITLE}}" aria-label="{{GCM_ARIA_SAVE_TP_EDITOR}}"
                  onclick="App.saveToolpathEditorConfiguration()">{{GCM_SAVE_BTN}}</button>
          <button type="button" class="btn btn-xs btn-secondary" id="btn-tp-close-editor"
                  title="{{GCM_TP_CLOSE_EDITOR_TITLE}}" aria-label="{{GCM_ARIA_CLOSE_TP_EDITOR}}"
                  onclick="App.closeTpEditorWithoutSave()">X</button>
        </div>
      </div>

      <div id="editor-body" class="editor-body">

        <!-- ── Line 1: Name | Start D | Cut C | Tool ── -->
        <div class="ed-inline-group mb-1">
          <div class="ed-field ed-field-grow">
            <label class="label-xs">{{GCM_LABEL_NAME}}</label>
            <input id="ed-name" type="text" class="form-control form-control-xs"
                   placeholder="{{GCM_NAME_PH}}"
                   onblur="App.onTpNameBlur(this)">
            <div id="ed-name-warning" class="text-danger label-xs d-none mt-1"></div>
          </div>
          <div class="ed-field">
            <label class="label-xs">{{GCM_LABEL_START_DEPTH}}</label>
            <div class="d-flex align-items-center gap-1">
              <input id="ed-startd" type="number" class="form-control form-control-xs ed-input-num"
                     step="any" inputmode="decimal" min="0" placeholder="{{GCM_PH_MM_ZERO}}"
                     onblur="App.onTpSettingNumBlur(this, 'start_depth')">
              <span class="label-xs text-muted">mm</span>
            </div>
          </div>
          <div class="ed-field">
            <label class="label-xs">{{GCM_LABEL_CUT_DEPTH}}</label>
            <div class="d-flex align-items-center gap-1">
              <input id="ed-depth" type="number" class="form-control form-control-xs ed-input-num"
                     step="any" inputmode="decimal" min="0" placeholder="{{GCM_PH_MM_ZERO}}"
                     onblur="App.onCutDepthBlur(this)">
              <span class="label-xs text-muted">mm</span>
            </div>
          </div>
          <div class="ed-field" id="ed-passes-readout-group">
            <label class="label-xs">{{GCM_LABEL_PASSES}}</label>
            <button type="button" id="ed-passes-edit-btn"
                    class="btn btn-xs btn-outline-secondary w-100"
                    onclick="App.openPassesEditor()">{{GCM_EDIT_PASSES_BTN}}</button>
          </div>
          <div class="ed-field ed-field-tool">
            <label class="label-xs">{{GCM_LABEL_TOOL}}</label>
            <div class="d-flex align-items-center gap-1">
              <div id="ed-tool-display" class="ed-tool-display flex-fill">{{GCM_NO_TOOL_SELECTED}}</div>
              <button type="button" class="btn btn-xs btn-outline-secondary flex-shrink-0"
                      onclick="App.openToolForTp()">{{GCM_SELECT_TOOL_BTN}}</button>
              <button type="button" id="ed-tool-clear"
                      class="btn btn-xs btn-outline-danger flex-shrink-0 d-none"
                      onclick="App.clearTpTool()" title="{{GCM_REMOVE_TOOL_TITLE}}">&#x2715;</button>
            </div>
          </div>
        </div>

        <!-- ══ PROFILE only ══════════════════════════════════════════════════ -->
        <div id="sec-profile">
          <div class="ed-profile-layout mb-1">
            <div class="ed-profile-col-main">
              <div class="ed-inline-group">
                <div class="ed-field">
                  <label class="label-xs">{{GCM_LABEL_MACHINE_VECTORS}}</label>
                  <div class="btn-group btn-group-xs" role="group">
                    <input type="radio" class="btn-check" name="ed-comp" id="comp-outside" value="outside"
                           onchange="App.setSetting('compensation','outside')">
                    <label class="btn btn-outline-secondary" for="comp-outside">{{GCM_COMP_OUTSIDE}}</label>
                    <input type="radio" class="btn-check" name="ed-comp" id="comp-inside" value="inside"
                           onchange="App.setSetting('compensation','inside')">
                    <label class="btn btn-outline-secondary" for="comp-inside">{{GCM_COMP_INSIDE}}</label>
                    <input type="radio" class="btn-check" name="ed-comp" id="comp-on" value="on"
                           onchange="App.setSetting('compensation','on')">
                    <label class="btn btn-outline-secondary" for="comp-on">{{GCM_COMP_ON}}</label>
                  </div>
                </div>
                <div class="ed-field">
                  <label class="label-xs">{{GCM_LABEL_DIRECTION}}</label>
                  <div class="btn-group btn-group-xs" role="group">
                    <input type="radio" class="btn-check" name="ed-dir" id="dir-climb" value="climb"
                           onchange="App.setSetting('direction','climb')">
                    <label class="btn btn-outline-secondary" for="dir-climb">{{GCM_DIR_CLIMB}}</label>
                    <input type="radio" class="btn-check" name="ed-dir" id="dir-conv" value="conventional"
                           onchange="App.setSetting('direction','conventional')">
                    <label class="btn btn-outline-secondary" for="dir-conv">{{GCM_DIR_CONVENTIONAL}}</label>
                  </div>
                </div>
              </div>
              <!-- Aspire-style: only applies when the profile is cut with a V-bit -->
              <div class="ed-field mt-1 d-none" id="ed-sharpen-wrap">
                <label class="label-xs d-flex align-items-center gap-1 mb-0">
                  <input type="checkbox" class="form-check-input m-0" id="ed-sharpen-corners"
                         onchange="App.setSetting('sharpen_corners', this.checked)">
                  {{GCM_LABEL_SHARPEN_CORNERS}}
                </label>
              </div>
            </div>
            <div class="ed-profile-col-start">
              <label class="label-xs">{{GCM_LABEL_STARTING_POINT}}</label>
              <div class="d-flex align-items-center gap-2">
                <div class="start-pt-grid" id="ed-start-point">
                  <button type="button" class="start-pt-cell" data-pos="nw"  onclick="App.setStartPoint('nw')"></button>
                  <button type="button" class="start-pt-cell" data-pos="n"   onclick="App.setStartPoint('n')"></button>
                  <button type="button" class="start-pt-cell" data-pos="ne"  onclick="App.setStartPoint('ne')"></button>
                  <button type="button" class="start-pt-cell" data-pos="w"   onclick="App.setStartPoint('w')"></button>
                  <span class="start-pt-grid-spacer" aria-hidden="true"></span>
                  <button type="button" class="start-pt-cell" data-pos="e"   onclick="App.setStartPoint('e')"></button>
                  <button type="button" class="start-pt-cell" data-pos="sw"  onclick="App.setStartPoint('sw')"></button>
                  <button type="button" class="start-pt-cell" data-pos="s"   onclick="App.setStartPoint('s')"></button>
                  <button type="button" class="start-pt-cell" data-pos="se"  onclick="App.setStartPoint('se')"></button>
                </div>
                <span id="ed-start-pt-label" class="label-xs text-muted"></span>
              </div>
            </div>
          </div>
        </div>

        <!-- ══ V-BIT PROFILE only: sharp-corner (V-carve) treatment ═════════ -->
        <div id="sec-corners">
          <div class="ed-field mb-1">
            <label class="label-xs">{{GCM_LABEL_VCARVE}}</label>
            <div class="form-check">
              <input class="form-check-input" type="checkbox" id="ed-sharp-ext"
                     onchange="App.onSharpExternalToggle(this.checked)">
              <label class="form-check-label label-xs" for="ed-sharp-ext">{{GCM_SHARP_EXTERNAL_CORNERS}}</label>
            </div>
            <div class="form-check">
              <input class="form-check-input" type="checkbox" id="ed-sharp-int"
                     onchange="App.setSetting('sharp_internal_corners', this.checked)">
              <label class="form-check-label label-xs" for="ed-sharp-int">{{GCM_SHARP_INTERNAL_CORNERS}}</label>
            </div>
            <div class="d-flex align-items-center gap-1 mt-1">
              <label class="label-xs">{{GCM_ALLOWANCE_CUT_OUT_TOOL}}</label>
              <input id="ed-corner-allowance" type="number" class="form-control form-control-xs ed-input-num"
                     step="any" min="0" placeholder="0"
                     onblur="App.onCutOutAllowanceBlur(this)"
                     title="{{GCM_CORNER_ALLOWANCE_TITLE}}">
              <span class="label-xs text-muted">mm</span>
            </div>
          </div>
        </div>

        <!-- ══ POCKET only ═══════════════════════════════════════════════════ -->
        <div id="sec-pocket">
          <div class="ed-inline-group mb-1">
            <div class="ed-field">
              <label class="label-xs">{{GCM_LABEL_CLEAR_POCKET}}</label>
              <div class="btn-group btn-group-xs" role="group">
                <input type="radio" class="btn-check" name="ed-fill" id="fill-offset" value="offset"
                       onchange="App.setPocketFill('offset')">
                <label class="btn btn-outline-secondary" for="fill-offset">{{GCM_POCKET_OFFSET}}</label>
                <input type="radio" class="btn-check" name="ed-fill" id="fill-raster" value="raster"
                       onchange="App.setPocketFill('raster')">
                <label class="btn btn-outline-secondary" for="fill-raster">{{GCM_POCKET_RASTER}}</label>
              </div>
            </div>
            <div class="ed-field">
              <label class="label-xs">{{GCM_LABEL_DIRECTION}}</label>
              <div class="btn-group btn-group-xs" role="group">
                <input type="radio" class="btn-check" name="ed-pdir" id="pdir-climb" value="climb"
                       onchange="App.setSetting('direction','climb')">
                <label class="btn btn-outline-secondary" for="pdir-climb">{{GCM_DIR_CLIMB}}</label>
                <input type="radio" class="btn-check" name="ed-pdir" id="pdir-conv" value="conventional"
                       onchange="App.setSetting('direction','conventional')">
                <label class="btn btn-outline-secondary" for="pdir-conv">{{GCM_DIR_CONVENTIONAL}}</label>
              </div>
            </div>
          </div>
          <!-- Raster-only: scan angle + perimeter (profile) pass, Aspire-style -->
          <div class="ed-inline-group mb-1" id="sec-pocket-raster">
            <div class="ed-field">
              <label class="label-xs">{{GCM_LABEL_RASTER_ANGLE}}</label>
              <div class="d-flex align-items-center gap-1">
                <input id="ed-raster-angle" type="number" class="form-control form-control-xs ed-input-num"
                       step="any" inputmode="decimal" placeholder="0"
                       onblur="App.onTpRasterAngleBlur(this)">
                <span class="label-xs text-muted">{{GCM_LABEL_DEGREES}}</span>
              </div>
            </div>
            <div class="ed-field">
              <label class="label-xs">{{GCM_LABEL_PROFILE_PASS}}</label>
              <select id="ed-profile-pass" class="form-select form-select-sm form-control-xs"
                      onchange="App.setSetting('profile_pass', this.value)">
                <option value="none">{{GCM_PROFILE_PASS_NONE}}</option>
                <option value="first">{{GCM_PROFILE_PASS_FIRST}}</option>
                <option value="last">{{GCM_PROFILE_PASS_LAST}}</option>
              </select>
            </div>
          </div>
        </div>

        <!-- ══ FLUTING only ══════════════════════════════════════════════════ -->
        <div id="sec-fluting">
          <div class="ed-field mb-1">
            <label class="label-xs">{{GCM_LABEL_FLUTE_TYPE}}</label>
            <div class="d-flex flex-column gap-1">
              <label class="label-xs d-flex align-items-center gap-1 mb-0">
                <input type="radio" class="form-check-input m-0" name="ed-flute-type" value="full"
                       onchange="App.setFluteType('full')">
                {{GCM_FLUTE_FULL}}
              </label>
              <label class="label-xs d-flex align-items-center gap-1 mb-0">
                <input type="radio" class="form-check-input m-0" name="ed-flute-type" value="start"
                       onchange="App.setFluteType('start')">
                {{GCM_FLUTE_START}}
              </label>
              <label class="label-xs d-flex align-items-center gap-1 mb-0">
                <input type="radio" class="form-check-input m-0" name="ed-flute-type" value="start_end"
                       onchange="App.setFluteType('start_end')">
                {{GCM_FLUTE_START_END}}
              </label>
            </div>
          </div>
          <div class="ed-inline-group mb-1">
            <div class="ed-field">
              <label class="label-xs d-flex align-items-center gap-1 mb-0">
                <input type="radio" class="form-check-input m-0" name="ed-ramp-spec" id="ed-ramp-spec-length"
                       value="length" onchange="App.setRampSpec('length')">
                {{GCM_LABEL_RAMP_LENGTH}}
              </label>
              <div class="d-flex align-items-center gap-1">
                <input id="ed-ramp-length" type="number" class="form-control form-control-xs ed-input-num"
                       step="any" inputmode="decimal" min="0" placeholder="{{GCM_PH_MM_ZERO}}"
                       onblur="App.onTpSettingNumBlur(this, 'ramp_length_mm')">
                <span class="label-xs text-muted">mm</span>
              </div>
            </div>
            <div class="ed-field">
              <label class="label-xs d-flex align-items-center gap-1 mb-0">
                <input type="radio" class="form-check-input m-0" name="ed-ramp-spec" id="ed-ramp-spec-percent"
                       value="percent" onchange="App.setRampSpec('percent')">
                {{GCM_LABEL_RAMP_PERCENT}}
              </label>
              <div class="d-flex align-items-center gap-1">
                <input id="ed-ramp-percent" type="number" class="form-control form-control-xs ed-input-num"
                       step="any" inputmode="decimal" min="0" max="100" placeholder="100"
                       onblur="App.onTpSettingNumBlur(this, 'ramp_percent')">
                <span class="label-xs text-muted">%</span>
              </div>
            </div>
          </div>
          <div class="ed-field mb-1">
            <label class="label-xs">{{GCM_LABEL_RAMP_TYPE}}</label>
            <div class="btn-group btn-group-xs" role="group">
              <input type="radio" class="btn-check" name="ed-ramp-shape" id="ramp-linear" value="linear"
                     onchange="App.setSetting('ramp_shape','linear')">
              <label class="btn btn-outline-secondary" for="ramp-linear">{{GCM_RAMP_LINEAR}}</label>
              <input type="radio" class="btn-check" name="ed-ramp-shape" id="ramp-smooth" value="smooth"
                     onchange="App.setSetting('ramp_shape','smooth')">
              <label class="btn btn-outline-secondary" for="ramp-smooth">{{GCM_RAMP_SMOOTH}}</label>
            </div>
          </div>
        </div>

        <div id="sec-tags" class="ed-field mb-0">
          <label class="label-xs">{{GCM_LABEL_TAGS}}</label>
          <div class="d-flex align-items-start gap-1">
            <div id="tag-chips" class="tag-chips profile-tag-chips flex-fill"></div>
            <button type="button" class="btn btn-xs btn-outline-primary flex-shrink-0"
                    onclick="App.openTagPicker()">{{GCM_SELECT_TAGS_BTN}}</button>
          </div>
        </div>

      </div>
    </div>

    <!-- Tool editor — popup dialog (opened when a tool row is expanded) -->
    <div id="tool-editor-backdrop" class="tp-editor-backdrop d-none" onclick="App.closeToolEditor()"></div>
    <div id="tool-editor-modal" class="tp-editor d-none" role="dialog" aria-modal="true">
      <div class="editor-header">
        <span id="tool-editor-title" class="editor-title fw-semibold label-sm text-truncate"></span>
        <div class="d-flex gap-1 align-items-center flex-shrink-0">
          <button type="button" class="btn btn-xs btn-primary" id="tool-editor-save-btn" disabled
                  onclick="App.saveCurrentToolDraft()">{{GCM_SAVE_BTN}}</button>
          <button type="button" class="btn btn-xs btn-secondary" onclick="App.closeToolEditor()"
                  aria-label="{{GCM_ARIA_CLOSE}}">X</button>
        </div>
      </div>
      <div id="tool-editor-mount" class="editor-body"></div>
    </div>

  </div><!-- panel-right -->
</div><!-- app -->

<!-- ── Tag selection dialog ─────────────────────────────────────────────── -->
<div id="tag-dialog-overlay" class="tag-dialog-overlay d-none" onclick="App._tagDialogOverlayClick(event)">
  <div class="tag-dialog" onclick="event.stopPropagation()">
    <div class="tag-dialog-header">
      <span class="fw-semibold" style="font-size:13px">{{GCM_TAG_DIALOG_TITLE}}</span>
      <button class="tag-dialog-close" onclick="App.closeTagDialog()">✕</button>
    </div>
    <div class="tag-dialog-body" id="tag-dialog-body">
      <!-- checkboxes injected by JS -->
    </div>
    <div class="tag-dialog-footer">
      <button class="btn btn-xs btn-outline-secondary" onclick="App.closeTagDialog()">{{GCM_COMMON_CANCEL}}</button>
      <button class="btn btn-xs btn-primary" onclick="App.applyTagDialog()">{{GCM_COMMON_APPLY}}</button>
    </div>
  </div>
</div>

<!-- ── Pass-depths (Aspire "Specify Pass Depths") modal ────────────────────────── -->
<div class="modal fade" id="passes-editor-modal" tabindex="-1" aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable">
    <div class="modal-content">
      <div class="modal-header py-2">
        <h5 class="modal-title fs-6">{{GCM_PASSES_TITLE}}</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{GCM_ARIA_CLOSE}}"></button>
      </div>
      <div class="modal-body py-2 small">
        <div id="passes-editor-rows"></div>
        <button type="button" class="btn btn-xs btn-outline-secondary mt-2" id="passes-add-btn"
                onclick="App.addPassRow()">+ {{GCM_PASSES_ADD}}</button>
      </div>
      <div class="modal-footer py-2">
        <button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">{{GCM_COMMON_CANCEL}}</button>
        <button type="button" class="btn btn-sm btn-primary" onclick="App.applyPassesEditor()">{{GCM_PASSES_APPLY}}</button>
      </div>
    </div>
  </div>
</div>

<!-- ── Tag picker modal — at body level so it isn't clipped by panel overflow ── -->
<div class="modal fade" id="tag-picker-modal" tabindex="-1" aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable">
    <div class="modal-content">
      <div class="modal-header py-2">
        <h5 class="modal-title fs-6">{{GCM_TAG_PICKER_TITLE}}</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{GCM_ARIA_CLOSE}}"></button>
      </div>
      <div class="modal-body py-2">
        <p id="tag-picker-empty" class="text-muted small mb-2 d-none">
          {{GCM_TAG_PICKER_EMPTY}}
        </p>
        <div id="tag-picker-list" class="d-flex flex-column gap-1 tag-picker-list"></div>
      </div>
      <div class="modal-footer py-2">
        <button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">{{GCM_COMMON_CANCEL}}</button>
        <button type="button" class="btn btn-sm btn-primary" onclick="App.applyTagPicker()">{{GCM_COMMON_APPLY}}</button>
      </div>
    </div>
  </div>
</div>

<div id="sheet-layer-list-home" class="d-none" aria-hidden="true">
  <div id="sheet-layer-list" class="layer-list"></div>
</div>

<div class="modal fade" id="material-setup-modal" tabindex="-1" aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg">
    <div class="modal-content">
      <div class="modal-header py-2">
        <h5 class="modal-title fs-6">{{GCM_MATERIAL_SETUP_TITLE}}</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{GCM_ARIA_CLOSE}}"></button>
      </div>
      <div class="modal-body py-2 small">
        <div class="row g-3">
          <div class="col-12">
            <div class="row g-3">
              <div class="col-md-6">
                <label class="label-xs d-block mb-1">{{GCM_MS_XY_DATUM}}</label>
                <div class="ms-datum-grid">
                  <button type="button" class="ms-datum-cell" data-datum="nw" onclick="App.pickDatum(this)"></button>
                  <button type="button" class="ms-datum-cell" data-datum="n" onclick="App.pickDatum(this)"></button>
                  <button type="button" class="ms-datum-cell" data-datum="ne" onclick="App.pickDatum(this)"></button>
                  <button type="button" class="ms-datum-cell" data-datum="w" onclick="App.pickDatum(this)"></button>
                  <button type="button" class="ms-datum-cell" data-datum="center" onclick="App.pickDatum(this)"></button>
                  <button type="button" class="ms-datum-cell" data-datum="e" onclick="App.pickDatum(this)"></button>
                  <button type="button" class="ms-datum-cell" data-datum="sw" onclick="App.pickDatum(this)"></button>
                  <button type="button" class="ms-datum-cell" data-datum="s" onclick="App.pickDatum(this)"></button>
                  <button type="button" class="ms-datum-cell" data-datum="se" onclick="App.pickDatum(this)"></button>
                </div>
                <div class="form-check mt-2">
                  <input id="ms-summary-toolpath" type="checkbox" class="form-check-input" checked>
                  <label class="form-check-label" for="ms-summary-toolpath">{{GCM_MS_SUMMARY_CHK}}</label>
                </div>
              </div>
              <div class="col-md-6">
                <label class="label-xs">{{GCM_MS_THICKNESS}}</label>
                <div class="input-group input-group-sm mb-2">
                  <input id="ms-thickness" type="number" class="form-control" step="0.1" min="0">
                  <span class="input-group-text">mm</span>
                </div>
                <div class="form-check mb-1">
                  <input id="ms-use-xy-offset" type="checkbox" class="form-check-input"
                         onchange="App.toggleMaterialOffset()">
                  <label class="form-check-label" for="ms-use-xy-offset">{{GCM_MS_USE_OFFSET}}</label>
                </div>
                <div class="row g-2 mb-2">
                  <div class="col-6">
                    <label class="label-xs">{{GCM_MS_X_MM}}</label>
                    <input id="ms-off-x" type="number" class="form-control form-control-sm" step="0.1" value="0">
                  </div>
                  <div class="col-6">
                    <label class="label-xs">{{GCM_MS_Y_MM}}</label>
                    <input id="ms-off-y" type="number" class="form-control form-control-sm" step="0.1" value="0">
                  </div>
                </div>
                <label class="label-xs d-block mb-1">{{GCM_MS_Z_ZERO}}</label>
                <div class="ms-z-zero-options">
                  <label class="form-check mb-0">
                    <input class="form-check-input" type="radio" name="ms-z-zero" id="ms-z-mat" value="material_surface">
                    <span class="form-check-label">{{GCM_MS_Z_MAT}}</span>
                  </label>
                  <label class="form-check mb-0">
                    <input class="form-check-input" type="radio" name="ms-z-zero" id="ms-z-bed" value="machine_bed" checked>
                    <span class="form-check-label">{{GCM_MS_Z_BED}}</span>
                  </label>
                </div>
              </div>
            </div>
          </div>
          <div class="col-12">
            <label class="label-xs d-block mb-2">{{GCM_MS_MODEL_POS}}</label>
            <div class="row g-2 mb-2">
              <div class="col-6">
                <div class="form-check mb-1">
                  <input class="form-check-input" type="radio" name="ms-gap-mode" id="ms-gap-above-radio" value="above" checked>
                  <label class="form-check-label" for="ms-gap-above-radio">{{GCM_MS_GAP_ABOVE}}</label>
                </div>
                <input id="ms-gap-above" type="number" class="form-control form-control-sm" step="0.1" min="0">
              </div>
              <div class="col-6">
                <div class="form-check mb-1">
                  <input class="form-check-input" type="radio" name="ms-gap-mode" id="ms-gap-below-radio" value="below">
                  <label class="form-check-label" for="ms-gap-below-radio">{{GCM_MS_GAP_BELOW}}</label>
                </div>
                <input id="ms-gap-below" type="number" class="form-control form-control-sm" step="0.1" min="0">
              </div>
            </div>
            <div class="row g-2">
              <div class="col-6">
                <label class="label-xs">{{GCM_MS_MODEL_THICK}}</label>
                <input id="ms-model-thick" type="number" class="form-control form-control-sm" step="0.1" min="0" value="0">
              </div>
              <div class="col-6">
                <label class="label-xs">{{GCM_MS_MODEL_PLANE_Z}}</label>
                <input id="ms-model-plane-z" type="number" class="form-control form-control-sm" step="0.1" value="0">
              </div>
            </div>
          </div>
          <div class="col-12">
            <label class="label-xs d-block mb-1">{{GCM_MS_RAPID_Z}}</label>
            <div class="row g-2">
              <div class="col-6">
                <label class="label-xs">{{GCM_MS_CLEARANCE_Z}}</label>
                <input id="ms-clearance-z" type="number" class="form-control form-control-sm" step="0.1" min="0">
              </div>
              <div class="col-6">
                <label class="label-xs">{{GCM_MS_PLUNGE_Z}}</label>
                <input id="ms-plunge-z" type="number" class="form-control form-control-sm" step="0.1" min="0">
              </div>
            </div>
          </div>
          <div class="col-12">
            <label class="label-xs d-block mb-1">{{GCM_MS_HOME_START}}</label>
            <div class="row g-2">
              <div class="col-md-3">
                <label class="label-xs">{{GCM_MS_X_MM}}</label>
                <input id="ms-home-x" type="number" class="form-control form-control-sm" step="0.1" value="0">
              </div>
              <div class="col-md-3">
                <label class="label-xs">{{GCM_MS_Y_MM}}</label>
                <input id="ms-home-y" type="number" class="form-control form-control-sm" step="0.1" value="0">
              </div>
              <div class="col-md-4">
                <label class="label-xs">{{GCM_MS_HOME_Z_GAP}}</label>
                <input id="ms-home-z-gap" type="number" class="form-control form-control-sm" step="0.1" min="0">
              </div>
            </div>
          </div>
          <div class="col-12 border-top pt-3 mt-1">
            <span class="label-xs fw-semibold d-block mb-2">{{GCM_MS_GCODE_OUT}}</span>
            <div class="form-check">
              <input id="ms-emit-fanuc-o" type="checkbox" class="form-check-input" checked>
              <label class="form-check-label" for="ms-emit-fanuc-o">{{GCM_MS_FANUC_O}}</label>
            </div>
            <div class="form-check">
              <input id="ms-tool-length-comp" type="checkbox" class="form-check-input" checked>
              <label class="form-check-label" for="ms-tool-length-comp">{{GCM_MS_TOOL_LEN}}</label>
            </div>
          </div>
        </div>
      </div>
      <div class="modal-footer py-2">
        <button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">{{GCM_COMMON_CANCEL}}</button>
        <button type="button" class="btn btn-sm btn-primary" onclick="App.applyMaterialSetup()">{{GCM_INVALID_MODAL_OK}}</button>
      </div>
    </div>
  </div>
</div>

<div class="modal fade" id="post-import-modal" tabindex="-1" aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-xl">
    <div class="modal-content">
      <div class="modal-header py-2">
        <h5 class="modal-title fs-6">{{GCM_POST_MODAL_TITLE}}</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{GCM_ARIA_CLOSE}}"></button>
      </div>
      <div class="modal-body py-2 small">
        <div class="d-flex flex-wrap align-items-center gap-2 mb-2">
          <button type="button" class="btn btn-sm btn-outline-primary" onclick="App.importPostFile()">{{GCM_POST_IMPORT_BTN}}</button>
          <span id="post-source-path" class="label-xs text-muted text-truncate"></span>
        </div>
        <div id="post-empty-hint" class="text-muted label-xs mb-2 d-none">{{GCM_POST_EMPTY_HINT}}</div>
        <div id="post-summary-wrap" class="mb-2 d-none">
          <div class="row g-2 mb-2">
            <div class="col-md-4">
              <span class="label-xs text-muted">{{GCM_POST_NAME_LABEL}}</span>
              <div id="post-info-name" class="fw-semibold text-break"></div>
            </div>
            <div class="col-md-2">
              <span class="label-xs text-muted">{{GCM_POST_UNITS_LABEL}}</span>
              <div id="post-info-units"></div>
            </div>
            <div class="col-md-2">
              <span class="label-xs text-muted">{{GCM_POST_EXT_LABEL}}</span>
              <div id="post-info-ext"></div>
            </div>
            <div class="col-md-4">
              <span class="label-xs text-muted">{{GCM_POST_LINE_NUM_LABEL}}</span>
              <div id="post-info-line-num"></div>
            </div>
          </div>
        </div>
        <div>
          <div class="label-xs fw-semibold mb-1">{{GCM_POST_PREVIEW_LABEL}}</div>
          <pre id="post-raw-preview" class="gcode-result-preview post-raw-pre"></pre>
        </div>
      </div>
      <div class="modal-footer py-2">
        <button type="button" class="btn btn-sm btn-primary" data-bs-dismiss="modal">{{GCM_ARIA_CLOSE}}</button>
      </div>
    </div>
  </div>
</div>

<div class="modal fade" id="gcode-generate-warning-modal" tabindex="-1" aria-labelledby="gcode-generate-warning-label" aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable modal-lg">
    <div class="modal-content">
      <div class="modal-header py-2">
        <h5 class="modal-title fs-6 text-warning" id="gcode-generate-warning-label">{{GCM_WARN_TITLE}}</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{GCM_ARIA_CLOSE}}"></button>
      </div>
      <div class="modal-body py-2 small" id="gcode-generate-warning-body"></div>
      <div class="modal-footer py-2">
        <button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">{{GCM_COMMON_CANCEL}}</button>
        <button type="button" class="btn btn-sm btn-warning" onclick="App.confirmOrphanLayersWarning()">{{GCM_COMMON_CONTINUE}}</button>
      </div>
    </div>
  </div>
</div>

<div class="modal fade" id="gcode-result-modal" tabindex="-1" aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable" style="max-width:420px">
    <div class="modal-content">
      <div class="modal-header py-2">
        <h5 class="modal-title fs-6">{{GCM_RESULT_TITLE}}</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{GCM_ARIA_CLOSE}}"></button>
      </div>
      <div class="modal-body py-2">
        <div id="gcode-result-preview">
        <div id="gcode-result-summary" class="small fw-semibold mb-2"></div>
        <div class="label-xs text-muted mb-1">{{GCM_RESULT_FILES}}</div>
        <ul id="gcode-result-file-list" class="small mb-0 ps-3"></ul>
        </div>
      </div>
      <div class="modal-footer py-2">
        <button type="button" class="btn btn-sm btn-outline-secondary" id="btn-open-output-folder" onclick="App.openOutputFolder()">{{GCM_RESULT_OPEN_FOLDER}}</button>
        <button type="button" class="btn btn-sm btn-primary" data-bs-dismiss="modal">{{GCM_ARIA_CLOSE}}</button>
      </div>
    </div>
  </div>
</div>

<!-- ── Tool picker for toolpath ──────────────────────────────────────────── -->
<div class="modal fade" id="tp-tool-picker-modal" tabindex="-1" aria-hidden="true">
  <div class="modal-dialog modal-dialog-scrollable" style="max-width:360px">
    <div class="modal-content">
      <div class="modal-header py-2">
        <h5 class="modal-title fs-6">{{GCM_TP_TOOL_PICKER_TITLE}}</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{GCM_ARIA_CLOSE}}"></button>
      </div>
      <div class="modal-body py-1 px-0">
        <div id="tp-tool-picker-list"></div>
      </div>
      <div class="modal-footer py-2">
        <button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">{{GCM_COMMON_CANCEL}}</button>
      </div>
    </div>
  </div>
</div>

<div class="modal fade" id="gcode-unsaved-settings-modal" tabindex="-1" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" style="max-width:420px">
    <div class="modal-content">
      <div class="modal-header py-2">
        <h5 class="modal-title fs-6">{{GCM_UNSAVED_TITLE}}</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{GCM_ARIA_CLOSE}}"></button>
      </div>
      <div class="modal-body py-2 small">
        {{GCM_UNSAVED_BODY}}
      </div>
      <div class="modal-footer py-2">
        <button type="button" class="btn btn-sm btn-secondary" onclick="App.unsavedModalCancel()">{{GCM_COMMON_CANCEL}}</button>
        <button type="button" class="btn btn-sm btn-outline-danger" onclick="App.unsavedModalDiscard()">{{GCM_UNSAVED_DONT_SAVE}}</button>
        <button type="button" class="btn btn-sm btn-primary" onclick="App.unsavedModalSave()">{{GCM_SAVE_BTN}}</button>
      </div>
    </div>
  </div>
</div>

<div class="modal fade" id="gcm-error-modal" tabindex="-1" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" style="max-width:420px">
    <div class="modal-content">
      <div class="modal-header py-2">
        <h5 class="modal-title fs-6 text-danger">{{GCM_ERROR_TITLE}}</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{GCM_ARIA_CLOSE}}"></button>
      </div>
      <div class="modal-body py-2 small" id="gcm-error-modal-body"></div>
      <div class="modal-footer py-2">
        <button type="button" class="btn btn-sm btn-primary" data-bs-dismiss="modal">{{GCM_INVALID_MODAL_OK}}</button>
      </div>
    </div>
  </div>
</div>

<div class="modal fade" id="field-validation-modal" tabindex="-1" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" style="max-width:380px">
    <div class="modal-content">
      <div class="modal-header py-2">
        <h5 class="modal-title fs-6">{{GCM_INVALID_VALUE_TITLE}}</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{GCM_ARIA_CLOSE}}"></button>
      </div>
      <div class="modal-body py-2 small" id="field-validation-modal-body"></div>
      <div class="modal-footer py-2">
        <button type="button" class="btn btn-sm btn-primary" data-bs-dismiss="modal">{{GCM_INVALID_MODAL_OK}}</button>
      </div>
    </div>
  </div>
</div>

<div class="modal fade" id="tp-clear-all-modal" tabindex="-1" aria-hidden="true">
  <div class="modal-dialog modal-dialog-centered" style="max-width:360px">
    <div class="modal-content">
      <div class="modal-header py-2">
        <h5 class="modal-title fs-6">{{GCM_CLEAR_ALL_TP_TITLE}}</h5>
        <button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="{{GCM_ARIA_CLOSE}}"></button>
      </div>
      <div class="modal-body py-2 small">
        {{GCM_CLEAR_ALL_TP_BODY}}
      </div>
      <div class="modal-footer py-2">
        <button type="button" class="btn btn-sm btn-secondary" data-bs-dismiss="modal">{{GCM_COMMON_CANCEL}}</button>
        <button type="button" class="btn btn-sm btn-danger" onclick="App.clearAllToolpathsConfirmed()">{{GCM_CLEAR_ALL_CONFIRM}}</button>
      </div>
    </div>
  </div>
</div>

{{BOOTSTRAP_JS}}
<script>
function ggT(k) { return (window.GG_I18N && window.GG_I18N[k] != null) ? String(window.GG_I18N[k]) : k; }
function ggTf(k) {
  var s = ggT(k);
  for (var i = 1; i < arguments.length; i++) { s = s.replace("%s", arguments[i]).replace("%d", arguments[i]); }
  return s;
}
</script>
<script>{{DIALOG_JS}}</script>
</body>
</html>
